From c47067d4c1eb2e6f2ea0ac7d3daba01d4f46e5e1 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 9 Apr 2024 21:09:00 +0200 Subject: refactor: move query structs to new query module --- ecs/src/lib.rs | 243 ++----------------------------------------------------- ecs/src/query.rs | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 236 deletions(-) create mode 100644 ecs/src/query.rs diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 9d79b91..33d981b 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,38 +1,34 @@ #![deny(clippy::all, clippy::pedantic)] -use std::any::{type_name, Any, TypeId}; +use std::any::{type_name, TypeId}; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; -use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ops::RangeBounds; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::vec::Drain; use crate::actions::Action; use crate::component::{Component, Sequence as ComponentSequence}; use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; -use crate::lock::{Lock, ReadGuard}; -use crate::system::{ - NoInitParamFlag as NoInitSystemParamFlag, - Param as SystemParam, - System, - TypeErased as TypeErasedSystem, -}; -use crate::tuple::FilterExclude as TupleFilterExclude; +use crate::lock::Lock; +use crate::system::{System, TypeErased as TypeErasedSystem}; use crate::type_name::TypeName; pub mod actions; pub mod component; pub mod event; pub mod lock; +pub mod query; pub mod system; pub mod tuple; pub mod type_name; pub use ecs_macros::Component; +pub use crate::query::Query; + #[derive(Debug, Default)] struct Entity { @@ -240,231 +236,6 @@ impl TypeName for ActionQueue } } -/// A entity query. -#[derive(Debug)] -pub struct Query<'world, Comps> -where - Comps: ComponentSequence, -{ - component_storage: ReadGuard<'world, ComponentStorage>, - component_storage_lock: Weak>, - comps_pd: PhantomData, -} - -impl<'world, Comps> Query<'world, Comps> -where - Comps: ComponentSequence, -{ - #[must_use] - pub fn iter<'this>(&'this self) -> QueryComponentIter<'world, Comps> - where - 'this: 'world, - { - QueryComponentIter { - component_storage: &self.component_storage, - current_entity_index: 0, - component_type_ids: Comps::type_ids(), - comps_pd: PhantomData, - } - } - - /// Returns a weak reference query to the same components. - #[must_use] - pub fn to_weak_ref(&self) -> WeakRefQuery - { - WeakRefQuery { - component_storage: self.component_storage_lock.clone(), - comps_pd: PhantomData, - } - } - - fn new(component_storage: &'world Arc>) -> Self - { - Self { - component_storage: component_storage - .read_nonblock() - .expect("Failed to acquire read-only component storage lock"), - component_storage_lock: Arc::downgrade(component_storage), - comps_pd: PhantomData, - } - } -} - -impl<'world, Comps> IntoIterator for &'world Query<'world, Comps> -where - Comps: ComponentSequence, -{ - type IntoIter = QueryComponentIter<'world, Comps>; - type Item = Comps::Refs<'world>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -unsafe impl<'world, Comps> SystemParam<'world> for Query<'world, Comps> -where - Comps: ComponentSequence, -{ - type Flags = NoInitSystemParamFlag; - type Input = TupleFilterExclude; - - fn initialize( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new( - _system: &'world impl System<'world, SystemImpl>, - world_data: &'world WorldData, - ) -> Self - { - Self::new(&world_data.component_storage) - } - - fn is_compatible>() -> bool - { - let other_comparable = Other::get_comparable(); - - let Some(other_query_component_ids) = - other_comparable.downcast_ref::() - else { - return true; - }; - - !other_query_component_ids.contains_component_in::() - } - - fn get_comparable() -> Box - { - Box::new(QueryComponentIds { - component_type_ids: Comps::type_ids(), - }) - } -} - -/// A entity query containing a weak reference to the world. -#[derive(Debug)] -pub struct WeakRefQuery -where - Comps: ComponentSequence, -{ - component_storage: Weak>, - comps_pd: PhantomData, -} - -impl WeakRefQuery -where - Comps: ComponentSequence, -{ - /// Returns a struct which can be used to retrieve a [`Query`]. - /// - /// Returns [`None`] if the [`World`] has been dropped. - #[must_use] - pub fn access(&self) -> Option> - { - Some(RefQuery { - component_storage: self.component_storage.upgrade()?, - _pd: PhantomData, - }) - } -} - -impl Clone for WeakRefQuery -where - Comps: ComponentSequence, -{ - fn clone(&self) -> Self - { - Self { - component_storage: self.component_storage.clone(), - comps_pd: PhantomData, - } - } -} - -/// Intermediate between [`Query`] and [`WeakRefQuery`]. Contains a strong reference to -/// the world which is not allowed direct access to. -#[derive(Debug, Clone)] -pub struct RefQuery<'weak_ref, Comps> -where - Comps: ComponentSequence, -{ - component_storage: Arc>, - _pd: PhantomData<&'weak_ref Comps>, -} - -impl<'weak_ref, Comps> RefQuery<'weak_ref, Comps> -where - Comps: ComponentSequence, -{ - #[must_use] - pub fn to_query(&self) -> Query<'_, Comps> - { - Query::new(&self.component_storage) - } -} - -#[derive(Debug)] -struct QueryComponentIds -{ - component_type_ids: Vec, -} - -impl QueryComponentIds -{ - fn contains_component_in(&self) -> bool - where - OtherComps: ComponentSequence, - { - let other_component_type_ids = OtherComps::type_ids() - .into_iter() - .collect::>(); - - // TODO: Make this a bit smarter. Queries with a same component can be compatible - // if one of the queries have a component the other one does not have - self.component_type_ids - .iter() - .any(|component_type_id| other_component_type_ids.contains(component_type_id)) - } -} - -pub struct QueryComponentIter<'world, Comps> -{ - component_storage: &'world ComponentStorage, - current_entity_index: usize, - component_type_ids: Vec, - comps_pd: PhantomData, -} - -impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps> -where - Comps: ComponentSequence + 'world, -{ - type Item = Comps::Refs<'world>; - - fn next(&mut self) -> Option - { - let (matching_entity_index, matching_entity) = - self.component_storage.find_entity_with_components( - self.current_entity_index, - &self.component_type_ids, - )?; - - self.current_entity_index = matching_entity_index + 1; - - Some(Comps::from_components( - matching_entity - .components - .iter() - .map(|component| &component.component), - )) - } -} - #[derive(Debug, Default)] pub struct ComponentStorage { diff --git a/ecs/src/query.rs b/ecs/src/query.rs new file mode 100644 index 0000000..9874b19 --- /dev/null +++ b/ecs/src/query.rs @@ -0,0 +1,238 @@ +use std::any::{Any, TypeId}; +use std::collections::HashSet; +use std::marker::PhantomData; +use std::sync::{Arc, Weak}; + +use crate::component::Sequence as ComponentSequence; +use crate::lock::{Lock, ReadGuard}; +use crate::system::{ + NoInitParamFlag as NoInitSystemParamFlag, + Param as SystemParam, + System, +}; +use crate::tuple::FilterExclude as TupleFilterExclude; +use crate::{ComponentStorage, WorldData}; + +#[derive(Debug)] +pub struct Query<'world, Comps> +where + Comps: ComponentSequence, +{ + component_storage: ReadGuard<'world, ComponentStorage>, + component_storage_lock: Weak>, + comps_pd: PhantomData, +} + +impl<'world, Comps> Query<'world, Comps> +where + Comps: ComponentSequence, +{ + #[must_use] + pub fn iter<'this>(&'this self) -> ComponentIter<'world, Comps> + where + 'this: 'world, + { + ComponentIter { + component_storage: &self.component_storage, + current_entity_index: 0, + component_type_ids: Comps::type_ids(), + comps_pd: PhantomData, + } + } + + /// Returns a weak reference query to the same components. + #[must_use] + pub fn to_weak_ref(&self) -> WeakRef + { + WeakRef { + component_storage: self.component_storage_lock.clone(), + comps_pd: PhantomData, + } + } + + pub(crate) fn new(component_storage: &'world Arc>) -> Self + { + Self { + component_storage: component_storage + .read_nonblock() + .expect("Failed to acquire read-only component storage lock"), + component_storage_lock: Arc::downgrade(component_storage), + comps_pd: PhantomData, + } + } +} + +impl<'world, Comps> IntoIterator for &'world Query<'world, Comps> +where + Comps: ComponentSequence, +{ + type IntoIter = ComponentIter<'world, Comps>; + type Item = Comps::Refs<'world>; + + fn into_iter(self) -> Self::IntoIter + { + self.iter() + } +} + +unsafe impl<'world, Comps> SystemParam<'world> for Query<'world, Comps> +where + Comps: ComponentSequence, +{ + type Flags = NoInitSystemParamFlag; + type Input = TupleFilterExclude; + + fn initialize( + _system: &mut impl System<'world, SystemImpl>, + _input: Self::Input, + ) + { + } + + fn new( + _system: &'world impl System<'world, SystemImpl>, + world_data: &'world WorldData, + ) -> Self + { + Self::new(&world_data.component_storage) + } + + fn is_compatible>() -> bool + { + let other_comparable = Other::get_comparable(); + + let Some(other_query_component_ids) = + other_comparable.downcast_ref::() + else { + return true; + }; + + !other_query_component_ids.contains_component_in::() + } + + fn get_comparable() -> Box + { + Box::new(QueryComponentIds { + component_type_ids: Comps::type_ids(), + }) + } +} + +/// A entity query containing a weak reference to the world. +#[derive(Debug)] +pub struct WeakRef +where + Comps: ComponentSequence, +{ + component_storage: Weak>, + comps_pd: PhantomData, +} + +impl WeakRef +where + Comps: ComponentSequence, +{ + /// Returns a struct which can be used to retrieve a [`Query`]. + /// + /// Returns [`None`] if the [`World`] has been dropped. + #[must_use] + pub fn access(&self) -> Option> + { + Some(Ref { + component_storage: self.component_storage.upgrade()?, + _pd: PhantomData, + }) + } +} + +impl Clone for WeakRef +where + Comps: ComponentSequence, +{ + fn clone(&self) -> Self + { + Self { + component_storage: self.component_storage.clone(), + comps_pd: PhantomData, + } + } +} + +/// Intermediate between [`Query`] and [`WeakRefQuery`]. Contains a strong reference to +/// the world which is not allowed direct access to. +#[derive(Debug, Clone)] +pub struct Ref<'weak_ref, Comps> +where + Comps: ComponentSequence, +{ + component_storage: Arc>, + _pd: PhantomData<&'weak_ref Comps>, +} + +impl<'weak_ref, Comps> Ref<'weak_ref, Comps> +where + Comps: ComponentSequence, +{ + #[must_use] + pub fn to_query(&self) -> Query<'_, Comps> + { + Query::new(&self.component_storage) + } +} + +pub struct ComponentIter<'world, Comps> +{ + component_storage: &'world ComponentStorage, + current_entity_index: usize, + component_type_ids: Vec, + comps_pd: PhantomData, +} + +impl<'world, Comps> Iterator for ComponentIter<'world, Comps> +where + Comps: ComponentSequence + 'world, +{ + type Item = Comps::Refs<'world>; + + fn next(&mut self) -> Option + { + let (matching_entity_index, matching_entity) = + self.component_storage.find_entity_with_components( + self.current_entity_index, + &self.component_type_ids, + )?; + + self.current_entity_index = matching_entity_index + 1; + + Some(Comps::from_components( + matching_entity + .components + .iter() + .map(|component| &component.component), + )) + } +} + +#[derive(Debug)] +struct QueryComponentIds +{ + component_type_ids: Vec, +} + +impl QueryComponentIds +{ + fn contains_component_in(&self) -> bool + where + OtherComps: ComponentSequence, + { + let other_component_type_ids = OtherComps::type_ids() + .into_iter() + .collect::>(); + + // TODO: Make this a bit smarter. Queries with a same component can be compatible + // if one of the queries have a component the other one does not have + self.component_type_ids + .iter() + .any(|component_type_id| other_component_type_ids.contains(component_type_id)) + } +} -- cgit v1.2.3-18-g5258