summaryrefslogtreecommitdiff
path: root/ecs/src/component
diff options
context:
space:
mode:
Diffstat (limited to 'ecs/src/component')
-rw-r--r--ecs/src/component/local.rs62
-rw-r--r--ecs/src/component/storage.rs621
2 files changed, 0 insertions, 683 deletions
diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs
deleted file mode 100644
index 20627bf..0000000
--- a/ecs/src/component/local.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use std::ops::{Deref, DerefMut};
-
-use crate::component::Component;
-use crate::system::{ComponentRefMut, Param as SystemParam, System};
-use crate::World;
-
-/// Holds a component which is local to a single system.
-#[derive(Debug)]
-pub struct Local<'world, LocalComponent: Component>
-{
- local_component: ComponentRefMut<'world, LocalComponent>,
-}
-
-unsafe impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent>
-where
- LocalComponent: Component,
-{
- type Flags = ();
- type Input = LocalComponent;
-
- fn initialize<SystemImpl>(
- system: &mut impl System<'world, SystemImpl>,
- input: Self::Input,
- )
- {
- system.set_local_component(input);
- }
-
- fn new<SystemImpl>(
- system: &'world impl System<'world, SystemImpl>,
- _world: &'world World,
- ) -> Self
- {
- let local_component = system
- .get_local_component_mut::<LocalComponent>()
- .expect("Local component is uninitialized");
-
- Self { local_component }
- }
-}
-
-impl<'world, LocalComponent> Deref for Local<'world, LocalComponent>
-where
- LocalComponent: Component,
-{
- type Target = LocalComponent;
-
- fn deref(&self) -> &Self::Target
- {
- &self.local_component
- }
-}
-
-impl<'world, LocalComponent> DerefMut for Local<'world, LocalComponent>
-where
- LocalComponent: Component,
-{
- fn deref_mut(&mut self) -> &mut Self::Target
- {
- &mut self.local_component
- }
-}
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs
deleted file mode 100644
index ffd682e..0000000
--- a/ecs/src/component/storage.rs
+++ /dev/null
@@ -1,621 +0,0 @@
-use std::any::type_name;
-use std::borrow::Borrow;
-use std::cell::RefCell;
-use std::collections::{HashMap, HashSet};
-use std::slice::Iter as SliceIter;
-use std::vec::IntoIter as OwnedVecIter;
-
-use crate::archetype::Id as ArchetypeId;
-use crate::component::{
- Component,
- IsOptional as ComponentIsOptional,
- Metadata as ComponentMetadata,
-};
-use crate::type_name::TypeName;
-use crate::uid::Uid;
-use crate::util::Sortable;
-use crate::EntityComponent;
-
-#[derive(Debug, Default)]
-pub struct Storage
-{
- archetypes: Vec<Archetype>,
- archetype_lookup: RefCell<HashMap<ArchetypeId, ArchetypeLookupEntry>>,
- entity_archetype_lookup: HashMap<Uid, ArchetypeId>,
-}
-
-impl Storage
-{
- pub fn find_entities<CompMetadataList>(
- &self,
- mut components_metadata: CompMetadataList,
- ) -> ArchetypeRefIter<'_>
- where
- CompMetadataList: Sortable<Item = ComponentMetadata>,
- CompMetadataList: AsRef<[ComponentMetadata]>,
- {
- components_metadata.sort_by_key_b(|component_metadata| component_metadata.id);
-
- let archetype_id = ArchetypeId::from_components_metadata(&components_metadata);
-
- // This looks stupid but the borrow checker complains otherwise
- if self.archetype_lookup.borrow().contains_key(&archetype_id) {
- return self.iter_archetypes_by_lookup(archetype_id);
- }
-
- let comp_ids_set = create_non_opt_component_id_set(components_metadata.as_ref());
-
- let matching_archetype_indices = self
- .archetypes
- .iter()
- .enumerate()
- .filter_map(|(index, archetype)| {
- if archetype.component_ids_is_superset(&comp_ids_set) {
- return Some(index);
- }
-
- None
- })
- .collect();
-
- self.archetype_lookup.borrow_mut().insert(
- archetype_id,
- ArchetypeLookupEntry {
- component_ids: comp_ids_set,
- archetype_indices: matching_archetype_indices,
- },
- );
-
- self.iter_archetypes_by_lookup(archetype_id)
- }
-
- pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype>
- {
- let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?;
-
- let archetype_index =
- self.find_archetype_index_with_entity(*archetype_id, entity_uid)?;
-
- self.archetypes.get(archetype_index)
- }
-
- #[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
- pub fn push_entity(
- &mut self,
- entity_uid: Uid,
- mut components: Vec<Box<dyn Component>>,
- ) -> Result<(ArchetypeId, Uid), Error>
- {
- if self.entity_archetype_lookup.contains_key(&entity_uid) {
- return Err(Error::EntityAlreadyExists(entity_uid));
- }
-
- components.sort_by_key(|component| component.self_id());
-
- #[cfg(feature = "debug")]
- tracing::debug!(
- "Pushing entity with components: ({})",
- components
- .iter()
- .map(|component| component.type_name())
- .collect::<Vec<_>>()
- .join(", ")
- );
-
- let archetype_id = ArchetypeId::from_components_metadata(
- &components
- .iter()
- .map(|component| ComponentMetadata::get(&**component))
- .collect::<Vec<_>>(),
- );
-
- let comp_ids_set = create_non_opt_component_id_set(
- components
- .iter()
- .map(|component| ComponentMetadata::get(&**component)),
- );
-
- let archetype_index =
- self.get_or_create_archetype(archetype_id, &comp_ids_set, &components);
-
- self.populate_matching_archetype_lookup_entries(&comp_ids_set, archetype_index);
-
- let archetype = self
- .archetypes
- .get_mut(archetype_index)
- .expect("Archetype is gone");
-
- archetype.push_entity(entity_uid, components);
-
- archetype
- .entity_lookup
- .insert(entity_uid, archetype.entities.len() - 1);
-
- self.entity_archetype_lookup
- .insert(entity_uid, archetype_id);
-
- Ok((archetype_id, entity_uid))
- }
-
- pub fn add_components_to_entity(
- &mut self,
- entity_uid: Uid,
- components: Vec<Box<dyn Component>>,
- ) -> Option<()>
- {
- let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?;
-
- let archetype_index =
- self.find_archetype_index_with_entity(*archetype_id, entity_uid)?;
-
- let archetype = self.archetypes.get_mut(archetype_index)?;
-
- let contains_component_already = components
- .iter()
- .any(|component| archetype.component_ids.contains_key(&component.self_id()));
-
- if contains_component_already {
- let component_cnt = components.len();
-
- // TODO: return error
- panic!(
- "Entity with UID {:?} already contains one or more component(s) ({})",
- entity_uid,
- components
- .iter()
- .flat_map(|component| [component.type_name(), ", "])
- .enumerate()
- .take_while(|(index, _)| { *index < (component_cnt * 2) - 1 })
- .map(|(_, component)| component)
- .collect::<String>()
- );
- }
-
- let entity = archetype.take_entity(entity_uid)?;
-
- self.entity_archetype_lookup.remove(&entity_uid);
-
- self.push_entity(
- entity_uid,
- entity
- .components
- .into_iter()
- .map(|component| component.component.into_inner())
- .chain(components)
- .collect(),
- )
- .expect("Not supposed to return Err since the entity is removed");
-
- Some(())
- }
-
- pub fn remove_components_from_entity(
- &mut self,
- entity_uid: Uid,
- component_ids: impl IntoIterator<Item = Uid>,
- ) -> Option<()>
- {
- let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?;
-
- let archetype_index =
- self.find_archetype_index_with_entity(*archetype_id, entity_uid)?;
-
- let archetype = self.archetypes.get_mut(archetype_index)?;
-
- let entity = archetype.take_entity(entity_uid)?;
-
- let component_ids_set = component_ids.into_iter().collect::<HashSet<_>>();
-
- self.entity_archetype_lookup.remove(&entity_uid);
-
- self.push_entity(
- entity_uid,
- entity
- .components
- .into_iter()
- .map(|component| component.component.into_inner())
- .filter(|component| !component_ids_set.contains(&component.self_id()))
- .collect(),
- )
- .expect("Not supposed to return Err since the entity is removed");
-
- Some(())
- }
-
- fn populate_matching_archetype_lookup_entries(
- &mut self,
- comp_ids_set: &HashSet<Uid>,
- archetype_index: usize,
- )
- {
- let mut archetype_lookup = self.archetype_lookup.borrow_mut();
-
- for (_, lookup_entry) in archetype_lookup.iter_mut() {
- if &lookup_entry.component_ids == comp_ids_set {
- continue;
- }
-
- // There shouldn't be duplicate archetype indices in the lookup entry
- if lookup_entry.archetype_indices.contains(&archetype_index) {
- continue;
- }
-
- if lookup_entry.component_ids.is_subset(comp_ids_set) {
- lookup_entry.archetype_indices.push(archetype_index);
- }
- }
- }
-
- fn get_or_create_archetype(
- &mut self,
- archetype_id: ArchetypeId,
- comp_ids_set: &HashSet<Uid>,
- components: &[Box<dyn Component>],
- ) -> usize
- {
- let mut archetype_lookup = self.archetype_lookup.borrow_mut();
-
- let lookup_entry = archetype_lookup.entry(archetype_id).or_insert_with(|| {
- ArchetypeLookupEntry {
- component_ids: comp_ids_set.clone(),
- archetype_indices: Vec::with_capacity(1),
- }
- });
-
- if lookup_entry.archetype_indices.is_empty() {
- self.archetypes.push(Archetype::new(
- components.iter().map(|component| component.self_id()),
- ));
-
- lookup_entry
- .archetype_indices
- .push(self.archetypes.len() - 1);
- }
-
- // SAFETY: Above, we push a archetype index if archetype_indices is empty so this
- // cannot fail
- unsafe { *lookup_entry.archetype_indices.first().unwrap_unchecked() }
- }
-
- fn find_archetype_index_with_entity(
- &self,
- archetype_id: ArchetypeId,
- entity_uid: Uid,
- ) -> Option<usize>
- {
- let archetype_lookup = self.archetype_lookup.borrow_mut();
-
- let archetype_lookup_entry = archetype_lookup.get(&archetype_id)?;
-
- // TODO: Also have a hashmap for precise archetype ID -> archetype index lookup.
- // This way is slow
- archetype_lookup_entry
- .archetype_indices
- .iter()
- .find(|archetype_index| {
- let Some(archetype) = self.archetypes.get(**archetype_index) else {
- return false;
- };
-
- archetype.has_entity(entity_uid)
- })
- .copied()
- }
-
- fn iter_archetypes_by_lookup(&self, archetype_id: ArchetypeId)
- -> ArchetypeRefIter<'_>
- {
- let archetype_lookup = self.archetype_lookup.borrow();
-
- // The archetype indices have to be cloned to prevent a panic when query
- // iterations are nested. The panic happens because the archetype_lookup RefCell
- // is borrowed and it tries to mutably borrow it
- let archetype_indices = archetype_lookup
- .get(&archetype_id)
- .unwrap()
- .archetype_indices
- .clone();
-
- ArchetypeRefIter {
- indices: archetype_indices.into_iter(),
- archetypes: &self.archetypes,
- }
- }
-}
-
-/// Component storage error
-#[derive(Debug, Clone, thiserror::Error)]
-pub enum Error
-{
- #[error("Entity already exists")]
- EntityAlreadyExists(Uid),
-}
-
-impl TypeName for Storage
-{
- fn type_name(&self) -> &'static str
- {
- type_name::<Self>()
- }
-}
-
-#[derive(Debug)]
-struct ArchetypeLookupEntry
-{
- component_ids: HashSet<Uid>,
- archetype_indices: Vec<usize>,
-}
-
-#[derive(Debug)]
-pub struct Archetype
-{
- component_ids: HashMap<Uid, usize>,
- entity_lookup: HashMap<Uid, usize>,
- entities: Vec<ArchetypeEntity>,
-}
-
-impl Archetype
-{
- fn new(component_ids: impl IntoIterator<Item = Uid>) -> Self
- {
- Self {
- component_ids: component_ids
- .into_iter()
- .enumerate()
- .map(|(index, component_id)| (component_id, index))
- .collect(),
- entity_lookup: HashMap::new(),
- entities: Vec::new(),
- }
- }
-
- pub fn component_ids_is_superset(&self, other_component_ids: &HashSet<Uid>) -> bool
- {
- if other_component_ids.len() <= self.component_ids.len() {
- other_component_ids
- .iter()
- .all(|v| self.component_ids.contains_key(v))
- } else {
- false
- }
- }
-
- pub fn get_entity(&self, entity_uid: Uid) -> Option<&ArchetypeEntity>
- {
- let entity_index = *self.entity_lookup.get(&entity_uid)?;
-
- self.entities.get(entity_index)
- }
-
- pub fn entities(&self) -> EntityIter<'_>
- {
- EntityIter { iter: self.entities.iter() }
- }
-
- pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize>
- {
- self.component_ids.get(&component_id).copied()
- }
-
- fn push_entity(
- &mut self,
- entity_uid: Uid,
- components: impl IntoIterator<Item = Box<dyn Component>>,
- )
- {
- self.entities.push(ArchetypeEntity {
- uid: entity_uid,
- components: components.into_iter().map(Into::into).collect(),
- });
- }
-
- pub fn take_entity(&mut self, entity_uid: Uid) -> Option<ArchetypeEntity>
- {
- let entity_index = self.entity_lookup.remove(&entity_uid)?;
-
- let entity = self.entities.remove(entity_index);
-
- for index in self.entity_lookup.values_mut() {
- if *index > entity_index {
- *index -= 1;
- }
- }
-
- Some(entity)
- }
-
- fn has_entity(&self, entity_uid: Uid) -> bool
- {
- self.entity_lookup.contains_key(&entity_uid)
- }
-}
-
-#[derive(Debug)]
-pub struct ArchetypeEntity
-{
- uid: Uid,
- components: Vec<EntityComponent>,
-}
-
-impl ArchetypeEntity
-{
- pub fn uid(&self) -> Uid
- {
- self.uid
- }
-
- pub fn components(&self) -> &[EntityComponent]
- {
- &self.components
- }
-
- pub fn get_component(&self, index: usize) -> Option<&EntityComponent>
- {
- self.components.get(index)
- }
-}
-
-#[derive(Debug)]
-pub struct ArchetypeRefIter<'component_storage>
-{
- indices: OwnedVecIter<usize>,
- archetypes: &'component_storage [Archetype],
-}
-
-impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage>
-{
- type Item = &'component_storage Archetype;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let archetype_index = self.indices.next()?;
-
- Some(
- self.archetypes
- .get(archetype_index)
- .expect("Archetype index in archetype lookup entry was not found"),
- )
- }
-}
-
-#[derive(Debug)]
-pub struct EntityIter<'archetype>
-{
- iter: SliceIter<'archetype, ArchetypeEntity>,
-}
-
-impl<'archetype> Iterator for EntityIter<'archetype>
-{
- type Item = &'archetype ArchetypeEntity;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- self.iter.next()
- }
-}
-
-fn create_non_opt_component_id_set<Item>(
- component_metadata_iter: impl IntoIterator<Item = Item>,
-) -> HashSet<Uid>
-where
- Item: Borrow<ComponentMetadata>,
-{
- component_metadata_iter
- .into_iter()
- .filter_map(|item| {
- let component_metadata = item.borrow();
-
- if component_metadata.is_optional == ComponentIsOptional::Yes {
- return None;
- }
-
- Some(component_metadata.id)
- })
- .collect::<HashSet<_>>()
-}
-
-#[cfg(test)]
-mod tests
-{
-
- use ecs_macros::Component;
-
- use super::Storage;
- use crate::archetype::Id as ArchetypeId;
- use crate::component::{
- Component,
- IsOptional as ComponentIsOptional,
- Metadata as ComponentMetadata,
- };
- use crate::uid::{Kind as UidKind, Uid};
-
- #[derive(Debug, Component)]
- struct HealthPotion
- {
- _hp_restoration: u32,
- }
-
- #[derive(Debug, Component)]
- struct Hookshot
- {
- _range: u32,
- }
-
- #[derive(Debug, Component)]
- struct DekuNut
- {
- _throwing_damage: u32,
- }
-
- #[derive(Debug, Component)]
- struct Bow
- {
- _damage: u32,
- }
-
- #[derive(Debug, Component)]
- struct IronBoots;
-
- #[test]
- fn push_entity_works()
- {
- let mut component_storage = Storage::default();
-
- component_storage
- .push_entity(
- Uid::new_unique(UidKind::Entity),
- vec![
- Box::new(HealthPotion { _hp_restoration: 12 }),
- Box::new(Hookshot { _range: 50 }),
- ],
- )
- .expect("Expected Ok");
-
- assert_eq!(component_storage.archetypes.len(), 1);
-
- let archetype = component_storage
- .archetypes
- .first()
- .expect("Expected a archetype in archetypes Vec");
-
- assert_eq!(archetype.component_ids.len(), 2);
-
- // One entity
- assert_eq!(archetype.entities.len(), 1);
-
- let entity_components = archetype
- .entities
- .first()
- .expect("Expected a entity in archetype");
-
- assert_eq!(entity_components.components.len(), 2);
-
- assert_eq!(component_storage.archetype_lookup.borrow().len(), 1);
-
- let mut components_metadata = [
- ComponentMetadata {
- id: HealthPotion::id(),
- is_optional: ComponentIsOptional::No,
- },
- ComponentMetadata {
- id: Hookshot::id(),
- is_optional: ComponentIsOptional::No,
- },
- ];
-
- components_metadata.sort_by_key(|comp_metadata| comp_metadata.id);
-
- let archetype_lookup = component_storage.archetype_lookup.borrow();
-
- let lookup_entry = archetype_lookup
- .get(&ArchetypeId::from_components_metadata(&components_metadata))
- .expect("Expected entry in archetype lookup map");
-
- let first_archetype_index = lookup_entry
- .archetype_indices
- .first()
- .expect("Expected archetype lookup to contain a archetype reference");
-
- assert_eq!(*first_archetype_index, 0);
- }
-}