diff options
-rw-r--r-- | ecs/src/component.rs | 2 | ||||
-rw-r--r-- | ecs/src/component/storage.rs | 81 | ||||
-rw-r--r-- | ecs/src/lib.rs | 117 | ||||
-rw-r--r-- | ecs/src/query.rs | 4 |
4 files changed, 104 insertions, 100 deletions
diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 63ef7c4..d3b00ef 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -10,6 +10,8 @@ use crate::EntityComponent; pub mod local; +pub(crate) mod storage; + pub trait Component: SystemInput + Any + TypeName { /// The component type in question. Will usually be `Self` diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs new file mode 100644 index 0000000..cdff09e --- /dev/null +++ b/ecs/src/component/storage.rs @@ -0,0 +1,81 @@ +use std::any::{type_name, TypeId}; +use std::collections::HashSet; + +use crate::component::{Component, IsOptional as ComponentIsOptional}; +use crate::lock::Lock; +use crate::type_name::TypeName; +use crate::EntityComponent; + +#[derive(Debug, Default)] +pub struct ComponentStorage +{ + entities: Vec<Entity>, +} + +impl ComponentStorage +{ + pub fn find_entity_with_components( + &self, + start_index: usize, + component_type_ids: &[(TypeId, ComponentIsOptional)], + ) -> Option<(usize, &[EntityComponent])> + { + // TODO: This is a really dumb and slow way to do this. Refactor the world + // to store components in archetypes + self.entities + .iter() + .enumerate() + .skip(start_index) + .find(move |(_index, entity)| { + let entity_components = entity + .components + .iter() + .map(|component| component.id) + .collect::<HashSet<_>>(); + + if component_type_ids + .iter() + .filter(|(_, is_optional)| *is_optional == ComponentIsOptional::No) + .all(|(component_type_id, _)| { + entity_components.contains(component_type_id) + }) + { + return true; + } + + false + }) + .map(|(index, entity)| (index, &*entity.components)) + } + + pub fn push_entity( + &mut self, + components: impl IntoIterator<Item = Box<dyn Component>>, + ) + { + self.entities.push(Entity { + components: components + .into_iter() + .map(|component| EntityComponent { + id: (*component).type_id(), + name: component.type_name(), + component: Lock::new(component), + }) + .collect(), + }); + } +} + +impl TypeName for ComponentStorage +{ + fn type_name(&self) -> &'static str + { + type_name::<Self>() + } +} + +#[derive(Debug, Default)] +struct Entity +{ + components: Vec<EntityComponent>, +} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index f76d1ae..1983f66 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,7 +1,7 @@ #![deny(clippy::all, clippy::pedantic)] use std::any::{type_name, TypeId}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; use std::mem::ManuallyDrop; use std::ops::RangeBounds; @@ -10,11 +10,8 @@ use std::sync::Arc; use std::vec::Drain; use crate::actions::Action; -use crate::component::{ - Component, - IsOptional as ComponentIsOptional, - Sequence as ComponentSequence, -}; +use crate::component::storage::ComponentStorage; +use crate::component::{Component, Sequence as ComponentSequence}; use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; use crate::extension::{Collector as ExtensionCollector, Extension}; use crate::lock::Lock; @@ -38,21 +35,6 @@ pub use ecs_macros::{Component, Sole}; pub use crate::query::Query; #[derive(Debug, Default)] -struct Entity -{ - components: Vec<EntityComponent>, -} - -#[derive(Debug)] -#[non_exhaustive] -pub struct EntityComponent -{ - pub id: TypeId, - pub name: &'static str, - pub component: Lock<Box<dyn Component>>, -} - -#[derive(Debug, Default)] pub struct World { systems: Vec<TypeErasedSystem>, @@ -80,18 +62,7 @@ impl World .component_storage .write_nonblock() .expect("Failed to acquire read-write component storage lock") - .entities - .push(Entity { - components: components - .into_vec() - .into_iter() - .map(|component| EntityComponent { - id: (*component).type_id(), - name: component.type_name(), - component: Lock::new(component), - }) - .collect(), - }); + .push_entity(components.into_vec()) } /// Adds a globally shared singleton value. @@ -169,23 +140,12 @@ impl World .drain(..) { match action { - Action::Spawn(components) => { - self.data - .component_storage - .write_nonblock() - .expect("Failed to acquire read-write component storage lock") - .entities - .push(Entity { - components: components - .into_iter() - .map(|component| EntityComponent { - id: (*component).type_id(), - name: component.type_name(), - component: Lock::new(component), - }) - .collect(), - }); - } + Action::Spawn(components) => self + .data + .component_storage + .write_nonblock() + .expect("Failed to acquire read-write component storage lock") + .push_entity(components), Action::Stop => { self.stop.store(true, Ordering::Relaxed); } @@ -240,6 +200,15 @@ pub struct WorldData action_queue: Arc<Lock<ActionQueue>>, } +#[derive(Debug)] +#[non_exhaustive] +pub struct EntityComponent +{ + pub id: TypeId, + pub name: &'static str, + pub component: Lock<Box<dyn Component>>, +} + #[derive(Debug, Default)] struct ActionQueue { @@ -267,54 +236,6 @@ impl TypeName for ActionQueue } } -#[derive(Debug, Default)] -pub struct ComponentStorage -{ - entities: Vec<Entity>, -} - -impl ComponentStorage -{ - fn find_entity_with_components( - &self, - start_index: usize, - component_type_ids: &[(TypeId, ComponentIsOptional)], - ) -> Option<(usize, &Entity)> - { - // TODO: This is a really dumb and slow way to do this. Refactor the world - // to store components in archetypes - self.entities.iter().enumerate().skip(start_index).find( - move |(_index, entity)| { - let entity_components = entity - .components - .iter() - .map(|component| component.id) - .collect::<HashSet<_>>(); - - if component_type_ids - .iter() - .filter(|(_, is_optional)| *is_optional == ComponentIsOptional::No) - .all(|(component_type_id, _)| { - entity_components.contains(component_type_id) - }) - { - return true; - } - - false - }, - ) - } -} - -impl TypeName for ComponentStorage -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} - #[derive(Debug, thiserror::Error)] #[error("Sole {0} already exists")] pub struct SoleAlreadyExistsError(pub &'static str); diff --git a/ecs/src/query.rs b/ecs/src/query.rs index ffab105..90e6169 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -199,7 +199,7 @@ where fn next(&mut self) -> Option<Self::Item> { - let (matching_entity_index, matching_entity) = + let (matching_entity_index, matching_entity_components) = self.component_storage.find_entity_with_components( self.current_entity_index, &self.component_type_ids, @@ -208,7 +208,7 @@ where self.current_entity_index = matching_entity_index + 1; Some(Comps::from_components( - matching_entity.components.iter().map(|component| component), + matching_entity_components.iter().map(|component| component), )) } } |