From ab2fa8a93de33ccb17ee1efd6f9b8858a8b79253 Mon Sep 17 00:00:00 2001 From: HampusM Date: Fri, 10 Jan 2025 15:09:33 +0100 Subject: refactor(ecs): add struct for querying using component metadata --- ecs/src/query/flexible.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 ecs/src/query/flexible.rs (limited to 'ecs/src/query/flexible.rs') diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs new file mode 100644 index 0000000..d081d31 --- /dev/null +++ b/ecs/src/query/flexible.rs @@ -0,0 +1,145 @@ +//! Low-level querying. +use std::iter::{repeat_n, Filter, Flatten, Map, RepeatN, Zip}; + +use crate::component::storage::{ + Archetype, + ArchetypeEntity, + ArchetypeRefIter, + EntityIter, + Storage as ComponentStorage, +}; +use crate::component::{ + Metadata as ComponentMetadata, + RefSequence as ComponentRefSequence, +}; +use crate::lock::ReadGuard; +use crate::query::options::Options; +use crate::query::ComponentIter; +use crate::uid::Uid; +use crate::util::Sortable; +use crate::{EntityComponent, World}; + +/// Low-level entity query structure. +#[derive(Debug)] +pub struct Query<'world, CompMetadata> +where + CompMetadata: Sortable + AsRef<[ComponentMetadata]>, +{ + component_storage: ReadGuard<'world, ComponentStorage>, + comp_metadata: CompMetadata, +} + +impl<'world, CompMetadata> Query<'world, CompMetadata> +where + CompMetadata: Sortable + AsRef<[ComponentMetadata]>, +{ + /// Iterates over the entities matching this query. + #[must_use] + pub fn iter<'query, OptionsT: Options>(&'query self) -> Iter<'query> + { + Iter { + iter: self + .component_storage + .iter_archetypes_with_comps(&self.comp_metadata) + .map( + (|archetype| { + repeat_n(archetype, archetype.entity_cnt()) + .zip(archetype.entities()) + }) as ComponentIterMapFn, + ) + .flatten() + .filter(|(_, entity)| OptionsT::entity_filter(entity.components())), + } + } + + pub(crate) fn new(world: &'world World, mut comp_metadata: CompMetadata) -> Self + { + comp_metadata.sort_by_key_b(|metadata| metadata.id); + + Self { + component_storage: world + .data + .component_storage + .read_nonblock() + .expect("Failed to acquire read-only component storage lock"), + comp_metadata, + } + } +} + +pub struct Iter<'query> +{ + iter: QueryEntityIter<'query>, +} + +impl<'query> Iter<'query> +{ + /// Converts this iterator into a [`ComponentIter`]. + /// + /// Note: The matching entities of this iterator should have all of the non-[`Option`] + /// components in `Comps`, otherwise iterating the [`ComponentIter`] will cause a + /// panic. + #[must_use] + #[inline] + pub fn into_component_iter<'world, Comps>( + self, + world: &'world World, + ) -> ComponentIter<'query, 'world, Comps> + where + Comps: ComponentRefSequence + 'world, + 'world: 'query, + { + ComponentIter::new(world, self) + } +} + +impl<'query> Iterator for Iter<'query> +{ + type Item = EntityHandle<'query>; + + fn next(&mut self) -> Option + { + let (archetype, entity) = self.iter.next()?; + + Some(EntityHandle { archetype, entity }) + } +} + +pub struct EntityHandle<'query> +{ + archetype: &'query Archetype, + entity: &'query ArchetypeEntity, +} + +impl<'query> EntityHandle<'query> +{ + /// Returns the [`Uid`] of this entity. + #[inline] + pub fn uid(&self) -> Uid + { + self.entity.uid() + } + + #[inline] + pub fn components(&self) -> &'query [EntityComponent] + { + self.entity.components() + } + + #[inline] + pub fn get_component_index(&self, component_uid: Uid) -> Option + { + self.archetype.get_index_for_component(component_uid) + } +} + +type ComponentIterMapFn = + for<'a> fn(&'a Archetype) -> Zip, EntityIter<'a>>; + +type ComponentIterFilterFn = + for<'a, 'b> fn(&'a (&'b Archetype, &'b ArchetypeEntity)) -> bool; + +type QueryEntityIter<'query> = Filter< + Flatten, ComponentIterMapFn>>, + ComponentIterFilterFn, +>; -- cgit v1.2.3-18-g5258