diff options
Diffstat (limited to 'ecs/src/query.rs')
-rw-r--r-- | ecs/src/query.rs | 362 |
1 files changed, 231 insertions, 131 deletions
diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 1889e00..5f13579 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -1,31 +1,34 @@ use std::any::type_name; -use std::borrow::Cow; use std::marker::PhantomData; use seq_macro::seq; -use crate::component::{Component, FromLockedOptional, Ref as ComponentRef}; -use crate::query::flexible::{ - EntityHandle, - Iter as FlexibleQueryIter, - Query as FlexibleQuery, +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, }; -use crate::system::{Param as SystemParam, System}; -use crate::uid::Uid; -use crate::util::VecExt; +use crate::entity::Handle as EntityHandle; +use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; +use crate::uid::{Kind as UidKind, Uid, With as WithUid}; +use crate::util::array_vec::ArrayVec; +use crate::util::Array; use crate::World; pub mod flexible; pub mod term; +// A term tuple type can have a maximum of 17 elements +pub const MAX_TERM_CNT: usize = 17; + #[derive(Debug)] pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where FieldTerms: TermWithFieldTuple, FieldlessTerms: TermWithoutFieldTuple, { - world: &'world World, - inner: FlexibleQuery<'world, 'static>, + inner: FlexibleQuery<'world, MAX_TERM_CNT>, _pd: PhantomData<(FieldTerms, FieldlessTerms)>, } @@ -39,19 +42,19 @@ where #[must_use] pub fn iter<'query>( &'query self, - ) -> ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> + ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - ComponentIter { - world: self.world, - iter: self.inner.iter(), + Iter { + world: self.inner.world(), + inner: self.inner.iter(), comps_pd: PhantomData, } } /// Iterates over the entities matching this query, the iterator item being the entity - /// [`Uid`] and the entity components. + /// [`Uid`] and the matching entity components. #[must_use] pub fn iter_with_euids<'query>( &'query self, @@ -60,7 +63,7 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentAndEuidIter { - world: self.world, + world: self.inner.world(), iter: self.inner.iter(), comps_pd: PhantomData, } @@ -70,20 +73,20 @@ where /// `func`. /// /// This function exists so that a custom [`EntityHandle`] iterator can be given to - /// [`ComponentIter`] without giving the user access to a reference to the [`World`]. + /// [`Iter`] without giving the user access to a reference to the [`World`]. #[must_use] pub fn iter_with<'query, OutIter>( &'query self, func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter, - ) -> ComponentIter<'query, 'world, FieldTerms, OutIter> + ) -> Iter<'query, 'world, FieldTerms, OutIter> where OutIter: Iterator<Item = EntityHandle<'query>>, { tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - ComponentIter { - world: self.world, - iter: func(self.inner.iter()), + Iter { + world: self.inner.world(), + inner: func(self.inner.iter()), comps_pd: PhantomData, } } @@ -95,6 +98,25 @@ where Some(self.inner.iter().nth(entity_index)?.uid()) } + /// Returns a new `Query` created from a [`FlexibleQuery`]. + /// + /// # Important notes + /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms + /// in the given [`FlexibleQuery`], otherwise any method call or iterating might + /// panic. + #[must_use] + pub fn from_flexible_query( + flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>, + ) -> Self + { + // TODO: Check compatability of terms + + Self { + inner: flexible_query, + _pd: PhantomData, + } + } + pub(crate) fn new(world: &'world World) -> Self { let mut terms_builder = Terms::builder(); @@ -103,7 +125,6 @@ where FieldlessTerms::apply_terms_to_builder(&mut terms_builder); Self { - world, inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } @@ -113,10 +134,10 @@ where impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator for &'query Query<'world, FieldTerms, FieldlessTerms> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, FieldlessTerms: TermWithoutFieldTuple, { - type IntoIter = ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; + type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; type Item = FieldTerms::Fields<'query>; fn into_iter(self) -> Self::IntoIter @@ -133,60 +154,58 @@ where { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(world) } } #[derive(Debug)] -pub struct Terms<'a> +pub struct Terms<const MAX_TERM_CNT: usize> { - required_components: Cow<'a, [Uid]>, - excluded_components: Cow<'a, [Uid]>, + required_components: ArrayVec<Uid, MAX_TERM_CNT>, + excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, } -impl<'a> Terms<'a> +impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT> { - pub fn builder() -> TermsBuilder<'a> + pub fn builder() -> TermsBuilder<MAX_TERM_CNT> { TermsBuilder::default() } } #[derive(Debug, Default)] -pub struct TermsBuilder<'a> +#[must_use] +pub struct TermsBuilder<const MAX_TERM_CNT: usize> { - required_components: Cow<'a, [Uid]>, - excluded_components: Cow<'a, [Uid]>, + required_components: ArrayVec<Uid, MAX_TERM_CNT>, + excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, } -pub trait TermsBuilderInterface<'a> +#[allow(clippy::return_self_not_must_use)] +pub trait TermsBuilderInterface { - fn with<ComponentT: Component>(self) -> Self; + fn with<WithUidT: WithUid>(self) -> Self; + + fn without<WithUidT: WithUid>(self) -> Self; - fn without<ComponentT: Component>(self) -> Self; + fn with_required(self, ids: impl Array<Uid>) -> Self; - fn with_required_ids(self, ids: &'a mut [Uid]) -> Self; + fn without_ids(self, ids: impl Array<Uid>) -> Self; } macro_rules! impl_terms_builder { ($($impl_content: tt)*) => { - impl<'a> TermsBuilderInterface<'a> for TermsBuilder<'a> { + impl<const MAX_TERM_CNT: usize> + TermsBuilderInterface for TermsBuilder<MAX_TERM_CNT> + { $($impl_content)* } - impl<'a> TermsBuilderInterface<'a> for &mut TermsBuilder<'a> { + impl<const MAX_TERM_CNT: usize> + TermsBuilderInterface for &mut TermsBuilder<MAX_TERM_CNT> + { $($impl_content)* } }; @@ -194,74 +213,101 @@ macro_rules! impl_terms_builder { impl_terms_builder! { #[allow(unused_mut)] - fn with<ComponentT: Component>(mut self) -> Self + fn with<WithUidT: WithUid>(mut self) -> Self { - if ComponentT::is_optional() { - return self; - } + let insert_index = self.required_components + .partition_point(|id| *id <= WithUidT::uid()); self.required_components - .to_mut() - .insert_at_partition_point_by_key(ComponentT::id(), |id| *id); + .insert(insert_index, WithUidT::uid()); self } #[allow(unused_mut)] - fn without<ComponentT: Component>(mut self) -> Self + fn without<WithUidT: WithUid>(mut self) -> Self { - if ComponentT::is_optional() { - panic!( - "{}::without cannot take optional component", - type_name::<Self>() - ); - } + let insert_index = self.excluded_components + .partition_point(|id| *id <= WithUidT::uid()); self.excluded_components - .to_mut() - .insert_at_partition_point_by_key(ComponentT::id(), |id| *id); + .insert(insert_index, WithUidT::uid()); self } #[allow(unused_mut)] - fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self + fn with_required(mut self, mut ids: impl Array<Uid>) -> Self { - if ids.is_empty() { + if !ids.as_ref().is_sorted() { + ids.as_mut().sort(); + } + + if self.required_components.is_empty() { + self.required_components.extend(ids); return self; } - if !ids.is_sorted() { - ids.sort(); + let mut id_iter = ids.into_iter(); + + while let Some(id) = id_iter.next() { + let insert_index = self.required_components + .partition_point(|other_id| *other_id <= id); + + if insert_index == self.required_components.len() { + self.required_components.extend([id].into_iter().chain(id_iter)); + + return self; + } + + self.required_components + .insert(insert_index, id); + } - if self.required_components.is_empty() { - self.required_components = Cow::Borrowed(ids); + self + } + + #[allow(unused_mut)] + fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self + { + if !ids.as_ref().is_sorted() { + ids.as_mut().sort(); + } + if self.excluded_components.is_empty() { + self.excluded_components.extend(ids); return self; } - let first_id_pp_index = self.required_components.partition_point(|req_comp_id| { - req_comp_id <= ids.first().expect("Cannot happend since not empty") - }); + let mut id_iter = ids.into_iter(); + + while let Some(id) = id_iter.next() { + let insert_index = self.excluded_components + .partition_point(|other_id| *other_id <= id); + + if insert_index == self.excluded_components.len() { + self.excluded_components.extend([id].into_iter().chain(id_iter)); - let removed = self - .required_components - .to_mut() - .splice(first_id_pp_index..first_id_pp_index, ids.iter().copied()); + return self; + } + + self.excluded_components + .insert(insert_index, id); - assert_eq!(removed.count(), 0); + } self } } -impl<'a> TermsBuilder<'a> +impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT> { - pub fn build(self) -> Terms<'a> + #[must_use] + pub fn build(self) -> Terms<MAX_TERM_CNT> { - assert!(self.required_components.is_sorted()); - assert!(self.excluded_components.is_sorted()); + debug_assert!(self.required_components.is_sorted()); + debug_assert!(self.excluded_components.is_sorted()); Terms { required_components: self.required_components, @@ -272,14 +318,18 @@ impl<'a> TermsBuilder<'a> pub trait TermWithoutField { - fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>); + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); } pub trait TermWithField { type Field<'a>; - fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>); + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); fn get_field<'world>( entity_handle: &EntityHandle<'world>, @@ -287,13 +337,56 @@ pub trait TermWithField ) -> Self::Field<'world>; } -impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT +impl<ComponentT: Component> TermWithField for &ComponentT +{ + type Field<'a> = ComponentHandle<'a, ComponentT>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with::<ComponentT>(); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + _world: &'world World, + ) -> Self::Field<'world> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { + panic!( + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + }) + } +} + +impl<ComponentT: Component> TermWithField for &mut ComponentT { - type Field<'a> = ComponentRefT::Handle<'a>; + type Field<'a> = ComponentHandleMut<'a, ComponentT>; - fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>) + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) { - terms_builder.with::<ComponentRefT::Component>(); + terms_builder.with::<ComponentT>(); } fn get_field<'world>( @@ -301,16 +394,26 @@ impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT world: &'world World, ) -> Self::Field<'world> { - Self::Field::from_locked_optional_component( - entity_handle - .get_component(ComponentRefT::Component::id()) - .map(|component| &component.component), - world, - ) - .unwrap_or_else(|err| { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { panic!( - "Taking component {} lock failed: {err}", - type_name::<ComponentRefT::Component>() + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() ); }) } @@ -318,14 +421,18 @@ impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT pub trait TermWithoutFieldTuple { - fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>); + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); } pub trait TermWithFieldTuple { type Fields<'component>; - fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>); + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); fn get_fields<'component>( entity_handle: &EntityHandle<'component>, @@ -333,39 +440,20 @@ pub trait TermWithFieldTuple ) -> Self::Fields<'component>; } -pub struct ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> +pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, - iter: EntityHandleIter, + inner: EntityHandleIter, comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, FieldTerms, EntityHandleIter> - ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple + 'world, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - /// Creates a new iterator from the given entity handle iterator. - /// - /// # Important - /// All of the yielded entities of the entity handle iterator should match the - /// terms `Terms`. The [`Self::next`] function will panic if it encounters a - /// entity that does not match the terms `Terms`. - pub fn new(world: &'world World, iter: EntityHandleIter) -> Self - { - Self { world, iter, comps_pd: PhantomData } - } -} - impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for ComponentIter<'query, 'world, FieldTerms, EntityHandleIter> + for Iter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { @@ -373,7 +461,7 @@ where fn next(&mut self) -> Option<Self::Item> { - let entity_handle = self.iter.next()?; + let entity_handle = self.inner.next()?; Some(FieldTerms::get_fields(&entity_handle, self.world)) } @@ -381,7 +469,7 @@ where pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, @@ -392,7 +480,7 @@ where impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - FieldTerms: TermWithFieldTuple + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { @@ -414,7 +502,9 @@ macro_rules! impl_term_sequence { seq!(I in 0..=$c { impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*) { - fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>) + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT> + ) { #( Term~I::apply_to_terms_builder(terms_builder); @@ -426,7 +516,9 @@ macro_rules! impl_term_sequence { { type Fields<'component> = (#(Term~I::Field<'component>,)*); - fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>) + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT> + ) { #( Term~I::apply_to_terms_builder(terms_builder); @@ -451,14 +543,22 @@ seq!(C in 0..=16 { impl TermWithoutFieldTuple for () { - fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {} + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } } impl TermWithFieldTuple for () { type Fields<'component> = (); - fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {} + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } fn get_fields<'component>( _entity_handle: &EntityHandle<'_>, |