summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ecs/src/lib.rs243
-rw-r--r--ecs/src/query.rs238
2 files changed, 245 insertions, 236 deletions
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<Lock<ComponentStorage>>,
- comps_pd: PhantomData<Comps>,
-}
-
-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<Comps>
- {
- WeakRefQuery {
- component_storage: self.component_storage_lock.clone(),
- comps_pd: PhantomData,
- }
- }
-
- fn new(component_storage: &'world Arc<Lock<ComponentStorage>>) -> 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<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world_data: &'world WorldData,
- ) -> Self
- {
- Self::new(&world_data.component_storage)
- }
-
- fn is_compatible<Other: SystemParam<'world>>() -> bool
- {
- let other_comparable = Other::get_comparable();
-
- let Some(other_query_component_ids) =
- other_comparable.downcast_ref::<QueryComponentIds>()
- else {
- return true;
- };
-
- !other_query_component_ids.contains_component_in::<Comps>()
- }
-
- fn get_comparable() -> Box<dyn Any>
- {
- Box::new(QueryComponentIds {
- component_type_ids: Comps::type_ids(),
- })
- }
-}
-
-/// A entity query containing a weak reference to the world.
-#[derive(Debug)]
-pub struct WeakRefQuery<Comps>
-where
- Comps: ComponentSequence,
-{
- component_storage: Weak<Lock<ComponentStorage>>,
- comps_pd: PhantomData<Comps>,
-}
-
-impl<Comps> WeakRefQuery<Comps>
-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<RefQuery<'_, Comps>>
- {
- Some(RefQuery {
- component_storage: self.component_storage.upgrade()?,
- _pd: PhantomData,
- })
- }
-}
-
-impl<Comps> Clone for WeakRefQuery<Comps>
-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<Lock<ComponentStorage>>,
- _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<TypeId>,
-}
-
-impl QueryComponentIds
-{
- fn contains_component_in<OtherComps>(&self) -> bool
- where
- OtherComps: ComponentSequence,
- {
- let other_component_type_ids = OtherComps::type_ids()
- .into_iter()
- .collect::<HashSet<TypeId>>();
-
- // 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<TypeId>,
- comps_pd: PhantomData<Comps>,
-}
-
-impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps>
-where
- Comps: ComponentSequence + 'world,
-{
- type Item = Comps::Refs<'world>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- 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<Lock<ComponentStorage>>,
+ comps_pd: PhantomData<Comps>,
+}
+
+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<Comps>
+ {
+ WeakRef {
+ component_storage: self.component_storage_lock.clone(),
+ comps_pd: PhantomData,
+ }
+ }
+
+ pub(crate) fn new(component_storage: &'world Arc<Lock<ComponentStorage>>) -> 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<SystemImpl>(
+ _system: &mut impl System<'world, SystemImpl>,
+ _input: Self::Input,
+ )
+ {
+ }
+
+ fn new<SystemImpl>(
+ _system: &'world impl System<'world, SystemImpl>,
+ world_data: &'world WorldData,
+ ) -> Self
+ {
+ Self::new(&world_data.component_storage)
+ }
+
+ fn is_compatible<Other: SystemParam<'world>>() -> bool
+ {
+ let other_comparable = Other::get_comparable();
+
+ let Some(other_query_component_ids) =
+ other_comparable.downcast_ref::<QueryComponentIds>()
+ else {
+ return true;
+ };
+
+ !other_query_component_ids.contains_component_in::<Comps>()
+ }
+
+ fn get_comparable() -> Box<dyn Any>
+ {
+ Box::new(QueryComponentIds {
+ component_type_ids: Comps::type_ids(),
+ })
+ }
+}
+
+/// A entity query containing a weak reference to the world.
+#[derive(Debug)]
+pub struct WeakRef<Comps>
+where
+ Comps: ComponentSequence,
+{
+ component_storage: Weak<Lock<ComponentStorage>>,
+ comps_pd: PhantomData<Comps>,
+}
+
+impl<Comps> WeakRef<Comps>
+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<Ref<'_, Comps>>
+ {
+ Some(Ref {
+ component_storage: self.component_storage.upgrade()?,
+ _pd: PhantomData,
+ })
+ }
+}
+
+impl<Comps> Clone for WeakRef<Comps>
+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<Lock<ComponentStorage>>,
+ _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<TypeId>,
+ comps_pd: PhantomData<Comps>,
+}
+
+impl<'world, Comps> Iterator for ComponentIter<'world, Comps>
+where
+ Comps: ComponentSequence + 'world,
+{
+ type Item = Comps::Refs<'world>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ 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<TypeId>,
+}
+
+impl QueryComponentIds
+{
+ fn contains_component_in<OtherComps>(&self) -> bool
+ where
+ OtherComps: ComponentSequence,
+ {
+ let other_component_type_ids = OtherComps::type_ids()
+ .into_iter()
+ .collect::<HashSet<TypeId>>();
+
+ // 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))
+ }
+}