summaryrefslogtreecommitdiff
path: root/engine-ecs/src/query
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/query')
-rw-r--r--engine-ecs/src/query/flexible.rs102
-rw-r--r--engine-ecs/src/query/term.rs271
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 {}
+}