diff options
Diffstat (limited to 'engine-ecs/src/query')
| -rw-r--r-- | engine-ecs/src/query/flexible.rs | 102 | ||||
| -rw-r--r-- | engine-ecs/src/query/term.rs | 271 |
2 files changed, 373 insertions, 0 deletions
diff --git a/engine-ecs/src/query/flexible.rs b/engine-ecs/src/query/flexible.rs new file mode 100644 index 0000000..06b0e79 --- /dev/null +++ b/engine-ecs/src/query/flexible.rs @@ -0,0 +1,102 @@ +//! Low-level querying. +use crate::component::storage::archetype::{Archetype, EntityIter}; +use crate::component::storage::{ArchetypeRefIter, TraversalResult}; +use crate::entity::Handle as EntityHandle; +use crate::query::Terms; +use crate::util::array_vec::ArrayVec; +use crate::World; + +/// Low-level entity query structure. +#[derive(Debug)] +pub struct Query<'world, const TERM_CAP: usize> +{ + world: &'world World, + terms: Terms<TERM_CAP>, +} + +impl<'world, const TERM_CAP: usize> Query<'world, TERM_CAP> +{ + /// Iterates over the entities matching this query. + #[must_use] + pub fn iter<'this>(&'this self) -> Iter<'this, TERM_CAP> + where + 'world: 'this, + { + Iter { + inner: self + .world + .data + .component_storage + .search_archetypes(&self.terms), + current: None, + world: self.world, + } + } + + #[must_use] + pub fn world(&self) -> &'world World + { + self.world + } + + pub(crate) fn new(world: &'world World, terms: Terms<TERM_CAP>) -> Self + { + Self { world, terms } + } +} + +impl<'query, 'world, const TERM_CAP: usize> IntoIterator + for &'query Query<'world, TERM_CAP> +where + 'world: 'query, +{ + type IntoIter = Iter<'query, TERM_CAP>; + type Item = EntityHandle<'query>; + + fn into_iter(self) -> Self::IntoIter + { + self.iter() + } +} + +pub struct Iter<'query, const TERM_CAP: usize> +{ + inner: ArchetypeRefIter<'query, 'query, TERM_CAP>, + current: Option<( + &'query Archetype, + ArrayVec<TraversalResult, TERM_CAP>, + EntityIter<'query>, + )>, + world: &'query World, +} + +impl<const TERM_CAP: usize> Iter<'_, TERM_CAP> +{ + #[must_use] + pub fn traversal_results(&self) -> Option<&[TraversalResult]> + { + self.current + .as_ref() + .map(|(_, traversal_results, _)| traversal_results.as_ref()) + } +} + +impl<'query, const TERM_CAP: usize> Iterator for Iter<'query, TERM_CAP> +{ + type Item = EntityHandle<'query>; + + fn next(&mut self) -> Option<Self::Item> + { + if let Some((archetype, _, entity_iter)) = self.current.as_mut() { + if let Some(entity) = entity_iter.next() { + return Some(EntityHandle::new(archetype, entity, self.world)); + } + } + + let (archetype, traversal_results) = self.inner.next()?; + + self.current = Some((archetype, traversal_results, archetype.entities())); + + self.next() + } +} diff --git a/engine-ecs/src/query/term.rs b/engine-ecs/src/query/term.rs new file mode 100644 index 0000000..980ee87 --- /dev/null +++ b/engine-ecs/src/query/term.rs @@ -0,0 +1,271 @@ +use std::any::type_name; +use std::marker::PhantomData; + +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, +}; +use crate::query::{ + FoundByTraversal, + SearchResult, + Term, + TermMetadata, + TermTuple, + TermsBuilder, + TermsBuilderInterface, + Traversal, + TraversalKind, +}; +use crate::uid::With as WithUid; +use crate::World; + +pub struct With<WithUidT> +where + WithUidT: WithUid, +{ + _pd: PhantomData<WithUidT>, +} + +impl<'query, WithUidT> Term<'query> for With<WithUidT> +where + WithUidT: WithUid, +{ + type Fields = (); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + _term_metadata: TermMetadata, + ) + { + terms_builder.present([WithUidT::uid()]); + } + + fn fields( + _world: &'query World, + _search_result: &SearchResult<'query, '_>, + _term_metadata: TermMetadata, + ) -> Self::Fields + { + } +} + +pub struct Without<WithUidT> +where + WithUidT: WithUid, +{ + _pd: PhantomData<WithUidT>, +} + +impl<'query, WithUidT> Term<'query> for Without<WithUidT> +where + WithUidT: WithUid, +{ + type Fields = (); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + _term_metadata: TermMetadata, + ) + { + terms_builder.absent([WithUidT::uid()]); + } + + fn fields( + _world: &'query World, + _search_result: &SearchResult<'query, '_>, + _term_metadata: TermMetadata, + ) -> Self::Fields + { + } +} + +impl<'query, ComponentT: Component> Term<'query> for Option<&ComponentT> +{ + type Fields = (Option<ComponentHandle<'query, ComponentT>>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + _term_metadata: TermMetadata, + ) + { + } + + fn fields( + _world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: TermMetadata, + ) -> Self::Fields + { + let component = (|| { + let comp_ref = &search_result + .entity_handle + .get_matching_components(ComponentT::id()) + .next()?; + + match ComponentHandle::<ComponentT>::from_entity_component_ref(comp_ref) { + Ok(component) => Some(component), + Err(err) => { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + } + } + })(); + + (component,) + } +} + +impl<'query, ComponentT: Component> Term<'query> for Option<&mut ComponentT> +{ + type Fields = (Option<ComponentHandleMut<'query, ComponentT>>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + _term_metadata: TermMetadata, + ) + { + } + + fn fields( + world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: TermMetadata, + ) -> Self::Fields + { + let component = (|| { + let comp_ref = &search_result + .entity_handle + .get_matching_components(ComponentT::id()) + .next()?; + + match ComponentHandleMut::<ComponentT>::from_entity_component_ref( + comp_ref, world, + ) { + Ok(component) => Some(component), + Err(err) => { + panic!( + "Creating mut handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + } + } + })(); + + (component,) + } +} + +pub struct Traverse<TermsT, Kind, Relation> +{ + _pd: PhantomData<(TermsT, Kind, Relation)>, +} + +impl<'query, TermsT, Kind, Relation> Term<'query> for Traverse<TermsT, Kind, Relation> +where + TermsT: TermTuple<'query>, + Kind: TraversalKindType, + Relation: Component, +{ + type Fields = TermsT::Fields; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + term_metadata: TermMetadata, + ) + { + terms_builder.traverse([Traversal { + kind: Kind::traversal_kind(), + relation: Relation::id(), + terms: |traversal| { + let mut terms_builder = TermsBuilder::default(); + + TermsT::apply_terms_to_builder( + &mut terms_builder, + traversal + .term_metadata + .level + .checked_add(1) + .expect("Too large term level. Would overflow"), + ); + + terms_builder.build() + }, + term_metadata, + }]); + } + + fn fields( + world: &'query World, + search_result: &SearchResult<'query, '_>, + term_metadata: TermMetadata, + ) -> Self::Fields + { + let Some(traversal_result) = search_result + .traversal_results + .iter() + .find(|traversal_result| traversal_result.term_metadata == term_metadata) + else { + unreachable!(); + }; + + let ent = match traversal_result.found { + FoundByTraversal::SelfEntity => search_result.entity_handle.clone(), + FoundByTraversal::OtherEntity(found_ent_id) => { + let Some(found_ent) = world.get_entity(found_ent_id) else { + unreachable!(); + }; + + found_ent + } + }; + + TermsT::get_fields( + world, + &SearchResult { + entity_handle: ent, + traversal_results: search_result.traversal_results, + }, + term_metadata + .level + .checked_add(1) + .expect("Too large term level. Would overflow"), + ) + } +} + +pub trait TraversalKindType: sealed::Sealed +{ + fn traversal_kind() -> TraversalKind; +} + +pub struct TraverseUp; + +impl TraversalKindType for TraverseUp +{ + fn traversal_kind() -> TraversalKind + { + TraversalKind::Up + } +} + +impl sealed::Sealed for TraverseUp {} + +pub struct TraverseSelfOrUp; + +impl TraversalKindType for TraverseSelfOrUp +{ + fn traversal_kind() -> TraversalKind + { + TraversalKind::SelfOrUp + } +} + +impl sealed::Sealed for TraverseSelfOrUp {} + +mod sealed +{ + pub trait Sealed {} +} |
