diff options
Diffstat (limited to 'ecs/src/component/storage')
-rw-r--r-- | ecs/src/component/storage/archetype.rs | 253 | ||||
-rw-r--r-- | ecs/src/component/storage/graph.rs | 36 |
2 files changed, 230 insertions, 59 deletions
diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs index f6f8132..bb29701 100644 --- a/ecs/src/component/storage/archetype.rs +++ b/ecs/src/component/storage/archetype.rs @@ -1,18 +1,21 @@ +use std::any::Any; +use std::array::IntoIter as ArrayIntoIter; use std::hash::{DefaultHasher, Hash, Hasher}; +use std::iter::{Enumerate, Filter, Map, RepeatN, Zip}; +use std::option::IntoIter as OptionIntoIter; use std::slice::Iter as SliceIter; use hashbrown::HashMap; -use crate::component::Metadata as ComponentMetadata; +use crate::lock::Lock; use crate::uid::{Kind as UidKind, Uid}; -use crate::util::HashMapExt; -use crate::EntityComponent; +use crate::util::{Either, HashMapExt}; #[derive(Debug)] pub struct Archetype { id: Id, - entities: Vec<ArchetypeEntity>, + entities: Vec<Entity>, entity_index_lookup: HashMap<Uid, usize>, component_index_lookup: HashMap<Uid, usize>, component_ids: Vec<Uid>, @@ -53,7 +56,7 @@ impl Archetype .keys_is_subset(&other.component_index_lookup) } - pub fn get_entity_by_id(&self, entity_uid: Uid) -> Option<&ArchetypeEntity> + pub fn get_entity_by_id(&self, entity_uid: Uid) -> Option<&Entity> { let index = *self.entity_index_lookup.get(&entity_uid)?; @@ -64,7 +67,7 @@ impl Archetype })) } - pub fn push_entity(&mut self, entity: ArchetypeEntity) + pub fn push_entity(&mut self, entity: Entity) { self.entity_index_lookup .insert(entity.uid, self.entities.len()); @@ -72,7 +75,7 @@ impl Archetype self.entities.push(entity); } - pub fn remove_entity(&mut self, entity_uid: Uid) -> Option<ArchetypeEntity> + pub fn remove_entity(&mut self, entity_uid: Uid) -> Option<Entity> { //debug_assert_eq!(entity_uid.kind(), UidKind::Entity); @@ -116,8 +119,54 @@ impl Archetype self.component_index_lookup.len() } + pub fn get_matching_component_indices( + &self, + component_id: Uid, + ) -> MatchingComponentIter + { + assert!( + component_id.kind() == UidKind::Component + || component_id.kind() == UidKind::Pair + ); + + if component_id.kind() == UidKind::Pair + && component_id.target_component() == Uid::wildcard() + { + return MatchingComponentIter { + inner: Either::A( + self.component_ids + .iter() + .enumerate() + .zip(std::iter::repeat_n(component_id, self.component_ids.len())) + .filter( + (|((_, other_comp_id), component_id)| { + other_comp_id.kind() == UidKind::Pair + && other_comp_id.has_same_relation_as(*component_id) + }) + as MatchingComponentIterFilterFn, + ) + .map(|((index, other_comp_id), _)| (*other_comp_id, index)), + ), + }; + } + + MatchingComponentIter { + inner: Either::B( + [component_id] + .into_iter() + .zip(self.get_index_for_component(component_id)), + ), + } + } + pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize> { + assert!( + component_id.kind() == UidKind::Component + || (component_id.kind() == UidKind::Pair + && component_id.target_component() != Uid::wildcard()) + ); + self.component_index_lookup.get(&component_id).copied() } @@ -131,23 +180,79 @@ impl Archetype self.component_ids.iter().copied() } - pub fn has_component_with_id(&self, component_id: Uid) -> bool + pub fn contains_matching_component(&self, component_id: Uid) -> bool { - debug_assert_eq!(component_id.kind(), UidKind::Component); + let component_id_kind = component_id.kind(); + + debug_assert!( + component_id_kind == UidKind::Component || component_id_kind == UidKind::Pair + ); + + if component_id.kind() == UidKind::Pair + && component_id.target_component() == Uid::wildcard() + { + return self.component_ids.iter().any(|other_comp_id| { + other_comp_id.kind() == UidKind::Pair + && other_comp_id.has_same_relation_as(component_id) + }); + } + + self.contains_component_with_exact_id(component_id) + } + + pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool + { + let component_id_kind = component_id.kind(); + + debug_assert!( + component_id_kind == UidKind::Component + || (component_id_kind == UidKind::Pair + && component_id.target_component() != Uid::wildcard()) + ); self.component_index_lookup.contains_key(&component_id) } } +type MatchingComponentIterFilterFn = fn(&((usize, &Uid), Uid)) -> bool; + +type MatchingComponentIterMapFn = fn(((usize, &Uid), Uid)) -> (Uid, usize); + +type InnerMatchingComponentIterA<'archetype> = Map< + Filter< + Zip<Enumerate<SliceIter<'archetype, Uid>>, RepeatN<Uid>>, + MatchingComponentIterFilterFn, + >, + MatchingComponentIterMapFn, +>; + +type InnerMatchingComponentIterB = Zip<ArrayIntoIter<Uid, 1>, OptionIntoIter<usize>>; + +#[derive(Debug)] +pub struct MatchingComponentIter<'archetype> +{ + inner: Either<InnerMatchingComponentIterA<'archetype>, InnerMatchingComponentIterB>, +} + +impl Iterator for MatchingComponentIter<'_> +{ + type Item = (Uid, usize); + + fn next(&mut self) -> Option<Self::Item> + { + self.inner.next() + } +} + #[derive(Debug)] pub struct EntityIter<'archetype> { - iter: SliceIter<'archetype, ArchetypeEntity>, + iter: SliceIter<'archetype, Entity>, } impl<'archetype> Iterator for EntityIter<'archetype> { - type Item = &'archetype ArchetypeEntity; + type Item = &'archetype Entity; fn next(&mut self) -> Option<Self::Item> { @@ -156,71 +261,125 @@ impl<'archetype> Iterator for EntityIter<'archetype> } #[derive(Debug)] -pub struct ArchetypeEntity +pub struct Entity { - pub uid: Uid, - pub components: Vec<EntityComponent>, + uid: Uid, + components: Vec<EntityComponent>, } -/// Archetype ID. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Id +impl Entity { - hash: u64, + pub fn new(uid: Uid, components: impl IntoIterator<Item = EntityComponent>) -> Self + { + Self { + uid, + components: components.into_iter().collect(), + } + } + + pub fn uid(&self) -> Uid + { + self.uid + } + + pub fn components(&self) -> &[EntityComponent] + { + &self.components + } + + pub fn remove_component(&mut self, component_id: Uid, archetype: &Archetype) + { + let index = archetype + .get_index_for_component(component_id) + .expect("Archetype should contain component"); + + self.components.remove(index); + } + + pub fn insert_component( + &mut self, + component_id: Uid, + component: EntityComponent, + archetype: &Archetype, + ) + { + let index = archetype + .get_index_for_component(component_id) + .expect("Archetype should contain component"); + + self.components.insert(index, component); + } } -impl Id +#[derive(Debug)] +pub struct EntityComponent { - pub fn new(component_ids: &impl AsRef<[Uid]>) -> Self + id: Uid, + component: Lock<Box<dyn Any>>, +} + +impl EntityComponent +{ + pub fn new( + component: Box<dyn Any>, + component_id: Uid, + component_name: &'static str, + ) -> Self { - if component_ids.as_ref().is_empty() { - return Self { hash: 0 }; + Self { + id: component_id, + component: Lock::new(component, component_name), } + } - debug_assert!( - component_ids.as_ref().is_sorted(), - "Cannot create archetype ID from unsorted component IDs" - ); + #[allow(dead_code)] + pub fn id(&self) -> Uid + { + self.id + } - let mut hasher = DefaultHasher::new(); + pub fn component(&self) -> &Lock<Box<dyn Any>> + { + &self.component + } +} - for component_id in component_ids.as_ref() { - component_id.hash(&mut hasher); - } +/// Archetype ID. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Id +{ + hash: u64, +} - Self { hash: hasher.finish() } +impl Id +{ + pub fn new_empty() -> Self + { + Self { hash: 0 } } - pub fn from_components_metadata<'a>( - components_metadata: impl IntoIterator<Item = &'a ComponentMetadata>, - ) -> Self + pub fn new<'a>(component_ids: impl IntoIterator<Item = &'a Uid>) -> Self { let mut hasher = DefaultHasher::new(); let mut prev_component_id: Option<Uid> = None; - let mut comp_metadata_iter = components_metadata.into_iter().peekable(); + let mut component_id_iter = component_ids.into_iter().peekable(); - if comp_metadata_iter.peek().is_none() { - return Self { hash: 0 }; + if component_id_iter.peek().is_none() { + return Self::new_empty(); } - for comp_metadata in comp_metadata_iter { - if prev_component_id - .is_some_and(|prev_comp_id| comp_metadata.id < prev_comp_id) - { + for comp_id in component_id_iter { + if prev_component_id.is_some_and(|prev_comp_id| *comp_id < prev_comp_id) { panic!( "Cannot create archetype ID from a unsorted component metadata list" ); } - prev_component_id = Some(comp_metadata.id); - - if comp_metadata.is_optional { - continue; - } + prev_component_id = Some(*comp_id); - comp_metadata.id.hash(&mut hasher); + comp_id.hash(&mut hasher); } Self { hash: hasher.finish() } diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs index 11160e7..29fa937 100644 --- a/ecs/src/component/storage/graph.rs +++ b/ecs/src/component/storage/graph.rs @@ -140,19 +140,31 @@ impl Graph } fn create_missing_subset_node_edges( - target_node: &ArchetypeNode, + target_node: &mut ArchetypeNode, subset_node: &mut ArchetypeNode, ) { let uniq_comp_id = target_node .archetype() .component_ids_sorted() - .find(|id| !subset_node.archetype().has_component_with_id(*id)) + .find(|id| { + !subset_node + .archetype() + .contains_component_with_exact_id(*id) + }) .unwrap(); subset_node .get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default) .add = Some(subset_node.make_add_edge(uniq_comp_id).0); + + if target_node.archetype().component_cnt() + == subset_node.archetype().component_cnt() + 1 + { + target_node + .get_or_insert_edges(uniq_comp_id, ArchetypeEdges::default) + .remove = Some(subset_node.archetype().id()); + } } fn create_missing_superset_node_edges( @@ -169,7 +181,7 @@ impl Graph .find(|other_archetype_comp_id| { !target_node .archetype() - .has_component_with_id(*other_archetype_comp_id) + .contains_component_with_exact_id(*other_archetype_comp_id) }) .or_else(|| { if target_node.archetype().component_cnt() != 0 { @@ -196,7 +208,11 @@ impl Graph let extra_comp_id = superset_node .archetype() .component_ids_unsorted() - .find(|comp_id| !target_node.archetype().has_component_with_id(*comp_id)) + .find(|comp_id| { + !target_node + .archetype() + .contains_component_with_exact_id(*comp_id) + }) .expect("Archetype should contain one extra component ID"); superset_node @@ -234,7 +250,10 @@ impl ArchetypeNode insert_fn: impl FnOnce() -> ArchetypeEdges, ) -> &mut ArchetypeEdges { - debug_assert_eq!(component_id.kind(), UidKind::Component); + debug_assert!(matches!( + component_id.kind(), + UidKind::Component | UidKind::Pair + )); self.edges.entry(component_id).or_insert_with(insert_fn) } @@ -245,13 +264,6 @@ impl ArchetypeNode self.edges.iter() } - pub fn get_edges_mut(&mut self, component_id: Uid) -> Option<&mut ArchetypeEdges> - { - debug_assert_eq!(component_id.kind(), UidKind::Component); - - self.edges.get_mut(&component_id) - } - pub fn make_add_edge(&self, component_id: Uid) -> (ArchetypeId, Vec<Uid>) { let mut edge_comp_ids = self |