summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2025-03-21 20:05:53 +0100
committerHampusM <hampus@hampusmat.com>2025-03-22 15:16:01 +0100
commitfe62665b1d62d36ee0839e6bf24e3841ea667da9 (patch)
tree0533941d2cbbe5bf0a1995a33f05bca37da949fd
parent76e7e612e7b516bf52b508ae5bb367b1ddc3babc (diff)
refactor(ecs): replace query options with fieldless terms
-rw-r--r--ecs/src/component/storage.rs96
-rw-r--r--ecs/src/component/storage/graph.rs11
-rw-r--r--ecs/src/lib.rs17
-rw-r--r--ecs/src/query.rs279
-rw-r--r--ecs/src/query/flexible.rs44
-rw-r--r--ecs/src/query/options.rs59
-rw-r--r--ecs/src/query/term.rs38
-rw-r--r--ecs/tests/query.rs139
8 files changed, 428 insertions, 255 deletions
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs
index 21f9a37..4b2b6d8 100644
--- a/ecs/src/component/storage.rs
+++ b/ecs/src/component/storage.rs
@@ -26,6 +26,26 @@ pub mod archetype;
mod graph;
+#[derive(Debug)]
+pub struct ArchetypeSearchTerms<'a>
+{
+ pub required_components: &'a [Uid],
+ pub excluded_components: &'a [Uid],
+}
+
+impl ArchetypeSearchTerms<'_>
+{
+ fn excluded_contains(&self, uid: Uid) -> bool
+ {
+ self.excluded_components.binary_search(&uid).is_ok()
+ }
+
+ fn required_contains(&self, uid: Uid) -> bool
+ {
+ self.required_components.binary_search(&uid).is_ok()
+ }
+}
+
#[derive(Debug, Default)]
pub struct Storage
{
@@ -36,9 +56,25 @@ pub struct Storage
impl Storage
{
- pub fn search_archetypes(&self, component_ids: &[Uid]) -> ArchetypeRefIter<'_>
+ pub fn search_archetypes<'search_terms>(
+ &self,
+ search_terms: ArchetypeSearchTerms<'search_terms>,
+ ) -> ArchetypeRefIter<'_, 'search_terms>
{
- let archetype_id = ArchetypeId::new(&component_ids);
+ let archetype_id = ArchetypeId::new(&search_terms.required_components);
+
+ if search_terms
+ .excluded_components
+ .iter()
+ .any(|excluded_comp_id| search_terms.required_contains(*excluded_comp_id))
+ {
+ return ArchetypeRefIter {
+ storage: self,
+ pre_iter: Either::B(Vec::new().into_iter()),
+ dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &[]),
+ search_terms,
+ };
+ }
let Some(add_edge_recursive_iter) =
self.graph.dfs_archetype_add_edges(archetype_id)
@@ -47,15 +83,16 @@ impl Storage
.borrow_mut()
.push(ImaginaryArchetype {
id: archetype_id,
- component_ids: component_ids.to_vec(),
+ component_ids: search_terms.required_components.to_vec(),
});
- let found_archetypes = self.find_all_archetype_with_comps(component_ids);
+ let found_archetypes = self.find_all_archetype_with_comps(&search_terms);
return ArchetypeRefIter {
storage: self,
pre_iter: Either::B(found_archetypes.clone().into_iter()),
dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &found_archetypes),
+ search_terms,
};
};
@@ -63,6 +100,7 @@ impl Storage
storage: self,
pre_iter: Either::A([archetype_id].into_iter()),
dfs_iter: add_edge_recursive_iter,
+ search_terms,
}
}
@@ -321,7 +359,10 @@ impl Storage
}
}
- fn find_all_archetype_with_comps(&self, component_ids: &[Uid]) -> Vec<ArchetypeId>
+ fn find_all_archetype_with_comps(
+ &self,
+ search_terms: &ArchetypeSearchTerms<'_>,
+ ) -> Vec<ArchetypeId>
{
let Some(mut search_iter) =
self.graph.dfs_archetype_add_edges(ArchetypeId::new(&[]))
@@ -333,20 +374,30 @@ impl Storage
let mut found = Vec::<ArchetypeId>::new();
while let Some(node_id) = search_iter.streaming_next() {
- let ArchetypeAddEdgeDfsIterResult::AddEdge(node_id) = node_id else {
+ let ArchetypeAddEdgeDfsIterResult::AddEdge {
+ add_edge_archetype_id: node_id,
+ add_edge_component_id,
+ } = node_id
+ else {
continue;
};
+ if search_terms.excluded_contains(add_edge_component_id) {
+ search_iter.pop();
+ continue;
+ }
+
let node = self
.graph
.get_node_by_id(node_id)
.expect("Graph node found through DFS doesn't exist");
- if node.archetype().component_cnt() < component_ids.len() {
+ if node.archetype().component_cnt() < search_terms.required_components.len() {
continue;
}
- if !component_ids
+ if !search_terms
+ .required_components
.iter()
.all(|comp_id| node.archetype().has_component_with_id(*comp_id))
{
@@ -371,14 +422,16 @@ impl TypeName for Storage
}
#[derive(Debug)]
-pub struct ArchetypeRefIter<'storage>
+pub struct ArchetypeRefIter<'storage, 'search_terms>
{
storage: &'storage Storage,
pre_iter: Either<ArrayIter<ArchetypeId, 1>, VecIntoIter<ArchetypeId>>,
dfs_iter: ArchetypeAddEdgeDfsIter<'storage>,
+ search_terms: ArchetypeSearchTerms<'search_terms>,
}
-impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage>
+impl<'component_storage, 'search_terms> Iterator
+ for ArchetypeRefIter<'component_storage, 'search_terms>
{
type Item = &'component_storage Archetype;
@@ -400,14 +453,26 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage>
| ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { .. }
)
})? {
- ArchetypeAddEdgeDfsIterResult::AddEdge(add_edge_archetype_id) => {
- break add_edge_archetype_id
+ ArchetypeAddEdgeDfsIterResult::AddEdge {
+ add_edge_archetype_id,
+ add_edge_component_id,
+ } => {
+ if self.search_terms.excluded_contains(add_edge_component_id) {
+ self.dfs_iter.pop();
+ continue;
+ }
+
+ break add_edge_archetype_id;
}
ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound {
archetype,
add_edge_archetype_id,
add_edge_component_id,
} => {
+ if self.search_terms.excluded_contains(add_edge_component_id) {
+ continue;
+ }
+
let mut add_edge_archetype_comps =
archetype.component_ids_sorted().collect::<Vec<_>>();
@@ -453,7 +518,7 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage>
}
}
-impl ArchetypeRefIter<'_>
+impl ArchetypeRefIter<'_, '_>
{
fn find_edges_of_imaginary_archetype(
&self,
@@ -461,7 +526,10 @@ impl ArchetypeRefIter<'_>
) -> Vec<(Uid, ArchetypeEdges)>
{
self.storage
- .find_all_archetype_with_comps(imaginary_archetype_comps)
+ .find_all_archetype_with_comps(&ArchetypeSearchTerms {
+ required_components: imaginary_archetype_comps,
+ excluded_components: &[],
+ })
.into_iter()
.filter_map(|found_id| {
let found_archetype = self.storage.get_archetype_by_id(found_id).unwrap();
diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs
index feac16c..e1f48f7 100644
--- a/ecs/src/component/storage/graph.rs
+++ b/ecs/src/component/storage/graph.rs
@@ -381,14 +381,21 @@ impl<'graph> StreamingIterator for ArchetypeAddEdgeDfsIter<'graph>
.into_iter(),
));
- Some(ArchetypeAddEdgeDfsIterResult::AddEdge(add_edge))
+ Some(ArchetypeAddEdgeDfsIterResult::AddEdge {
+ add_edge_archetype_id: add_edge,
+ add_edge_component_id: component_id,
+ })
}
}
#[derive(Debug)]
pub enum ArchetypeAddEdgeDfsIterResult<'graph, 'iter>
{
- AddEdge(ArchetypeId),
+ AddEdge
+ {
+ add_edge_archetype_id: ArchetypeId,
+ add_edge_component_id: Uid,
+ },
NoEdgesLeftForArchetype,
NoAddEdge,
AddEdgeAlreadyVisited,
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
index cb2059b..9b787a2 100644
--- a/ecs/src/lib.rs
+++ b/ecs/src/lib.rs
@@ -18,11 +18,13 @@ use crate::extension::{Collector as ExtensionCollector, Extension};
use crate::lock::{Lock, WriteGuard};
use crate::phase::{Phase, START as START_PHASE};
use crate::query::flexible::Query as FlexibleQuery;
-use crate::query::options::{Not, Options as QueryOptions, With};
+use crate::query::term::Without;
use crate::query::{
ComponentIter,
- TermSequence as QueryTermSequence,
+ TermWithFieldTuple as QueryTermWithFieldTuple,
+ TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
Terms as QueryTerms,
+ TermsBuilderInterface,
};
use crate::relationship::{ChildOf, DependsOn, Relationship};
use crate::sole::Sole;
@@ -173,10 +175,10 @@ impl World
extension.collect(extension_collector);
}
- pub fn query<Terms, OptionsT>(&self) -> Query<Terms, OptionsT>
+ pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms>
where
- Terms: QueryTermSequence,
- OptionsT: QueryOptions,
+ FieldTerms: QueryTermWithFieldTuple,
+ FieldlessTerms: QueryTermWithoutFieldTuple,
{
Query::new(self)
}
@@ -283,7 +285,7 @@ impl World
fn perform_phases(&self)
{
let phase_query =
- self.query::<(&Phase,), Not<With<Relationship<ChildOf, Phase>>>>();
+ self.query::<(&Phase,), (Without<Relationship<ChildOf, Phase>>,)>();
for (phase_euid, (_,)) in phase_query.iter_with_euids() {
if phase_euid == *START_PHASE {
@@ -492,8 +494,7 @@ impl World
.build(),
);
- for (system,) in
- ComponentIter::<(&SystemComponent,), _>::new(self, query.iter::<()>())
+ for (system,) in ComponentIter::<(&SystemComponent,), _>::new(self, query.iter())
{
unsafe {
system.system.run(self);
diff --git a/ecs/src/query.rs b/ecs/src/query.rs
index 2fcd2cc..1889e00 100644
--- a/ecs/src/query.rs
+++ b/ecs/src/query.rs
@@ -10,42 +10,42 @@ use crate::query::flexible::{
Iter as FlexibleQueryIter,
Query as FlexibleQuery,
};
-use crate::query::options::Options;
use crate::system::{Param as SystemParam, System};
use crate::uid::Uid;
use crate::util::VecExt;
-use crate::{EntityComponent, World};
+use crate::World;
pub mod flexible;
-pub mod options;
+pub mod term;
#[derive(Debug)]
-pub struct Query<'world, Terms, OptionsT = ()>
+pub struct Query<'world, FieldTerms, FieldlessTerms = ()>
where
- Terms: TermSequence,
+ FieldTerms: TermWithFieldTuple,
+ FieldlessTerms: TermWithoutFieldTuple,
{
world: &'world World,
inner: FlexibleQuery<'world, 'static>,
- _pd: PhantomData<(Terms, OptionsT)>,
+ _pd: PhantomData<(FieldTerms, FieldlessTerms)>,
}
-impl<'world, Terms, OptionsT> Query<'world, Terms, OptionsT>
+impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms>
where
- Terms: TermSequence,
- OptionsT: Options,
+ FieldTerms: TermWithFieldTuple,
+ FieldlessTerms: TermWithoutFieldTuple,
{
/// Iterates over the entities matching this query, the iterator item being the entity
/// components.
#[must_use]
pub fn iter<'query>(
&'query self,
- ) -> ComponentIter<'query, 'world, Terms, FlexibleQueryIter<'query>>
+ ) -> ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
{
- tracing::trace!("Searching for {}", std::any::type_name::<Terms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
ComponentIter {
world: self.world,
- iter: self.inner.iter::<OptionsT>(),
+ iter: self.inner.iter(),
comps_pd: PhantomData,
}
}
@@ -55,13 +55,13 @@ where
#[must_use]
pub fn iter_with_euids<'query>(
&'query self,
- ) -> ComponentAndEuidIter<'query, 'world, Terms, FlexibleQueryIter<'query>>
+ ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
{
- tracing::trace!("Searching for {}", std::any::type_name::<Terms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
ComponentAndEuidIter {
world: self.world,
- iter: self.inner.iter::<OptionsT>(),
+ iter: self.inner.iter(),
comps_pd: PhantomData,
}
}
@@ -75,15 +75,15 @@ where
pub fn iter_with<'query, OutIter>(
&'query self,
func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter,
- ) -> ComponentIter<'query, 'world, Terms, OutIter>
+ ) -> ComponentIter<'query, 'world, FieldTerms, OutIter>
where
OutIter: Iterator<Item = EntityHandle<'query>>,
{
- tracing::trace!("Searching for {}", std::any::type_name::<Terms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
ComponentIter {
world: self.world,
- iter: func(self.inner.iter::<OptionsT>()),
+ iter: func(self.inner.iter()),
comps_pd: PhantomData,
}
}
@@ -92,27 +92,32 @@ where
#[must_use]
pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid>
{
- Some(self.inner.iter::<OptionsT>().nth(entity_index)?.uid())
+ Some(self.inner.iter().nth(entity_index)?.uid())
}
pub(crate) fn new(world: &'world World) -> Self
{
+ let mut terms_builder = Terms::builder();
+
+ FieldTerms::apply_terms_to_builder(&mut terms_builder);
+ FieldlessTerms::apply_terms_to_builder(&mut terms_builder);
+
Self {
world,
- inner: world.flexible_query(Terms::build_terms()),
+ inner: world.flexible_query(terms_builder.build()),
_pd: PhantomData,
}
}
}
-impl<'query, 'world, Terms, OptionsT> IntoIterator
- for &'query Query<'world, Terms, OptionsT>
+impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator
+ for &'query Query<'world, FieldTerms, FieldlessTerms>
where
- Terms: TermSequence + 'world,
- OptionsT: Options,
+ FieldTerms: TermWithFieldTuple + 'world,
+ FieldlessTerms: TermWithoutFieldTuple,
{
- type IntoIter = ComponentIter<'query, 'world, Terms, FlexibleQueryIter<'query>>;
- type Item = Terms::Handles<'query>;
+ type IntoIter = ComponentIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>;
+ type Item = FieldTerms::Fields<'query>;
fn into_iter(self) -> Self::IntoIter
{
@@ -120,10 +125,11 @@ where
}
}
-impl<'world, Terms, OptionsT> SystemParam<'world> for Query<'world, Terms, OptionsT>
+impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world>
+ for Query<'world, FieldTerms, FieldlessTerms>
where
- Terms: TermSequence,
- OptionsT: Options,
+ FieldTerms: TermWithFieldTuple,
+ FieldlessTerms: TermWithoutFieldTuple,
{
type Input = ();
@@ -147,7 +153,7 @@ where
pub struct Terms<'a>
{
required_components: Cow<'a, [Uid]>,
- //optional_components: Cow<'a, [Uid]>,
+ excluded_components: Cow<'a, [Uid]>,
}
impl<'a> Terms<'a>
@@ -162,18 +168,35 @@ impl<'a> Terms<'a>
pub struct TermsBuilder<'a>
{
required_components: Cow<'a, [Uid]>,
- //optional_components: Cow<'a, [Uid]>,
+ excluded_components: Cow<'a, [Uid]>,
}
-impl<'a> TermsBuilder<'a>
+pub trait TermsBuilderInterface<'a>
{
- pub fn with<ComponentT: Component>(mut self) -> Self
+ fn with<ComponentT: Component>(self) -> Self;
+
+ fn without<ComponentT: Component>(self) -> Self;
+
+ fn with_required_ids(self, ids: &'a mut [Uid]) -> Self;
+}
+
+macro_rules! impl_terms_builder {
+ ($($impl_content: tt)*) => {
+ impl<'a> TermsBuilderInterface<'a> for TermsBuilder<'a> {
+ $($impl_content)*
+ }
+
+ impl<'a> TermsBuilderInterface<'a> for &mut TermsBuilder<'a> {
+ $($impl_content)*
+ }
+ };
+}
+
+impl_terms_builder! {
+ #[allow(unused_mut)]
+ fn with<ComponentT: Component>(mut self) -> Self
{
if ComponentT::is_optional() {
- //self.optional_components
- // .to_mut()
- // .insert_at_partition_point_by_key(ComponentT::id(), |id| *id);
-
return self;
}
@@ -181,12 +204,28 @@ impl<'a> TermsBuilder<'a>
.to_mut()
.insert_at_partition_point_by_key(ComponentT::id(), |id| *id);
- assert!(self.required_components.is_sorted());
+ self
+ }
+
+ #[allow(unused_mut)]
+ fn without<ComponentT: Component>(mut self) -> Self
+ {
+ if ComponentT::is_optional() {
+ panic!(
+ "{}::without cannot take optional component",
+ type_name::<Self>()
+ );
+ }
+
+ self.excluded_components
+ .to_mut()
+ .insert_at_partition_point_by_key(ComponentT::id(), |id| *id);
self
}
- pub fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self
+ #[allow(unused_mut)]
+ fn with_required_ids(mut self, ids: &'a mut [Uid]) -> Self
{
if ids.is_empty() {
return self;
@@ -215,47 +254,56 @@ impl<'a> TermsBuilder<'a>
self
}
+}
+impl<'a> TermsBuilder<'a>
+{
pub fn build(self) -> Terms<'a>
{
assert!(self.required_components.is_sorted());
- //assert!(self.optional_components.is_sorted());
+ assert!(self.excluded_components.is_sorted());
Terms {
required_components: self.required_components,
- //optional_components: self.optional_components,
+ excluded_components: self.excluded_components,
}
}
}
-pub trait Term
+pub trait TermWithoutField
+{
+ fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>);
+}
+
+pub trait TermWithField
{
- type Handle<'a>;
+ type Field<'a>;
- fn apply_to_terms_builder(terms_builder: TermsBuilder) -> TermsBuilder;
+ fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>);
- fn create_handle<'world>(
- get_component: &impl Fn(Uid) -> Option<&'world EntityComponent>,
+ fn get_field<'world>(
+ entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Handle<'world>;
+ ) -> Self::Field<'world>;
}
-impl<ComponentRefT: ComponentRef> Term for ComponentRefT
+impl<ComponentRefT: ComponentRef> TermWithField for ComponentRefT
{
- type Handle<'a> = ComponentRefT::Handle<'a>;
+ type Field<'a> = ComponentRefT::Handle<'a>;
- fn apply_to_terms_builder(terms_builder: TermsBuilder) -> TermsBuilder
+ fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>)
{
- terms_builder.with::<ComponentRefT::Component>()
+ terms_builder.with::<ComponentRefT::Component>();
}
- fn create_handle<'world>(
- get_component: &impl Fn(Uid) -> Option<&'world EntityComponent>,
+ fn get_field<'world>(
+ entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Handle<'world>
+ ) -> Self::Field<'world>
{
- Self::Handle::from_locked_optional_component(
- get_component(ComponentRefT::Component::id())
+ Self::Field::from_locked_optional_component(
+ entity_handle
+ .get_component(ComponentRefT::Component::id())
.map(|component| &component.component),
world,
)
@@ -268,32 +316,37 @@ impl<ComponentRefT: ComponentRef> Term for ComponentRefT
}
}
-/// A sequence of references (immutable or mutable) to components.
-pub trait TermSequence
+pub trait TermWithoutFieldTuple
{
- type Handles<'component>;
+ fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>);
+}
+
+pub trait TermWithFieldTuple
+{
+ type Fields<'component>;
- fn build_terms() -> Terms<'static>;
+ fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>);
- fn from_components<'component>(
- get_component: impl Fn(Uid) -> Option<&'component EntityComponent>,
+ fn get_fields<'component>(
+ entity_handle: &EntityHandle<'component>,
world: &'component World,
- ) -> Self::Handles<'component>;
+ ) -> Self::Fields<'component>;
}
-pub struct ComponentIter<'query, 'world, Terms, EntityHandleIter>
+pub struct ComponentIter<'query, 'world, FieldTerms, EntityHandleIter>
where
+ FieldTerms: TermWithFieldTuple + 'world,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
{
world: &'world World,
iter: EntityHandleIter,
- comps_pd: PhantomData<Terms>,
+ comps_pd: PhantomData<FieldTerms>,
}
-impl<'query, 'world, Terms, EntityHandleIter>
- ComponentIter<'query, 'world, Terms, EntityHandleIter>
+impl<'query, 'world, FieldTerms, EntityHandleIter>
+ ComponentIter<'query, 'world, FieldTerms, EntityHandleIter>
where
- Terms: TermSequence + 'world,
+ FieldTerms: TermWithFieldTuple + 'world,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
'world: 'query,
{
@@ -309,47 +362,41 @@ where
}
}
-impl<'query, 'world, Terms, EntityHandleIter> Iterator
- for ComponentIter<'query, 'world, Terms, EntityHandleIter>
+impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
+ for ComponentIter<'query, 'world, FieldTerms, EntityHandleIter>
where
- Terms: TermSequence + 'world,
+ FieldTerms: TermWithFieldTuple + 'world,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
'world: 'query,
{
- type Item = Terms::Handles<'query>;
+ type Item = FieldTerms::Fields<'query>;
fn next(&mut self) -> Option<Self::Item>
{
let entity_handle = self.iter.next()?;
- Some(Terms::from_components(
- |component_uid| {
- entity_handle
- .components()
- .get(entity_handle.get_component_index(component_uid)?)
- },
- self.world,
- ))
+ Some(FieldTerms::get_fields(&entity_handle, self.world))
}
}
-pub struct ComponentAndEuidIter<'query, 'world, Terms, EntityHandleIter>
+pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
where
+ FieldTerms: TermWithFieldTuple + 'world,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
{
world: &'world World,
iter: EntityHandleIter,
- comps_pd: PhantomData<Terms>,
+ comps_pd: PhantomData<FieldTerms>,
}
-impl<'query, 'world, Terms, EntityHandleIter> Iterator
- for ComponentAndEuidIter<'query, 'world, Terms, EntityHandleIter>
+impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
+ for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
where
- Terms: TermSequence + 'world,
+ FieldTerms: TermWithFieldTuple + 'world,
EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
'world: 'query,
{
- type Item = (Uid, Terms::Handles<'query>);
+ type Item = (Uid, FieldTerms::Fields<'query>);
fn next(&mut self) -> Option<Self::Item>
{
@@ -357,14 +404,7 @@ where
Some((
entity_handle.uid(),
- Terms::from_components(
- |component_uid| {
- entity_handle
- .components()
- .get(entity_handle.get_component_index(component_uid)?)
- },
- self.world,
- ),
+ FieldTerms::get_fields(&entity_handle, self.world),
))
}
}
@@ -372,28 +412,33 @@ where
macro_rules! impl_term_sequence {
($c: tt) => {
seq!(I in 0..=$c {
- impl<#(Term~I: Term,)*> TermSequence for (#(Term~I,)*)
+ impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*)
{
- type Handles<'component> = (#(Term~I::Handle<'component>,)*);
-
- fn build_terms() -> Terms<'static>
+ fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>)
{
- let mut term_builder = Terms::builder();
-
#(
- term_builder =
- Term~I::apply_to_terms_builder(term_builder);
+ Term~I::apply_to_terms_builder(terms_builder);
)*
+ }
+ }
+
+ impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*)
+ {
+ type Fields<'component> = (#(Term~I::Field<'component>,)*);
- term_builder.build()
+ fn apply_terms_to_builder(terms_builder: &mut TermsBuilder<'_>)
+ {
+ #(
+ Term~I::apply_to_terms_builder(terms_builder);
+ )*
}
- fn from_components<'component>(
- get_component: impl Fn(Uid) -> Option<&'component EntityComponent>,
+ fn get_fields<'component>(
+ entity_handle: &EntityHandle<'component>,
world: &'component World,
- ) -> Self::Handles<'component>
+ ) -> Self::Fields<'component>
{
- (#(Term~I::create_handle(&get_component, world),)*)
+ (#(Term~I::get_field(entity_handle, world),)*)
}
}
});
@@ -404,19 +449,21 @@ seq!(C in 0..=16 {
impl_term_sequence!(C);
});
-impl TermSequence for ()
+impl TermWithoutFieldTuple for ()
{
- type Handles<'component> = ();
+ fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {}
+}
- fn build_terms() -> Terms<'static>
- {
- Terms::builder().build()
- }
+impl TermWithFieldTuple for ()
+{
+ type Fields<'component> = ();
+
+ fn apply_terms_to_builder(_terms_builder: &mut TermsBuilder<'_>) {}
- fn from_components<'component>(
- _get_component: impl Fn(Uid) -> Option<&'component EntityComponent>,
+ fn get_fields<'component>(
+ _entity_handle: &EntityHandle<'_>,
_world: &'component World,
- ) -> Self::Handles<'component>
+ ) -> Self::Fields<'component>
{
}
}
diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs
index a3e8701..7d24dc9 100644
--- a/ecs/src/query/flexible.rs
+++ b/ecs/src/query/flexible.rs
@@ -1,10 +1,13 @@
//! Low-level querying.
-use std::iter::{repeat_n, Filter, FlatMap, RepeatN, Zip};
+use std::iter::{repeat_n, FlatMap, RepeatN, Zip};
use crate::component::storage::archetype::{Archetype, ArchetypeEntity, EntityIter};
-use crate::component::storage::{ArchetypeRefIter, Storage as ComponentStorage};
+use crate::component::storage::{
+ ArchetypeRefIter,
+ ArchetypeSearchTerms,
+ Storage as ComponentStorage,
+};
use crate::lock::ReadGuard;
-use crate::query::options::Options;
use crate::query::Terms;
use crate::uid::Uid;
use crate::{EntityComponent, World};
@@ -21,19 +24,21 @@ impl<'world, 'terms> Query<'world, 'terms>
{
/// Iterates over the entities matching this query.
#[must_use]
- pub fn iter<OptionsT: Options>(&self) -> Iter<'_>
+ pub fn iter(&self) -> Iter<'_>
{
Iter {
iter: self
.component_storage
- .search_archetypes(self.terms.required_components.as_ref())
+ .search_archetypes(ArchetypeSearchTerms {
+ required_components: &self.terms.required_components,
+ excluded_components: &self.terms.excluded_components,
+ })
.flat_map(
(|archetype| {
repeat_n(archetype, archetype.entity_cnt())
.zip(archetype.entities())
}) as ComponentIterMapFn,
- )
- .filter(|(_, entity)| OptionsT::entity_filter(&entity.components)),
+ ),
}
}
@@ -85,16 +90,11 @@ impl<'query> EntityHandle<'query>
#[inline]
#[must_use]
- pub fn components(&self) -> &'query [EntityComponent]
+ pub fn get_component(&self, component_uid: Uid) -> Option<&'query EntityComponent>
{
- &self.entity.components
- }
+ let index = self.archetype.get_index_for_component(component_uid)?;
- #[inline]
- #[must_use]
- pub fn get_component_index(&self, component_uid: Uid) -> Option<usize>
- {
- self.archetype.get_index_for_component(component_uid)
+ Some(self.entity.components.get(index).unwrap())
}
}
@@ -102,14 +102,8 @@ type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>;
type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>;
-type ComponentIterFilterFn =
- for<'a, 'b> fn(&'a (&'b Archetype, &'b ArchetypeEntity)) -> bool;
-
-type QueryEntityIter<'query> = Filter<
- FlatMap<
- ArchetypeRefIter<'query>,
- ComponentIterMapFnOutput<'query>,
- ComponentIterMapFn,
- >,
- ComponentIterFilterFn,
+type QueryEntityIter<'query> = FlatMap<
+ ArchetypeRefIter<'query, 'query>,
+ ComponentIterMapFnOutput<'query>,
+ ComponentIterMapFn,
>;
diff --git a/ecs/src/query/options.rs b/ecs/src/query/options.rs
deleted file mode 100644
index 6318359..0000000
--- a/ecs/src/query/options.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::marker::PhantomData;
-
-use hashbrown::HashSet;
-
-use crate::component::Component;
-use crate::EntityComponent;
-
-/// Query options.
-pub trait Options
-{
- fn entity_filter(components: &[EntityComponent]) -> bool;
-}
-
-impl Options for ()
-{
- fn entity_filter(_components: &[EntityComponent]) -> bool
- {
- true
- }
-}
-
-pub struct With<ComponentT>
-where
- ComponentT: Component,
-{
- _pd: PhantomData<ComponentT>,
-}
-
-impl<ComponentT> Options for With<ComponentT>
-where
- ComponentT: Component,
-{
- fn entity_filter(components: &[EntityComponent]) -> bool
- {
- let ids_set = components
- .iter()
- .map(|component| component.id)
- .collect::<HashSet<_>>();
-
- ids_set.contains(&ComponentT::id())
- }
-}
-
-pub struct Not<OptionsT>
-where
- OptionsT: Options,
-{
- _pd: PhantomData<OptionsT>,
-}
-
-impl<OptionsT> Options for Not<OptionsT>
-where
- OptionsT: Options,
-{
- fn entity_filter(components: &[EntityComponent]) -> bool
- {
- !OptionsT::entity_filter(components)
- }
-}
diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs
new file mode 100644
index 0000000..7f24147
--- /dev/null
+++ b/ecs/src/query/term.rs
@@ -0,0 +1,38 @@
+use std::marker::PhantomData;
+
+use crate::component::Component;
+use crate::query::{TermWithoutField, TermsBuilder, TermsBuilderInterface};
+
+pub struct With<ComponentT>
+where
+ ComponentT: Component,
+{
+ _pd: PhantomData<ComponentT>,
+}
+
+impl<ComponentT> TermWithoutField for With<ComponentT>
+where
+ ComponentT: Component,
+{
+ fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>)
+ {
+ terms_builder.with::<ComponentT>();
+ }
+}
+
+pub struct Without<ComponentT>
+where
+ ComponentT: Component,
+{
+ _pd: PhantomData<ComponentT>,
+}
+
+impl<ComponentT> TermWithoutField for Without<ComponentT>
+where
+ ComponentT: Component,
+{
+ fn apply_to_terms_builder(terms_builder: &mut TermsBuilder<'_>)
+ {
+ terms_builder.without::<ComponentT>();
+ }
+}
diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs
index 79dfe85..fa36689 100644
--- a/ecs/tests/query.rs
+++ b/ecs/tests/query.rs
@@ -1,7 +1,11 @@
use ecs::component::Component;
-use ecs::query::TermSequence as QueryTermSequence;
+use ecs::query::term::Without;
+use ecs::query::{
+ TermWithFieldTuple as QueryTermWithFieldTuple,
+ TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
+};
use ecs::uid::Uid;
-use ecs::{Component, World};
+use ecs::{Component, Query, World};
use parking_lot::{Mutex, Once};
pub static SETUP: Once = Once::new();
@@ -42,26 +46,26 @@ fn setup()
});
}
-fn assert_query_finds_ents<QueryTerms>(world: &World, mut expected_ent_ids: Vec<Uid>)
-where
- QueryTerms: QueryTermSequence,
+fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>(
+ query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>,
+ mut expected_ent_ids: Vec<Uid>,
+) where
+ QueryFieldTerms: QueryTermWithFieldTuple,
+ QueryFieldlessTerms: QueryTermWithoutFieldTuple,
{
assert!(
- world
- .query::<QueryTerms, ()>()
- .iter_with_euids()
- .all(|(ent_id, _)| {
- let Some(index) = expected_ent_ids
- .iter()
- .position(|expected_id| *expected_id == ent_id)
- else {
- return false;
- };
-
- expected_ent_ids.remove(index);
-
- true
- }),
+ query.iter_with_euids().all(|(ent_id, _)| {
+ let Some(index) = expected_ent_ids
+ .iter()
+ .position(|expected_id| *expected_id == ent_id)
+ else {
+ return false;
+ };
+
+ expected_ent_ids.remove(index);
+
+ true
+ }),
"Unexpected entity was found. Expected entities left: {expected_ent_ids:?}"
);
@@ -90,8 +94,8 @@ fn query_archetype_exists_with_edges_to_next_archetypes()
let ent_3_id = world.create_entity((A, B, C, E));
let ent_4_id = world.create_entity((A, B, C, G, F));
- assert_query_finds_ents::<(&A, &B, &C)>(
- &world,
+ assert_query_finds_ents(
+ world.query::<(&A, &B, &C), ()>(),
vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id],
);
}
@@ -109,7 +113,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype()
let ent_2_id = world.create_entity((A, B, F));
- assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -125,7 +129,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev()
let ent_2_id = world.create_entity((A, B, C, D, F));
- assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -141,7 +145,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype()
let ent_2_id = world.create_entity((A, B, F));
- assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -157,7 +161,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev()
let ent_2_id = world.create_entity((A, B, C, D, E, F));
- assert_query_finds_ents::<(&A, &B, &F)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -173,7 +177,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype()
let ent_2_id = world.create_entity((A, B, G));
- assert_query_finds_ents::<(&A, &B, &G)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -189,7 +193,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev()
let ent_2_id = world.create_entity((A, B, C, D, E, F, G));
- assert_query_finds_ents::<(&A, &B, &G)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -205,7 +209,10 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp()
let ent_2_id = world.create_entity((A, B, G));
- assert_query_finds_ents::<(&A, &Option<E>, &G)>(&world, vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(
+ world.query::<(&A, &Option<E>, &G), ()>(),
+ vec![ent_1_id, ent_2_id],
+ );
}
#[test]
@@ -224,7 +231,7 @@ fn query_archetype_nonexistant()
world.create_entity((A, B, C, G, F));
- assert_query_finds_ents::<(&A, &E)>(&world, vec![ent_2_id, ent_3_id]);
+ assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]);
}
#[test]
@@ -241,5 +248,75 @@ fn query_archetype_nonexistant_and_opt_comp()
let ent_3_id = world.create_entity((A, B, C, E));
world.create_entity((A, B, C, G, F));
- assert_query_finds_ents::<(&A, &E, &Option<D>)>(&world, vec![ent_2_id, ent_3_id]);
+ assert_query_finds_ents(
+ world.query::<(&A, &E, &Option<D>), ()>(),
+ vec![ent_2_id, ent_3_id],
+ );
+}
+
+#[test]
+fn query_without_comp_and_archetype_exists()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.create_entity((A, B, C));
+
+ world.create_entity((A, B, C, E));
+ world.create_entity((A, B, C, F, E));
+
+ let ent_2_id = world.create_entity((A, B, C, G));
+ let ent_3_id = world.create_entity((A, B, C, G, F));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &B, &C), (Without<E>,)>(),
+ vec![ent_1_id, ent_2_id, ent_3_id],
+ );
+}
+
+#[test]
+fn query_without_required_comp_and_archetype_exists()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.create_entity((A, B, C));
+
+ world.create_entity((A, B, C, E));
+ world.create_entity((A, B, C, F, E));
+
+ world.create_entity((A, B, C, G));
+ world.create_entity((A, B, C, G, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]);
+}
+
+#[test]
+fn query_without_comp_and_archetype_nonexistant()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.create_entity((A, B, C));
+
+ let ent_1_id = world.create_entity((A, B, C, E));
+
+ world.create_entity((A, B, C, F, E));
+
+ let ent_2_id = world.create_entity((A, B, C, G, E));
+ world.create_entity((A, B, C, G, F, E));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &E), (Without<F>,)>(),
+ vec![ent_1_id, ent_2_id],
+ );
}