diff options
author | HampusM <hampus@hampusmat.com> | 2025-03-21 20:05:53 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-03-22 15:16:01 +0100 |
commit | fe62665b1d62d36ee0839e6bf24e3841ea667da9 (patch) | |
tree | 0533941d2cbbe5bf0a1995a33f05bca37da949fd | |
parent | 76e7e612e7b516bf52b508ae5bb367b1ddc3babc (diff) |
refactor(ecs): replace query options with fieldless terms
-rw-r--r-- | ecs/src/component/storage.rs | 96 | ||||
-rw-r--r-- | ecs/src/component/storage/graph.rs | 11 | ||||
-rw-r--r-- | ecs/src/lib.rs | 17 | ||||
-rw-r--r-- | ecs/src/query.rs | 279 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 44 | ||||
-rw-r--r-- | ecs/src/query/options.rs | 59 | ||||
-rw-r--r-- | ecs/src/query/term.rs | 38 | ||||
-rw-r--r-- | ecs/tests/query.rs | 139 |
8 files changed, 428 insertions, 255 deletions
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 21f9a37..4b2b6d8 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -26,6 +26,26 @@ pub mod archetype; mod graph; +#[derive(Debug)] +pub struct ArchetypeSearchTerms<'a> +{ + pub required_components: &'a [Uid], + pub excluded_components: &'a [Uid], +} + +impl ArchetypeSearchTerms<'_> +{ + fn excluded_contains(&self, uid: Uid) -> bool + { + self.excluded_components.binary_search(&uid).is_ok() + } + + fn required_contains(&self, uid: Uid) -> bool + { + self.required_components.binary_search(&uid).is_ok() + } +} + #[derive(Debug, Default)] pub struct Storage { @@ -36,9 +56,25 @@ pub struct Storage impl Storage { - pub fn search_archetypes(&self, component_ids: &[Uid]) -> ArchetypeRefIter<'_> + pub fn search_archetypes<'search_terms>( + &self, + search_terms: ArchetypeSearchTerms<'search_terms>, + ) -> ArchetypeRefIter<'_, 'search_terms> { - let archetype_id = ArchetypeId::new(&component_ids); + let archetype_id = ArchetypeId::new(&search_terms.required_components); + + if search_terms + .excluded_components + .iter() + .any(|excluded_comp_id| search_terms.required_contains(*excluded_comp_id)) + { + return ArchetypeRefIter { + storage: self, + pre_iter: Either::B(Vec::new().into_iter()), + dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &[]), + search_terms, + }; + } let Some(add_edge_recursive_iter) = self.graph.dfs_archetype_add_edges(archetype_id) @@ -47,15 +83,16 @@ impl Storage .borrow_mut() .push(ImaginaryArchetype { id: archetype_id, - component_ids: component_ids.to_vec(), + component_ids: search_terms.required_components.to_vec(), }); - let found_archetypes = self.find_all_archetype_with_comps(component_ids); + let found_archetypes = self.find_all_archetype_with_comps(&search_terms); return ArchetypeRefIter { storage: self, pre_iter: Either::B(found_archetypes.clone().into_iter()), dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &found_archetypes), + search_terms, }; }; @@ -63,6 +100,7 @@ impl Storage storage: self, pre_iter: Either::A([archetype_id].into_iter()), dfs_iter: add_edge_recursive_iter, + search_terms, } } @@ -321,7 +359,10 @@ impl Storage } } - fn find_all_archetype_with_comps(&self, component_ids: &[Uid]) -> Vec<ArchetypeId> + fn find_all_archetype_with_comps( + &self, + search_terms: &ArchetypeSearchTerms<'_>, + ) -> Vec<ArchetypeId> { let Some(mut search_iter) = self.graph.dfs_archetype_add_edges(ArchetypeId::new(&[])) @@ -333,20 +374,30 @@ impl Storage let mut found = Vec::<ArchetypeId>::new(); while let Some(node_id) = search_iter.streaming_next() { - let ArchetypeAddEdgeDfsIterResult::AddEdge(node_id) = node_id else { + let ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id: node_id, + add_edge_component_id, + } = node_id + else { continue; }; + if search_terms.excluded_contains(add_edge_component_id) { + search_iter.pop(); + continue; + } + let node = self .graph .get_node_by_id(node_id) .expect("Graph node found through DFS doesn't exist"); - if node.archetype().component_cnt() < component_ids.len() { + if node.archetype().component_cnt() < search_terms.required_components.len() { continue; } - if !component_ids + if !search_terms + .required_components .iter() .all(|comp_id| node.archetype().has_component_with_id(*comp_id)) { @@ -371,14 +422,16 @@ impl TypeName for Storage } #[derive(Debug)] -pub struct ArchetypeRefIter<'storage> +pub struct ArchetypeRefIter<'storage, 'search_terms> { storage: &'storage Storage, pre_iter: Either<ArrayIter<ArchetypeId, 1>, VecIntoIter<ArchetypeId>>, dfs_iter: ArchetypeAddEdgeDfsIter<'storage>, + search_terms: ArchetypeSearchTerms<'search_terms>, } -impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage> +impl<'component_storage, 'search_terms> Iterator + for ArchetypeRefIter<'component_storage, 'search_terms> { type Item = &'component_storage Archetype; @@ -400,14 +453,26 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage> | ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { .. } ) })? { - ArchetypeAddEdgeDfsIterResult::AddEdge(add_edge_archetype_id) => { - break add_edge_archetype_id + ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id, + add_edge_component_id, + } => { + if self.search_terms.excluded_contains(add_edge_component_id) { + self.dfs_iter.pop(); + continue; + } + + break add_edge_archetype_id; } ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { archetype, add_edge_archetype_id, add_edge_component_id, } => { + if self.search_terms.excluded_contains(add_edge_component_id) { + continue; + } + let mut add_edge_archetype_comps = archetype.component_ids_sorted().collect::<Vec<_>>(); @@ -453,7 +518,7 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage> } } -impl ArchetypeRefIter<'_> +impl ArchetypeRefIter<'_, '_> { fn find_edges_of_imaginary_archetype( &self, @@ -461,7 +526,10 @@ impl ArchetypeRefIter<'_> ) -> Vec<(Uid, ArchetypeEdges)> { self.storage - .find_all_archetype_with_comps(imaginary_archetype_comps) + .find_all_archetype_with_comps(&ArchetypeSearchTerms { + required_components: imaginary_archetype_comps, + excluded_components: &[], + }) .into_iter() .filter_map(|found_id| { let found_archetype = self.storage.get_archetype_by_id(found_id).unwrap(); diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs index feac16c..e1f48f7 100644 --- a/ecs/src/component/storage/graph.rs +++ b/ecs/src/component/storage/graph.rs @@ -381,14 +381,21 @@ impl<'graph> StreamingIterator for ArchetypeAddEdgeDfsIter<'graph> .into_iter(), )); - Some(ArchetypeAddEdgeDfsIterResult::AddEdge(add_edge)) + Some(ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id: add_edge, + add_edge_component_id: component_id, + }) } } #[derive(Debug)] pub enum ArchetypeAddEdgeDfsIterResult<'graph, 'iter> { - AddEdge(ArchetypeId), + AddEdge + { + add_edge_archetype_id: ArchetypeId, + add_edge_component_id: Uid, + }, NoEdgesLeftForArchetype, NoAddEdge, AddEdgeAlreadyVisited, diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index cb2059b..9b787a2 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -18,11 +18,13 @@ use crate::extension::{Collector as ExtensionCollector, Extension}; use crate::lock::{Lock, WriteGuard}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; -use crate::query::options::{Not, Options as QueryOptions, With}; +use crate::query::term::Without; use crate::query::{ ComponentIter, - TermSequence as QueryTermSequence, + TermWithFieldTuple as QueryTermWithFieldTuple, + TermWithoutFieldTuple as QueryTermWithoutFieldTuple, Terms as QueryTerms, + TermsBuilderInterface, }; use crate::relationship::{ChildOf, DependsOn, Relationship}; use crate::sole::Sole; @@ -173,10 +175,10 @@ impl World extension.collect(extension_collector); } - pub fn query<Terms, OptionsT>(&self) -> Query<Terms, OptionsT> + pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> where - Terms: QueryTermSequence, - OptionsT: QueryOptions, + FieldTerms: QueryTermWithFieldTuple, + FieldlessTerms: QueryTermWithoutFieldTuple, { Query::new(self) } @@ -283,7 +285,7 @@ impl World fn perform_phases(&self) { let phase_query = - self.query::<(&Phase,), Not<With<Relationship<ChildOf, Phase>>>>(); + self.query::<(&Phase,), (Without<Relationship<ChildOf, Phase>>,)>(); for (phase_euid, (_,)) in phase_query.iter_with_euids() { if phase_euid == *START_PHASE { @@ -492,8 +494,7 @@ impl World .build(), ); - for (system,) in - ComponentIter::<(&SystemComponent,), _>::new(self, query.iter::<()>()) + for (system,) in ComponentIter::<(&SystemComponent,), _>::new(self, query.iter()) { unsafe { system.system.run(self); diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 2fcd2cc..1889e00 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -10,42 +10,42 @@ use crate::query::flexible::{ Iter as FlexibleQueryIter, Query as FlexibleQuery, }; -use crate::query::options::Options; use crate::system::{Param as SystemParam, System}; use crate::uid::Uid; use crate::util::VecExt; -use crate::{EntityComponent, World}; +use crate::World; pub mod flexible; -pub mod options; +pub mod term; #[derive(Debug)] -pub struct Query<'world, Terms, OptionsT = ()> +pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where - Terms: TermSequence, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { world: &'world World, inner: FlexibleQuery<'world, 'static>, - _pd: PhantomData<(Terms, OptionsT)>, + _pd: PhantomData<(FieldTerms, FieldlessTerms)>, } -impl<'world, Terms, OptionsT> Query<'world, Terms, OptionsT> +impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms> where - Terms: TermSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { /// Iterates over the entities matching this query, the iterator item being the entity /// components. #[must_use] pub fn iter<'query>( &'query self, - ) -> ComponentIter<'query, 'world, Terms, FlexibleQueryIter<'query>> + ) -> ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - tracing::trace!("Searching for {}", std::any::type_name::<Terms>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentIter { world: self.world, - iter: self.inner.iter::<OptionsT>(), + iter: self.inner.iter(), comps_pd: PhantomData, } } @@ -55,13 +55,13 @@ where #[must_use] pub fn iter_with_euids<'query>( &'query self, - ) -> ComponentAndEuidIter<'query, 'world, Terms, FlexibleQueryIter<'query>> + ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - tracing::trace!("Searching for {}", std::any::type_name::<Terms>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentAndEuidIter { world: self.world, - iter: self.inner.iter::<OptionsT>(), + iter: self.inner.iter(), comps_pd: PhantomData, } } @@ -75,15 +75,15 @@ where pub fn iter_with<'query, OutIter>( &'query self, func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter, - ) -> ComponentIter<'query, 'world, Terms, OutIter> + ) -> ComponentIter<'query, 'world, FieldTerms, OutIter> where OutIter: Iterator<Item = EntityHandle<'query>>, { - tracing::trace!("Searching for {}", std::any::type_name::<Terms>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentIter { world: self.world, - iter: func(self.inner.iter::<OptionsT>()), + iter: func(self.inner.iter()), comps_pd: PhantomData, } } @@ -92,27 +92,32 @@ where #[must_use] pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid> { - Some(self.inner.iter::<OptionsT>().nth(entity_index)?.uid()) + Some(self.inner.iter().nth(entity_index)?.uid()) } pub(crate) fn new(world: &'world World) -> Self { + let mut terms_builder = Terms::builder(); + + FieldTerms::apply_terms_to_builder(&mut terms_builder); + FieldlessTerms::apply_terms_to_builder(&mut terms_builder); + Self { world, - inner: world.flexible_query(Terms::build_terms()), + inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } } } -impl<'query, 'world, Terms, OptionsT> IntoIterator - for &'query Query<'world, Terms, OptionsT> +impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator + for &'query Query<'world, FieldTerms, FieldlessTerms> where - Terms: TermSequence + 'world, - OptionsT: Options, + FieldTerms: TermWithFieldTuple + 'world, + FieldlessTerms: TermWithoutFieldTuple, { - type IntoIter = ComponentIter<'query, 'world, Terms, FlexibleQueryIter<'query>>; - type Item = Terms::Handles<'query>; + type IntoIter = ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; + type Item = FieldTerms::Fields<'query>; fn into_iter(self) -> Self::IntoIter { @@ -120,10 +125,11 @@ where } } -impl<'world, Terms, OptionsT> SystemParam<'world> for Query<'world, Terms, OptionsT> +impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world> + for Query<'world, FieldTerms, FieldlessTerms> where - Terms: TermSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { type Input = (); @@ -147,7 +153,7 @@ where pub struct Terms<'a> { required_components: Cow<'a, [Uid]>, - //optional_components: Cow<'a, [Uid]>, + excluded_components: Cow<'a, [Uid]>, } impl<'a> Terms<'a> @@ -162,18 +168,35 @@ impl<'a> Terms<'a> pub struct TermsBuilder<'a> { required_components: Cow<'a, [Uid]>, - //optional_components: Cow<'a, [Uid]>, + excluded_components: Cow<'a, [Uid]>, } -impl<'a> TermsBuilder<'a> +pub trait TermsBuilderInterface<'a> { - pub fn with<ComponentT: Component>(mut self) -> Self + fn with<ComponentT: Component>(self) -> Self; + + fn without<ComponentT: Component>(self) -> Self; + + fn with_required_ids(self, ids: &'a mut [Uid]) -> Self; +} + +macro_rules! impl_terms_builder { + ($($impl_content: tt)*) => { + impl<'a> TermsBuilderInterface<'a> for TermsBuilder<'a> { + $($impl_content)* + } + + impl<'a> TermsBuilderInterface<'a> for &mut TermsBuilder<'a> { + $($impl_content)* + } + }; +} + +impl_terms_builder! { + #[allow(unused_mut)] + fn with<ComponentT: Component>(mut self) -> Self { if ComponentT::is_optional() { - //self.optional_components - // .to_mut() - // .insert_at_partition_point_by_key(ComponentT::id(), |id| *id); - return self; } @@ -181,12 +204,28 @@ impl<'a> TermsBuilder<'a> .to_mut() .insert_at_partition_point_by_key(ComponentT::id(), |id| *id); - assert!(self.required_components.is_sorted()); + self + } + + #[allow(unused_mut)] + fn without<ComponentT: Component>(mut self) -> Self + { + if ComponentT::is_optional() { + panic!( + "{}::without cannot take optional component", + type_name::<Self>() + ); + } + + self.excluded_components + .to_mut() + .insert_at_partition_point_by_key(ComponentT::id(), |id| *id); self } - pub fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self + #[allow(unused_mut)] + fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self { if ids.is_empty() { return self; @@ -215,47 +254,56 @@ impl<'a> TermsBuilder<'a> self } +} +impl<'a> TermsBuilder<'a> +{ pub fn build(self) -> Terms<'a> { assert!(self.required_components.is_sorted()); - //assert!(self.optional_components.is_sorted()); + assert!(self.excluded_components.is_sorted()); Terms { required_components: self.required_components, - //optional_components: self.optional_components, + excluded_components: self.excluded_components, } } } -pub trait Term +pub trait TermWithoutField +{ + fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>); +} + +pub trait TermWithField { - type Handle<'a>; + type Field<'a>; - fn apply_to_terms_builder(terms_builder: TermsBuilder) -> TermsBuilder; + fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>); - fn create_handle<'world>( - get_component: &impl Fn(Uid) -> Option<&'world EntityComponent>, + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Handle<'world>; + ) -> Self::Field<'world>; } -impl<ComponentRefT: ComponentRef> Term for ComponentRefT +impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT { - type Handle<'a> = ComponentRefT::Handle<'a>; + type Field<'a> = ComponentRefT::Handle<'a>; - fn apply_to_terms_builder(terms_builder: TermsBuilder) -> TermsBuilder + fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>) { - terms_builder.with::<ComponentRefT::Component>() + terms_builder.with::<ComponentRefT::Component>(); } - fn create_handle<'world>( - get_component: &impl Fn(Uid) -> Option<&'world EntityComponent>, + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Handle<'world> + ) -> Self::Field<'world> { - Self::Handle::from_locked_optional_component( - get_component(ComponentRefT::Component::id()) + Self::Field::from_locked_optional_component( + entity_handle + .get_component(ComponentRefT::Component::id()) .map(|component| &component.component), world, ) @@ -268,32 +316,37 @@ impl<ComponentRefT: ComponentRef> Term for ComponentRefT } } -/// A sequence of references (immutable or mutable) to components. -pub trait TermSequence +pub trait TermWithoutFieldTuple { - type Handles<'component>; + fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>); +} + +pub trait TermWithFieldTuple +{ + type Fields<'component>; - fn build_terms() -> Terms<'static>; + fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>); - fn from_components<'component>( - get_component: impl Fn(Uid) -> Option<&'component EntityComponent>, + fn get_fields<'component>( + entity_handle: &EntityHandle<'component>, world: &'component World, - ) -> Self::Handles<'component>; + ) -> Self::Fields<'component>; } -pub struct ComponentIter<'query, 'world, Terms, EntityHandleIter> +pub struct ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> where + FieldTerms: TermWithFieldTuple + 'world, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, iter: EntityHandleIter, - comps_pd: PhantomData<Terms>, + comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, Terms, EntityHandleIter> - ComponentIter<'query, 'world, Terms, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> + ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> where - Terms: TermSequence + 'world, + FieldTerms: TermWithFieldTuple + 'world, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { @@ -309,47 +362,41 @@ where } } -impl<'query, 'world, Terms, EntityHandleIter> Iterator - for ComponentIter<'query, 'world, Terms, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator + for ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> where - Terms: TermSequence + 'world, + FieldTerms: TermWithFieldTuple + 'world, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { - type Item = Terms::Handles<'query>; + type Item = FieldTerms::Fields<'query>; fn next(&mut self) -> Option<Self::Item> { let entity_handle = self.iter.next()?; - Some(Terms::from_components( - |component_uid| { - entity_handle - .components() - .get(entity_handle.get_component_index(component_uid)?) - }, - self.world, - )) + Some(FieldTerms::get_fields(&entity_handle, self.world)) } } -pub struct ComponentAndEuidIter<'query, 'world, Terms, EntityHandleIter> +pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where + FieldTerms: TermWithFieldTuple + 'world, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, iter: EntityHandleIter, - comps_pd: PhantomData<Terms>, + comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, Terms, EntityHandleIter> Iterator - for ComponentAndEuidIter<'query, 'world, Terms, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator + for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - Terms: TermSequence + 'world, + FieldTerms: TermWithFieldTuple + 'world, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { - type Item = (Uid, Terms::Handles<'query>); + type Item = (Uid, FieldTerms::Fields<'query>); fn next(&mut self) -> Option<Self::Item> { @@ -357,14 +404,7 @@ where Some(( entity_handle.uid(), - Terms::from_components( - |component_uid| { - entity_handle - .components() - .get(entity_handle.get_component_index(component_uid)?) - }, - self.world, - ), + FieldTerms::get_fields(&entity_handle, self.world), )) } } @@ -372,28 +412,33 @@ where macro_rules! impl_term_sequence { ($c: tt) => { seq!(I in 0..=$c { - impl<#(Term~I: Term,)*> TermSequence for (#(Term~I,)*) + impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*) { - type Handles<'component> = (#(Term~I::Handle<'component>,)*); - - fn build_terms() -> Terms<'static> + fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>) { - let mut term_builder = Terms::builder(); - #( - term_builder = - Term~I::apply_to_terms_builder(term_builder); + Term~I::apply_to_terms_builder(terms_builder); )* + } + } + + impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*) + { + type Fields<'component> = (#(Term~I::Field<'component>,)*); - term_builder.build() + fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>) + { + #( + Term~I::apply_to_terms_builder(terms_builder); + )* } - fn from_components<'component>( - get_component: impl Fn(Uid) -> Option<&'component EntityComponent>, + fn get_fields<'component>( + entity_handle: &EntityHandle<'component>, world: &'component World, - ) -> Self::Handles<'component> + ) -> Self::Fields<'component> { - (#(Term~I::create_handle(&get_component, world),)*) + (#(Term~I::get_field(entity_handle, world),)*) } } }); @@ -404,19 +449,21 @@ seq!(C in 0..=16 { impl_term_sequence!(C); }); -impl TermSequence for () +impl TermWithoutFieldTuple for () { - type Handles<'component> = (); + fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {} +} - fn build_terms() -> Terms<'static> - { - Terms::builder().build() - } +impl TermWithFieldTuple for () +{ + type Fields<'component> = (); + + fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {} - fn from_components<'component>( - _get_component: impl Fn(Uid) -> Option<&'component EntityComponent>, + fn get_fields<'component>( + _entity_handle: &EntityHandle<'_>, _world: &'component World, - ) -> Self::Handles<'component> + ) -> Self::Fields<'component> { } } diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index a3e8701..7d24dc9 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -1,10 +1,13 @@ //! Low-level querying. -use std::iter::{repeat_n, Filter, FlatMap, RepeatN, Zip}; +use std::iter::{repeat_n, FlatMap, RepeatN, Zip}; use crate::component::storage::archetype::{Archetype, ArchetypeEntity, EntityIter}; -use crate::component::storage::{ArchetypeRefIter, Storage as ComponentStorage}; +use crate::component::storage::{ + ArchetypeRefIter, + ArchetypeSearchTerms, + Storage as ComponentStorage, +}; use crate::lock::ReadGuard; -use crate::query::options::Options; use crate::query::Terms; use crate::uid::Uid; use crate::{EntityComponent, World}; @@ -21,19 +24,21 @@ impl<'world, 'terms> Query<'world, 'terms> { /// Iterates over the entities matching this query. #[must_use] - pub fn iter<OptionsT: Options>(&self) -> Iter<'_> + pub fn iter(&self) -> Iter<'_> { Iter { iter: self .component_storage - .search_archetypes(self.terms.required_components.as_ref()) + .search_archetypes(ArchetypeSearchTerms { + required_components: &self.terms.required_components, + excluded_components: &self.terms.excluded_components, + }) .flat_map( (|archetype| { repeat_n(archetype, archetype.entity_cnt()) .zip(archetype.entities()) }) as ComponentIterMapFn, - ) - .filter(|(_, entity)| OptionsT::entity_filter(&entity.components)), + ), } } @@ -85,16 +90,11 @@ impl<'query> EntityHandle<'query> #[inline] #[must_use] - pub fn components(&self) -> &'query [EntityComponent] + pub fn get_component(&self, component_uid: Uid) -> Option<&'query EntityComponent> { - &self.entity.components - } + let index = self.archetype.get_index_for_component(component_uid)?; - #[inline] - #[must_use] - pub fn get_component_index(&self, component_uid: Uid) -> Option<usize> - { - self.archetype.get_index_for_component(component_uid) + Some(self.entity.components.get(index).unwrap()) } } @@ -102,14 +102,8 @@ type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>; type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>; -type ComponentIterFilterFn = - for<'a, 'b> fn(&'a (&'b Archetype, &'b ArchetypeEntity)) -> bool; - -type QueryEntityIter<'query> = Filter< - FlatMap< - ArchetypeRefIter<'query>, - ComponentIterMapFnOutput<'query>, - ComponentIterMapFn, - >, - ComponentIterFilterFn, +type QueryEntityIter<'query> = FlatMap< + ArchetypeRefIter<'query, 'query>, + ComponentIterMapFnOutput<'query>, + ComponentIterMapFn, >; diff --git a/ecs/src/query/options.rs b/ecs/src/query/options.rs deleted file mode 100644 index 6318359..0000000 --- a/ecs/src/query/options.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::marker::PhantomData; - -use hashbrown::HashSet; - -use crate::component::Component; -use crate::EntityComponent; - -/// Query options. -pub trait Options -{ - fn entity_filter(components: &[EntityComponent]) -> bool; -} - -impl Options for () -{ - fn entity_filter(_components: &[EntityComponent]) -> bool - { - true - } -} - -pub struct With<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Options for With<ComponentT> -where - ComponentT: Component, -{ - fn entity_filter(components: &[EntityComponent]) -> bool - { - let ids_set = components - .iter() - .map(|component| component.id) - .collect::<HashSet<_>>(); - - ids_set.contains(&ComponentT::id()) - } -} - -pub struct Not<OptionsT> -where - OptionsT: Options, -{ - _pd: PhantomData<OptionsT>, -} - -impl<OptionsT> Options for Not<OptionsT> -where - OptionsT: Options, -{ - fn entity_filter(components: &[EntityComponent]) -> bool - { - !OptionsT::entity_filter(components) - } -} diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs new file mode 100644 index 0000000..7f24147 --- /dev/null +++ b/ecs/src/query/term.rs @@ -0,0 +1,38 @@ +use std::marker::PhantomData; + +use crate::component::Component; +use crate::query::{TermWithoutField, TermsBuilder, TermsBuilderInterface}; + +pub struct With<ComponentT> +where + ComponentT: Component, +{ + _pd: PhantomData<ComponentT>, +} + +impl<ComponentT> TermWithoutField for With<ComponentT> +where + ComponentT: Component, +{ + fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>) + { + terms_builder.with::<ComponentT>(); + } +} + +pub struct Without<ComponentT> +where + ComponentT: Component, +{ + _pd: PhantomData<ComponentT>, +} + +impl<ComponentT> TermWithoutField for Without<ComponentT> +where + ComponentT: Component, +{ + fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>) + { + terms_builder.without::<ComponentT>(); + } +} diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs index 79dfe85..fa36689 100644 --- a/ecs/tests/query.rs +++ b/ecs/tests/query.rs @@ -1,7 +1,11 @@ use ecs::component::Component; -use ecs::query::TermSequence as QueryTermSequence; +use ecs::query::term::Without; +use ecs::query::{ + TermWithFieldTuple as QueryTermWithFieldTuple, + TermWithoutFieldTuple as QueryTermWithoutFieldTuple, +}; use ecs::uid::Uid; -use ecs::{Component, World}; +use ecs::{Component, Query, World}; use parking_lot::{Mutex, Once}; pub static SETUP: Once = Once::new(); @@ -42,26 +46,26 @@ fn setup() }); } -fn assert_query_finds_ents<QueryTerms>(world: &World, mut expected_ent_ids: Vec<Uid>) -where - QueryTerms: QueryTermSequence, +fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>( + query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>, + mut expected_ent_ids: Vec<Uid>, +) where + QueryFieldTerms: QueryTermWithFieldTuple, + QueryFieldlessTerms: QueryTermWithoutFieldTuple, { assert!( - world - .query::<QueryTerms, ()>() - .iter_with_euids() - .all(|(ent_id, _)| { - let Some(index) = expected_ent_ids - .iter() - .position(|expected_id| *expected_id == ent_id) - else { - return false; - }; - - expected_ent_ids.remove(index); - - true - }), + query.iter_with_euids().all(|(ent_id, _)| { + let Some(index) = expected_ent_ids + .iter() + .position(|expected_id| *expected_id == ent_id) + else { + return false; + }; + + expected_ent_ids.remove(index); + + true + }), "Unexpected entity was found. Expected entities left: {expected_ent_ids:?}" ); @@ -90,8 +94,8 @@ fn query_archetype_exists_with_edges_to_next_archetypes() let ent_3_id = world.create_entity((A, B, C, E)); let ent_4_id = world.create_entity((A, B, C, G, F)); - assert_query_finds_ents::<(&A, &B, &C)>( - &world, + assert_query_finds_ents( + world.query::<(&A, &B, &C), ()>(), vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id], ); } @@ -109,7 +113,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype() let ent_2_id = world.create_entity((A, B, F)); - assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -125,7 +129,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev() let ent_2_id = world.create_entity((A, B, C, D, F)); - assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -141,7 +145,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype() let ent_2_id = world.create_entity((A, B, F)); - assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -157,7 +161,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev() let ent_2_id = world.create_entity((A, B, C, D, E, F)); - assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -173,7 +177,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype() let ent_2_id = world.create_entity((A, B, G)); - assert_query_finds_ents::<(&A, &B, &G)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -189,7 +193,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev() let ent_2_id = world.create_entity((A, B, C, D, E, F, G)); - assert_query_finds_ents::<(&A, &B, &G)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -205,7 +209,10 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp() let ent_2_id = world.create_entity((A, B, G)); - assert_query_finds_ents::<(&A, &Option<E>, &G)>(&world, vec![ent_1_id, ent_2_id]); + assert_query_finds_ents( + world.query::<(&A, &Option<E>, &G), ()>(), + vec![ent_1_id, ent_2_id], + ); } #[test] @@ -224,7 +231,7 @@ fn query_archetype_nonexistant() world.create_entity((A, B, C, G, F)); - assert_query_finds_ents::<(&A, &E)>(&world, vec![ent_2_id, ent_3_id]); + assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]); } #[test] @@ -241,5 +248,75 @@ fn query_archetype_nonexistant_and_opt_comp() let ent_3_id = world.create_entity((A, B, C, E)); world.create_entity((A, B, C, G, F)); - assert_query_finds_ents::<(&A, &E, &Option<D>)>(&world, vec![ent_2_id, ent_3_id]); + assert_query_finds_ents( + world.query::<(&A, &E, &Option<D>), ()>(), + vec![ent_2_id, ent_3_id], + ); +} + +#[test] +fn query_without_comp_and_archetype_exists() +{ + setup(); + + let _test_lock = TEST_LOCK.lock(); + + let mut world = World::new(); + + let ent_1_id = world.create_entity((A, B, C)); + + world.create_entity((A, B, C, E)); + world.create_entity((A, B, C, F, E)); + + let ent_2_id = world.create_entity((A, B, C, G)); + let ent_3_id = world.create_entity((A, B, C, G, F)); + + assert_query_finds_ents( + world.query::<(&A, &B, &C), (Without<E>,)>(), + vec![ent_1_id, ent_2_id, ent_3_id], + ); +} + +#[test] +fn query_without_required_comp_and_archetype_exists() +{ + setup(); + + let _test_lock = TEST_LOCK.lock(); + + let mut world = World::new(); + + world.create_entity((A, B, C)); + + world.create_entity((A, B, C, E)); + world.create_entity((A, B, C, F, E)); + + world.create_entity((A, B, C, G)); + world.create_entity((A, B, C, G, F)); + + assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]); +} + +#[test] +fn query_without_comp_and_archetype_nonexistant() +{ + setup(); + + let _test_lock = TEST_LOCK.lock(); + + let mut world = World::new(); + + world.create_entity((A, B, C)); + + let ent_1_id = world.create_entity((A, B, C, E)); + + world.create_entity((A, B, C, F, E)); + + let ent_2_id = world.create_entity((A, B, C, G, E)); + world.create_entity((A, B, C, G, F, E)); + + assert_query_finds_ents( + world.query::<(&A, &E), (Without<F>,)>(), + vec![ent_1_id, ent_2_id], + ); } |