From 624e6dd450944e67d84ad7afda54ae2abd277aba Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 23 Jul 2026 17:38:51 +0200 Subject: refactor(engine-ecs): remove distinction between query terms with & without a field --- engine-ecs/examples/component_changed_event.rs | 2 +- engine-ecs/src/lib.rs | 12 +- engine-ecs/src/pair.rs | 103 +++++---- engine-ecs/src/phase.rs | 2 +- engine-ecs/src/query.rs | 302 +++++++++++++------------ engine-ecs/src/query/flexible.rs | 24 +- engine-ecs/src/query/term.rs | 121 ++++++---- engine-ecs/src/uid.rs | 2 +- engine-ecs/tests/query.rs | 43 ++-- 9 files changed, 339 insertions(+), 272 deletions(-) diff --git a/engine-ecs/examples/component_changed_event.rs b/engine-ecs/examples/component_changed_event.rs index a963b41..3f6b4a8 100644 --- a/engine-ecs/examples/component_changed_event.rs +++ b/engine-ecs/examples/component_changed_event.rs @@ -65,7 +65,7 @@ fn main() world.step(); - for (mut greeting,) in &world.query::<(&mut Greeting,), ()>() { + for (mut greeting,) in &world.query::<(&mut Greeting,)>() { if greeting.greeting == "Good afternoon" { greeting.greeting = "Yo yo".to_string(); greeting.set_changed(); diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs index 3a867ba..97316f9 100644 --- a/engine-ecs/src/lib.rs +++ b/engine-ecs/src/lib.rs @@ -45,8 +45,7 @@ use crate::phase::{ }; use crate::query::flexible::Query as FlexibleQuery; use crate::query::{ - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, + TermTuple as QueryTermTuple, Terms as QueryTerms, TermsBuilderInterface, MAX_TERM_CNT as QUERY_MAX_TERM_CNT, @@ -286,12 +285,11 @@ impl World extension.collect(extension_collector); } - pub fn query( - &self, - ) -> Query<'_, FieldTerms, FieldlessTerms> + pub fn query<'this, TermsT>( + &'this self, + ) -> Query<'this, TermsT> where - FieldTerms: QueryTermWithFieldTuple, - FieldlessTerms: QueryTermWithoutFieldTuple, + TermsT: QueryTermTuple<'this>, { Query::new(self) } diff --git a/engine-ecs/src/pair.rs b/engine-ecs/src/pair.rs index 3668f1b..1168f67 100644 --- a/engine-ecs/src/pair.rs +++ b/engine-ecs/src/pair.rs @@ -14,10 +14,11 @@ use crate::entity::{ MatchingComponentIter as EntityMatchingComponentIter, }; use crate::query::{ - TermWithField as QueryTermWithField, + Term as QueryTerm, TermsBuilder as QueryTermsBuilder, TermsBuilderInterface, }; +use crate::tuple::Tuple; use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid}; use crate::util::impl_multiple; use crate::{Component, EntityComponentRef, World}; @@ -191,12 +192,13 @@ where } } -impl QueryTermWithField for Pair +impl<'world, Relation, Target> QueryTerm<'world> for Pair where Relation: Component, Target: Component, { - type Field<'a> = ComponentHandle<'a, Target>; + type AddField = + Fields::WithElementAtEnd>; fn apply_to_terms_builder( terms_builder: &mut QueryTermsBuilder, @@ -205,31 +207,38 @@ where terms_builder.present([Pair::::uid()]); } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, _world: &'world World, - ) -> Self::Field<'world> + fields: Fields, + ) -> Self::AddField { let target_component = entity_handle .get_matching_components(Pair::::uid()) .next() .expect("Not possible"); - Self::Field::from_entity_component_ref(&target_component).unwrap_or_else(|err| { - panic!( - "Creating handle to target component {} failed: {err}", - type_name::() - ); - }) + let target_component = match ComponentHandle::::from_entity_component_ref(&target_component) { + Ok(target_component) => target_component, + Err(err) => { + panic!( + "Creating handle to target component {} failed: {err}", + type_name::() + ); + } + }; + + fields.with_elem(target_component) } } -impl QueryTermWithField for Pair +impl<'world, Relation, Target> QueryTerm<'world> for Pair where Relation: Component, Target: Component, { - type Field<'a> = ComponentHandleMut<'a, Target>; + type AddField = + Fields::WithElementAtEnd>; fn apply_to_terms_builder( terms_builder: &mut QueryTermsBuilder, @@ -238,35 +247,40 @@ where terms_builder.present([Pair::::uid()]); } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Field<'world> + fields: Fields, + ) -> Self::AddField { let target_component = entity_handle .get_matching_components(Pair::::uid()) .next() .expect("Not possible"); - Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else( - |err| { + let target_component = match ComponentHandleMut::::from_entity_component_ref(&target_component, world) { + Ok(target_component) => target_component, + Err(err) => { panic!( - "Creating handle to target component {} failed: {err}", + "Creating mut handle to target component {} failed: {err}", type_name::() ); - }, - ) + } + }; + + fields.with_elem(target_component) } } -// TODO: implement QueryTermWithField for Pair<&Relation, Target> (or equivalent) -// TODO: implement QueryTermWithField for Pair<&mut Relation, Target> (or equivalent) +// TODO: implement QueryTerm for Pair<&Relation, Target> (or equivalent) +// TODO: implement QueryTerm for Pair<&mut Relation, Target> (or equivalent) -impl QueryTermWithField for Pair +impl<'world, Relation> QueryTerm<'world> for Pair where Relation: Component, { - type Field<'a> = WithWildcard<'a, Relation, Wildcard>; + type AddField = + Fields::WithElementAtEnd>; fn apply_to_terms_builder( terms_builder: &mut QueryTermsBuilder, @@ -275,21 +289,24 @@ where terms_builder.present([Self::uid()]); } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Field<'world> + fields: Fields, + ) -> Self::AddField { let first_matching_comp = entity_handle .get_matching_components(Self::uid()) .next() .expect("Not possible"); - WithWildcard { - world, - component_ref: first_matching_comp, - _pd: PhantomData, - } + fields.with_elem( + WithWildcard { + world, + component_ref: first_matching_comp, + _pd: PhantomData, + } + ) } } @@ -320,11 +337,12 @@ where } } -impl QueryTermWithField for &'_ [Pair] +impl<'world, Relation> QueryTerm<'world> for &[Pair] where Relation: Component, { - type Field<'a> = MultipleWithWildcard<'a, Relation, Wildcard>; + type AddField = + Fields::WithElementAtEnd>; fn apply_to_terms_builder( _terms_builder: &mut QueryTermsBuilder, @@ -332,16 +350,19 @@ where { } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Field<'world> - { - MultipleWithWildcard { - entity_handle: entity_handle.clone(), - world, - _pd: PhantomData, - } + fields: Fields, + ) -> Self::AddField + { + fields.with_elem( + MultipleWithWildcard { + entity_handle: entity_handle.clone(), + world, + _pd: PhantomData, + } + ) } } diff --git a/engine-ecs/src/phase.rs b/engine-ecs/src/phase.rs index cccf978..cd8ab56 100644 --- a/engine-ecs/src/phase.rs +++ b/engine-ecs/src/phase.rs @@ -38,7 +38,7 @@ pub(crate) struct HasSystem; #[cfg(debug_assertions)] -pub(crate) fn check_parents(phase_query: Query<(Pair, Option<&EntityName>), (With,)>) +pub(crate) fn check_parents(phase_query: Query<(Pair, Option<&EntityName>, With,)>) { for (child_of, ent_name) in &phase_query { let Some(parent_ent) = child_of.get_target_ent() else { diff --git a/engine-ecs/src/query.rs b/engine-ecs/src/query.rs index 1b7bf43..8549411 100644 --- a/engine-ecs/src/query.rs +++ b/engine-ecs/src/query.rs @@ -2,6 +2,8 @@ use std::any::type_name; use std::marker::PhantomData; use seq_macro::seq; +use paste::paste; +use util_macros::sub; use crate::component::{ Component, @@ -11,6 +13,7 @@ use crate::component::{ 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::tuple::Tuple; use crate::uid::Uid; use crate::util::array_vec::ArrayVec; use crate::util::Array; @@ -23,28 +26,26 @@ pub mod term; pub const MAX_TERM_CNT: usize = 17; #[derive(Debug)] -pub struct Query<'world, FieldTerms, FieldlessTerms = ()> +pub struct Query<'world, TermsT> where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, + TermsT: TermTuple<'world>, { inner: FlexibleQuery<'world, MAX_TERM_CNT>, - _pd: PhantomData<(FieldTerms, FieldlessTerms)>, + _pd: PhantomData, } -impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms> +impl<'world, TermsT> Query<'world, TermsT> where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, + TermsT: TermTuple<'world>, { /// Iterates over the entities matching this query, the iterator item being the entity /// components. #[must_use] - pub fn iter<'query>( - &'query self, - ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> + pub fn iter<'this>( + &'this self, + ) -> Iter<'this, 'world, TermsT, FlexibleQueryIter<'this, 'world>> { - tracing::trace!("Searching for {}", std::any::type_name::()); + tracing::trace!("Searching for {}", std::any::type_name::()); Iter { world: self.inner.world(), @@ -56,11 +57,11 @@ where /// Iterates over the entities matching this query, the iterator item being the entity /// [`Uid`] and the matching entity components. #[must_use] - pub fn iter_with_euids<'query>( - &'query self, - ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> + pub fn iter_with_euids<'this>( + &'this self, + ) -> ComponentAndEuidIter<'this, 'world, TermsT, FlexibleQueryIter<'this, 'world>> { - tracing::trace!("Searching for {}", std::any::type_name::()); + tracing::trace!("Searching for {}", std::any::type_name::()); ComponentAndEuidIter { world: self.inner.world(), @@ -75,14 +76,14 @@ where /// This function exists so that a custom [`EntityHandle`] iterator can be given to /// [`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, - ) -> Iter<'query, 'world, FieldTerms, OutIter> + pub fn iter_with<'this, OutIter>( + &'this self, + func: impl FnOnce(FlexibleQueryIter<'this, 'world>) -> OutIter, + ) -> Iter<'this, 'world, TermsT, OutIter> where - OutIter: Iterator>, + OutIter: Iterator>, { - tracing::trace!("Searching for {}", std::any::type_name::()); + tracing::trace!("Searching for {}", std::any::type_name::()); Iter { world: self.inner.world(), @@ -101,7 +102,7 @@ where /// Returns a new `Query` created from a [`FlexibleQuery`]. /// /// # Important notes - /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms + /// The terms in `TermsT` must be compatible with the terms /// in the given [`FlexibleQuery`], otherwise any method call or iterating might /// panic. #[must_use] @@ -126,8 +127,7 @@ where { let mut terms_builder = Terms::builder(); - FieldTerms::apply_terms_to_builder(&mut terms_builder); - FieldlessTerms::apply_terms_to_builder(&mut terms_builder); + TermsT::apply_terms_to_builder(&mut terms_builder); Self { inner: world.flexible_query(terms_builder.build()), @@ -136,14 +136,13 @@ where } } -impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator - for &'query Query<'world, FieldTerms, FieldlessTerms> +impl<'query, 'world, TermsT> IntoIterator + for &'query Query<'world, TermsT> where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, + TermsT: TermTuple<'world>, { - type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; - type Item = FieldTerms::Fields<'query>; + type IntoIter = Iter<'query, 'world, TermsT, FlexibleQueryIter<'query, 'world>>; + type Item = TermsT::Fields; fn into_iter(self) -> Self::IntoIter { @@ -151,11 +150,10 @@ where } } -impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world> - for Query<'world, FieldTerms, FieldlessTerms> +impl<'world, TermsT> SystemParam<'world> + for Query<'world, TermsT> where - FieldTerms: TermWithFieldTuple, - FieldlessTerms: TermWithoutFieldTuple, + TermsT: TermTuple<'world>, { type Input = (); @@ -293,30 +291,24 @@ impl TermsBuilder } } -pub trait TermWithoutField +pub trait Term<'world> { - fn apply_to_terms_builder( - terms_builder: &mut TermsBuilder, - ); -} - -pub trait TermWithField -{ - type Field<'a>; + type AddField; fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, ); - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Field<'world>; + fields: Fields, + ) -> Self::AddField; } -impl TermWithField for &ComponentT +impl<'world, ComponentT: Component> Term<'world> for &ComponentT { - type Field<'a> = ComponentHandle<'a, ComponentT>; + type AddField = Fields::WithElementAtEnd>; fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, @@ -325,10 +317,11 @@ impl TermWithField for &ComponentT terms_builder.present([ComponentT::id()]); } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, _world: &'world World, - ) -> Self::Field<'world> + fields: Fields, + ) -> Self::AddField { assert!(!ComponentT::id().is_pair()); @@ -346,18 +339,23 @@ impl TermWithField for &ComponentT ); }; - Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::() - ); - }) + let component = match ComponentHandle::::from_entity_component_ref(&component) { + Ok(component) => component, + Err(err) => { + panic!( + "Creating handle to component {} failed: {err}", + type_name::() + ); + } + }; + + fields.with_elem(component) } } -impl TermWithField for &mut ComponentT +impl<'world, ComponentT: Component> Term<'world> for &mut ComponentT { - type Field<'a> = ComponentHandleMut<'a, ComponentT>; + type AddField = Fields::WithElementAtEnd>; fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, @@ -366,10 +364,11 @@ impl TermWithField for &mut ComponentT terms_builder.present([ComponentT::id()]); } - fn get_field<'world>( + fn add_field( entity_handle: &EntityHandle<'world>, world: &'world World, - ) -> Self::Field<'world> + fields: Fields, + ) -> Self::AddField { assert!(!ComponentT::id().is_pair()); @@ -387,81 +386,95 @@ impl TermWithField for &mut ComponentT ); }; - Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::() - ); - }) + let component = match ComponentHandleMut::::from_entity_component_ref(&component, world) { + Ok(component) => component, + Err(err) => { + panic!( + "Creating mut handle to component {} failed: {err}", + type_name::() + ); + } + }; + + fields.with_elem(component) } } -pub trait TermWithoutFieldTuple +impl<'world, TermT, FieldAcc> TermFieldSource<'world, FieldAcc> for TermT +where + TermT: Term<'world>, + TermT::AddField: Tuple, + FieldAcc: Tuple { - fn apply_terms_to_builder( - terms_builder: &mut TermsBuilder, - ); + type NewFieldAcc = TermT::AddField; + + fn collect_field( + entity_handle: &EntityHandle<'world>, + world: &'world World, + fields: FieldAcc, + ) -> Self::NewFieldAcc + { + Self::add_field(entity_handle, world, fields) + } } -pub trait TermWithFieldTuple +pub trait TermTuple<'world> { - type Fields<'component>; + type Fields; fn apply_terms_to_builder( terms_builder: &mut TermsBuilder, ); - fn get_fields<'component>( - entity_handle: &EntityHandle<'component>, - world: &'component World, - ) -> Self::Fields<'component>; + fn get_fields( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Fields; } -pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter> +pub struct Iter<'query, 'world, TermsT, EntityHandleIter> where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator>, + TermsT: TermTuple<'world>, + EntityHandleIter: Iterator>, { world: &'world World, inner: EntityHandleIter, - comps_pd: PhantomData, + comps_pd: PhantomData<(TermsT, &'query ())>, } -impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for Iter<'query, 'world, FieldTerms, EntityHandleIter> +impl<'query, 'world, TermsT, EntityHandleIter> Iterator + for Iter<'query, 'world, TermsT, EntityHandleIter> where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator>, - 'world: 'query, + TermsT: TermTuple<'world>, + EntityHandleIter: Iterator>, { - type Item = FieldTerms::Fields<'query>; + type Item = TermsT::Fields; fn next(&mut self) -> Option { let entity_handle = self.inner.next()?; - Some(FieldTerms::get_fields(&entity_handle, self.world)) + Some(TermsT::get_fields(&entity_handle, self.world)) } } -pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> +pub struct ComponentAndEuidIter<'query, 'world, TermsT, EntityHandleIter> where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator>, + TermsT: TermTuple<'world>, + EntityHandleIter: Iterator>, { world: &'world World, iter: EntityHandleIter, - comps_pd: PhantomData, + comps_pd: PhantomData<(TermsT, &'query ())>, } -impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator - for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> +impl<'query, 'world, TermsT, EntityHandleIter> Iterator + for ComponentAndEuidIter<'query, 'world, TermsT, EntityHandleIter> where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator>, - 'world: 'query, + TermsT: TermTuple<'world>, + EntityHandleIter: Iterator>, { - type Item = (Uid, FieldTerms::Fields<'query>); + type Item = (Uid, TermsT::Fields); fn next(&mut self) -> Option { @@ -469,16 +482,50 @@ where Some(( entity_handle.uid(), - FieldTerms::get_fields(&entity_handle, self.world), + TermsT::get_fields(&entity_handle, self.world), )) } } -macro_rules! impl_term_sequence { +pub trait TermFieldSource<'world, FieldAcc> +{ + type NewFieldAcc; + + fn collect_field( + entity_handle: &EntityHandle<'world>, + world: &'world World, + fields: FieldAcc, + ) -> Self::NewFieldAcc; +} + +macro_rules! term_field_source_new_field_acc { + (overflow) => { + () + }; + + ($index: tt) => { + paste! { + []::NewFieldAcc + } + }; +} + +macro_rules! gen_term_tuple_impls { ($c: tt) => { - seq!(I in 0..=$c { - impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*) + seq!(I in 0..$c { + impl<'world, #(Term~I,)*> TermTuple<'world> for (#(Term~I,)*) + where + #( + Term~I: Term<'world> + TermFieldSource< + 'world, + sub!(I - 1, term_field_source_new_field_acc), + NewFieldAcc: Tuple + >, + )* { + type Fields = sub!($c - 1, term_field_source_new_field_acc); + + #[allow(unused)] fn apply_terms_to_builder( terms_builder: &mut TermsBuilder ) @@ -487,60 +534,27 @@ macro_rules! impl_term_sequence { Term~I::apply_to_terms_builder(terms_builder); )* } - } - - impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*) - { - type Fields<'component> = (#(Term~I::Field<'component>,)*); - fn apply_terms_to_builder( - terms_builder: &mut TermsBuilder - ) + #[allow(unused)] + fn get_fields( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Fields { + let fields = (); + #( - Term~I::apply_to_terms_builder(terms_builder); + let fields = Term~I::collect_field(entity_handle, world, fields); )* - } - fn get_fields<'component>( - entity_handle: &EntityHandle<'component>, - world: &'component World, - ) -> Self::Fields<'component> - { - (#(Term~I::get_field(entity_handle, world),)*) + fields } } }); }; } -seq!(C in 0..=16 { - impl_term_sequence!(C); +seq!(C in 0..17 { + gen_term_tuple_impls!(C); }); -impl TermWithoutFieldTuple for () -{ - fn apply_terms_to_builder( - _terms_builder: &mut TermsBuilder, - ) - { - } -} - -impl TermWithFieldTuple for () -{ - type Fields<'component> = (); - - fn apply_terms_to_builder( - _terms_builder: &mut TermsBuilder, - ) - { - } - - fn get_fields<'component>( - _entity_handle: &EntityHandle<'_>, - _world: &'component World, - ) -> Self::Fields<'component> - { - } -} diff --git a/engine-ecs/src/query/flexible.rs b/engine-ecs/src/query/flexible.rs index 66d62c6..fd1e30e 100644 --- a/engine-ecs/src/query/flexible.rs +++ b/engine-ecs/src/query/flexible.rs @@ -19,7 +19,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> { /// Iterates over the entities matching this query. #[must_use] - pub fn iter(&self) -> Iter<'_> + pub fn iter(&self) -> Iter<'_, 'world> { Iter { iter: self @@ -52,10 +52,10 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> } } -impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT> +impl<'query, 'world, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'world, MAX_TERM_CNT> { - type IntoIter = Iter<'query>; - type Item = EntityHandle<'query>; + type IntoIter = Iter<'query, 'world>; + type Item = EntityHandle<'world>; fn into_iter(self) -> Self::IntoIter { @@ -63,15 +63,15 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T } } -pub struct Iter<'query> +pub struct Iter<'query, 'world> { - iter: QueryEntityIter<'query>, - world: &'query World, + iter: QueryEntityIter<'query, 'world>, + world: &'world World, } -impl<'query> Iterator for Iter<'query> +impl<'query, 'world> Iterator for Iter<'query, 'world> { - type Item = EntityHandle<'query>; + type Item = EntityHandle<'world>; fn next(&mut self) -> Option { @@ -85,8 +85,8 @@ type ComponentIterMapFnOutput<'a> = Zip, EntityIter<'a>>; type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>; -type QueryEntityIter<'query> = FlatMap< - ArchetypeRefIter<'query, 'query>, - ComponentIterMapFnOutput<'query>, +type QueryEntityIter<'query, 'world> = FlatMap< + ArchetypeRefIter<'world, 'query>, + ComponentIterMapFnOutput<'world>, ComponentIterMapFn, >; diff --git a/engine-ecs/src/query/term.rs b/engine-ecs/src/query/term.rs index 7c4503a..04bca1d 100644 --- a/engine-ecs/src/query/term.rs +++ b/engine-ecs/src/query/term.rs @@ -1,17 +1,19 @@ use std::any::type_name; use std::marker::PhantomData; +use crate::World; use crate::component::{ Component, Handle as ComponentHandle, HandleMut as ComponentHandleMut, }; +use crate::entity::Handle as EntityHandle; use crate::query::{ - TermWithField, - TermWithoutField, + Term, TermsBuilder, TermsBuilderInterface, }; +use crate::tuple::Tuple; use crate::uid::With as WithUid; pub struct With @@ -21,16 +23,27 @@ where _pd: PhantomData, } -impl TermWithoutField for With +impl<'world, WithUidT> Term<'world> for With where WithUidT: WithUid, { + type AddField = Fields; + fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, ) { terms_builder.present([WithUidT::uid()]); } + + fn add_field( + _entity_handle: &EntityHandle<'world>, + _world: &'world World, + fields: Fields, + ) -> Self::AddField + { + fields + } } pub struct Without @@ -40,21 +53,33 @@ where _pd: PhantomData, } -impl TermWithoutField for Without +impl<'world, WithUidT> Term<'world> for Without where WithUidT: WithUid, { + type AddField = Fields; + fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, ) { terms_builder.absent([WithUidT::uid()]); } + + fn add_field( + _entity_handle: &EntityHandle<'world>, + _world: &'world World, + fields: Fields, + ) -> Self::AddField + { + fields + } } -impl TermWithField for Option<&ComponentT> +impl<'world, ComponentT: Component> Term<'world> for Option<&ComponentT> { - type Field<'a> = Option>; + type AddField = + Fields::WithElementAtEnd>>; fn apply_to_terms_builder( _terms_builder: &mut TermsBuilder, @@ -62,30 +87,36 @@ impl TermWithField for Option<&ComponentT> { } - fn get_field<'world>( - entity_handle: &crate::entity::Handle<'world>, - _world: &'world crate::World, - ) -> Self::Field<'world> + fn add_field( + entity_handle: &EntityHandle<'world>, + _world: &'world World, + fields: Fields, + ) -> Self::AddField { - Some( - ComponentHandle::<'world, ComponentT>::from_entity_component_ref( - &entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::() - ); - }), - ) + let component = (|| { + let comp_ref = &entity_handle + .get_matching_components(ComponentT::id()) + .next()?; + + match ComponentHandle::::from_entity_component_ref(comp_ref) { + Ok(component) => Some(component), + Err(err) => { + panic!( + "Creating handle to component {} failed: {err}", + type_name::() + ); + } + } + })(); + + fields.with_elem(component) } } -impl TermWithField for Option<&mut ComponentT> +impl<'world, ComponentT: Component> Term<'world> for Option<&mut ComponentT> { - type Field<'a> = Option>; + type AddField = + Fields::WithElementAtEnd>>; fn apply_to_terms_builder( _terms_builder: &mut TermsBuilder, @@ -93,24 +124,28 @@ impl TermWithField for Option<&mut ComponentT> { } - fn get_field<'world>( - entity_handle: &crate::entity::Handle<'world>, - world: &'world crate::World, - ) -> Self::Field<'world> + fn add_field( + entity_handle: &EntityHandle<'world>, + world: &'world World, + fields: Fields, + ) -> Self::AddField { - Some( - ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref( - &entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - world, - ) - .unwrap_or_else(|err| { - panic!( - "Creating handle to component {} failed: {err}", - type_name::() - ); - }), - ) + let component = (|| { + let comp_ref = &entity_handle + .get_matching_components(ComponentT::id()) + .next()?; + + match ComponentHandleMut::::from_entity_component_ref(comp_ref, world) { + Ok(component) => Some(component), + Err(err) => { + panic!( + "Creating mut handle to component {} failed: {err}", + type_name::() + ); + } + } + })(); + + fields.with_elem(component) } } diff --git a/engine-ecs/src/uid.rs b/engine-ecs/src/uid.rs index 26fbaee..c148dbc 100644 --- a/engine-ecs/src/uid.rs +++ b/engine-ecs/src/uid.rs @@ -166,7 +166,7 @@ pub struct PairParams pub target: Uid, } -pub trait With +pub trait With: 'static { fn uid() -> Uid; } diff --git a/engine-ecs/tests/query.rs b/engine-ecs/tests/query.rs index af47083..870ff42 100644 --- a/engine-ecs/tests/query.rs +++ b/engine-ecs/tests/query.rs @@ -2,11 +2,11 @@ use engine_ecs::component::Component; use engine_ecs::pair::{Pair, Wildcard}; use engine_ecs::query::term::Without; use engine_ecs::query::{ - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, + TermTuple as QueryTermTuple, + Query, }; use engine_ecs::uid::Uid; -use engine_ecs::{Component, Query, World}; +use engine_ecs::{World, Component}; use parking_lot::{Mutex, Once}; pub static SETUP: Once = Once::new(); @@ -47,12 +47,11 @@ fn setup() }); } -fn assert_query_finds_ents( - query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>, +fn assert_query_finds_ents<'world, QueryTerms>( + query: Query<'world, QueryTerms>, mut expected_ent_ids: Vec, ) where - QueryFieldTerms: QueryTermWithFieldTuple, - QueryFieldlessTerms: QueryTermWithoutFieldTuple, + QueryTerms: QueryTermTuple<'world>, { assert!( query.iter_with_euids().all(|(ent_id, _)| { @@ -96,7 +95,7 @@ fn query_archetype_exists_with_edges_to_next_archetypes() let ent_4_id = world.spawn((A, B, C, G, F)); assert_query_finds_ents( - world.query::<(&A, &B, &C), ()>(), + world.query::<(&A, &B, &C)>(), vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id], ); } @@ -114,7 +113,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype() let ent_2_id = world.spawn((A, B, F)); - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -130,7 +129,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev() let ent_2_id = world.spawn((A, B, C, D, F)); - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -146,7 +145,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype() let ent_2_id = world.spawn((A, B, F)); - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -162,7 +161,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev() let ent_2_id = world.spawn((A, B, C, D, E, F)); - assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -178,7 +177,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype() let ent_2_id = world.spawn((A, B, G)); - assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -194,7 +193,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev() let ent_2_id = world.spawn((A, B, C, D, E, F, G)); - assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]); + assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]); } #[test] @@ -211,7 +210,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp() let ent_2_id = world.spawn((A, B, G)); assert_query_finds_ents( - world.query::<(&A, Option<&E>, &G), ()>(), + world.query::<(&A, Option<&E>, &G)>(), vec![ent_1_id, ent_2_id], ); } @@ -232,7 +231,7 @@ fn query_archetype_nonexistant() world.spawn((A, B, C, G, F)); - assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]); + assert_query_finds_ents(world.query::<(&A, &E)>(), vec![ent_2_id, ent_3_id]); } #[test] @@ -250,7 +249,7 @@ fn query_archetype_nonexistant_and_opt_comp() world.spawn((A, B, C, G, F)); assert_query_finds_ents( - world.query::<(&A, &E, Option<&D>), ()>(), + world.query::<(&A, &E, Option<&D>)>(), vec![ent_2_id, ent_3_id], ); } @@ -273,7 +272,7 @@ fn query_without_comp_and_archetype_exists() let ent_3_id = world.spawn((A, B, C, G, F)); assert_query_finds_ents( - world.query::<(&A, &B, &C), (Without,)>(), + world.query::<(&A, &B, &C, Without)>(), vec![ent_1_id, ent_2_id, ent_3_id], ); } @@ -295,7 +294,7 @@ fn query_without_required_comp_and_archetype_exists() world.spawn((A, B, C, G)); world.spawn((A, B, C, G, F)); - assert_query_finds_ents(world.query::<(&A, &B), (Without,)>(), vec![]); + assert_query_finds_ents(world.query::<(&A, &B, Without)>(), vec![]); } #[test] @@ -317,7 +316,7 @@ fn query_without_comp_and_archetype_nonexistant() world.spawn((A, B, C, G, F, E)); assert_query_finds_ents( - world.query::<(&A, &E), (Without,)>(), + world.query::<(&A, &E, Without)>(), vec![ent_1_id, ent_2_id], ); } @@ -363,7 +362,7 @@ fn query_with_wildcard_target_pair() )); assert_query_finds_ents( - world.query::<(&B, Pair), ()>(), + world.query::<(&B, Pair)>(), vec![ent_2_id, ent_3_id, ent_4_id], ); } @@ -407,7 +406,7 @@ fn query_with_component_target_pair() )); assert_query_finds_ents( - world.query::<(&B, Pair), ()>(), + world.query::<(&B, Pair)>(), vec![ent_2_id, ent_3_id], ); } -- cgit v1.2.3-18-g5258