summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-08-01 12:36:35 +0200
committerHampusM <hampus@hampusmat.com>2024-08-01 12:36:35 +0200
commit70c7d745f918dd23343599963a619539f4f880cb (patch)
tree076779b4364649fcea76ce07a6cd1d6b8d6956f2
parenta5c45dab18399461aff5dc13c471ea6c5ec80c34 (diff)
refactor(ecs): add & use component metadata struct
-rw-r--r--ecs/src/archetype.rs25
-rw-r--r--ecs/src/component.rs29
-rw-r--r--ecs/src/component/storage.rs93
-rw-r--r--ecs/src/query.rs31
4 files changed, 118 insertions, 60 deletions
diff --git a/ecs/src/archetype.rs b/ecs/src/archetype.rs
index a33de66..808d006 100644
--- a/ecs/src/archetype.rs
+++ b/ecs/src/archetype.rs
@@ -1,6 +1,10 @@
use std::hash::{DefaultHasher, Hash, Hasher};
-use crate::component::Id as ComponentId;
+use crate::component::{
+ Id as ComponentId,
+ IsOptional as ComponentIsOptional,
+ Metadata as ComponentMetadata,
+};
/// Archetype ID.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
@@ -11,8 +15,25 @@ pub struct Id
impl Id
{
+ pub fn from_components_metadata(
+ components_metadata: impl IntoIterator<Item = ComponentMetadata>,
+ ) -> Self
+ {
+ Self::new(
+ components_metadata
+ .into_iter()
+ .filter_map(|component_metadata| {
+ if component_metadata.is_optional == ComponentIsOptional::Yes {
+ return None;
+ }
+
+ Some(component_metadata.id)
+ }),
+ )
+ }
+
/// Returns the ID of a archetype with the given components.
- pub fn new(component_ids: impl IntoIterator<Item = ComponentId>) -> Self
+ fn new(component_ids: impl IntoIterator<Item = ComponentId>) -> Self
{
let mut hasher = DefaultHasher::new();
diff --git a/ecs/src/component.rs b/ecs/src/component.rs
index be5756f..67ae453 100644
--- a/ecs/src/component.rs
+++ b/ecs/src/component.rs
@@ -139,13 +139,33 @@ pub trait Sequence
fn into_vec(self) -> Vec<Box<dyn Component>>;
- fn ids() -> Vec<(Id, IsOptional)>;
+ fn metadata() -> Vec<Metadata>;
fn from_components<'component>(
components: impl Iterator<Item = &'component EntityComponent>,
) -> Self::Refs<'component>;
}
+/// [`Component`] metadata.
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Metadata
+{
+ pub id: Id,
+ pub is_optional: IsOptional,
+}
+
+impl Metadata
+{
+ pub fn of<ComponentT: Component + ?Sized>(component: &ComponentT) -> Self
+ {
+ Self {
+ id: component.id(),
+ is_optional: component.is_optional().into(),
+ }
+ }
+}
+
/// Whether or not a `Component` is optional.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsOptional
@@ -201,11 +221,14 @@ macro_rules! inner {
Vec::from_iter([#(Box::new(self.I) as Box<dyn Component>,)*])
}
- fn ids() -> Vec<(Id, IsOptional)>
+ fn metadata() -> Vec<Metadata>
{
vec![
#(
- (Id::of::<Comp~I>(), is_optional::<Comp~I>().into()),
+ Metadata {
+ id: Id::of::<Comp~I>(),
+ is_optional: is_optional::<Comp~I>().into()
+ },
)*
]
}
diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs
index 70981a3..76701ac 100644
--- a/ecs/src/component/storage.rs
+++ b/ecs/src/component/storage.rs
@@ -3,7 +3,12 @@ use std::collections::{HashMap, HashSet};
use std::slice::Iter as SliceIter;
use crate::archetype::Id as ArchetypeId;
-use crate::component::{Component, Id as ComponentId, IsOptional as ComponentIsOptional};
+use crate::component::{
+ Component,
+ Id as ComponentId,
+ IsOptional as ComponentIsOptional,
+ Metadata as ComponentMetadata,
+};
use crate::lock::Lock;
use crate::type_name::TypeName;
use crate::EntityComponent;
@@ -13,28 +18,18 @@ pub struct Storage
{
archetypes: Vec<Archetype>,
archetype_lookup: HashMap<ArchetypeId, Vec<usize>>,
- pending_archetype_lookup_entries: Vec<Vec<ComponentId>>,
+ pending_archetype_lookup_entries: Vec<Vec<ComponentMetadata>>,
}
impl Storage
{
pub fn find_entities(
&self,
- component_ids: &[(ComponentId, ComponentIsOptional)],
+ components_metadata: impl IntoIterator<Item = ComponentMetadata>,
) -> ArchetypeRefIter<'_>
{
- let ids = component_ids
- .iter()
- .filter_map(|(component_id, is_optional)| {
- if *is_optional == ComponentIsOptional::Yes {
- return None;
- }
-
- Some(*component_id)
- });
-
self.archetype_lookup
- .get(&ArchetypeId::new(ids))
+ .get(&ArchetypeId::from_components_metadata(components_metadata))
.map_or_else(ArchetypeRefIter::new_empty, |archetypes_indices| {
ArchetypeRefIter {
inner: archetypes_indices.iter(),
@@ -58,11 +53,10 @@ impl Storage
let archetype_indices = self
.archetype_lookup
- .entry(ArchetypeId::new(
+ .entry(ArchetypeId::from_components_metadata(
components
.iter()
- .filter(|component| !component.is_optional())
- .map(|component| component.id()),
+ .map(|component| ComponentMetadata::of(&**component)),
))
.or_insert_with(|| {
self.archetypes.push(Archetype::new(
@@ -102,10 +96,13 @@ impl Storage
);
}
- pub fn add_archetype_lookup_entry(&mut self, component_ids: &[ComponentId])
+ pub fn add_archetype_lookup_entry(
+ &mut self,
+ components_metadata: impl IntoIterator<Item = ComponentMetadata>,
+ )
{
self.pending_archetype_lookup_entries
- .push(component_ids.to_vec());
+ .push(components_metadata.into_iter().collect());
}
pub fn make_archetype_lookup_entries(&mut self)
@@ -115,14 +112,22 @@ impl Storage
self.archetype_lookup.clear();
for pending_entry in self.pending_archetype_lookup_entries.drain(..) {
- let components_set: HashSet<_> = pending_entry.iter().copied().collect();
+ let ids = pending_entry.iter().filter_map(|component_metadata| {
+ if component_metadata.is_optional == ComponentIsOptional::Yes {
+ return None;
+ }
+
+ Some(component_metadata.id)
+ });
+
+ let ids_set: HashSet<_> = ids.collect();
let matching_archetype_indices = self
.archetypes
.iter()
.enumerate()
.filter_map(|(index, archetype)| {
- if archetype.component_ids.is_superset(&components_set) {
+ if archetype.component_ids.is_superset(&ids_set) {
return Some(index);
}
@@ -131,7 +136,9 @@ impl Storage
let archetype_indices = self
.archetype_lookup
- .entry(ArchetypeId::new(pending_entry.into_iter()))
+ .entry(ArchetypeId::from_components_metadata(
+ pending_entry.into_iter(),
+ ))
.or_default();
archetype_indices.extend(matching_archetype_indices);
@@ -205,7 +212,11 @@ mod tests
use super::{Archetype, Storage};
use crate::archetype::Id as ArchetypeId;
- use crate::component::Id as ComponentId;
+ use crate::component::{
+ Id as ComponentId,
+ IsOptional as ComponentIsOptional,
+ Metadata as ComponentMetadata,
+ };
use crate::lock::Lock;
use crate::{self as ecs, EntityComponent};
@@ -269,9 +280,15 @@ mod tests
let lookup = component_storage
.archetype_lookup
- .get(&ArchetypeId::new([
- ComponentId::of::<HealthPotion>(),
- ComponentId::of::<Hookshot>(),
+ .get(&ArchetypeId::from_components_metadata([
+ ComponentMetadata {
+ id: ComponentId::of::<HealthPotion>(),
+ is_optional: ComponentIsOptional::No,
+ },
+ ComponentMetadata {
+ id: ComponentId::of::<Hookshot>(),
+ is_optional: ComponentIsOptional::No,
+ },
]))
.expect("Expected entry in archetype lookup map");
@@ -343,9 +360,15 @@ mod tests
],
});
- component_storage.add_archetype_lookup_entry(&[
- ComponentId::of::<IronBoots>(),
- ComponentId::of::<Hookshot>(),
+ component_storage.add_archetype_lookup_entry([
+ ComponentMetadata {
+ id: ComponentId::of::<IronBoots>(),
+ is_optional: ComponentIsOptional::No,
+ },
+ ComponentMetadata {
+ id: ComponentId::of::<Hookshot>(),
+ is_optional: ComponentIsOptional::No,
+ },
]);
assert_eq!(component_storage.pending_archetype_lookup_entries.len(), 1);
@@ -356,9 +379,15 @@ mod tests
let archetypes = component_storage
.archetype_lookup
- .get(&ArchetypeId::new([
- ComponentId::of::<IronBoots>(),
- ComponentId::of::<Hookshot>(),
+ .get(&ArchetypeId::from_components_metadata([
+ ComponentMetadata {
+ id: ComponentId::of::<IronBoots>(),
+ is_optional: ComponentIsOptional::No,
+ },
+ ComponentMetadata {
+ id: ComponentId::of::<Hookshot>(),
+ is_optional: ComponentIsOptional::No,
+ },
]))
.expect(concat!(
"Expected a archetype for IronBoots & Hookshot to be found in the ",
diff --git a/ecs/src/query.rs b/ecs/src/query.rs
index 2c52206..bc98ac0 100644
--- a/ecs/src/query.rs
+++ b/ecs/src/query.rs
@@ -9,11 +9,7 @@ use crate::component::storage::{
ArchetypeRefIter,
Storage as ComponentStorage,
};
-use crate::component::{
- Id as ComponentId,
- IsOptional as ComponentIsOptional,
- Sequence as ComponentSequence,
-};
+use crate::component::{Metadata as ComponentMetadata, Sequence as ComponentSequence};
use crate::lock::{Lock, ReadGuard};
use crate::query::options::Options;
use crate::system::{
@@ -52,7 +48,7 @@ where
ComponentIter {
entities: self
.component_storage
- .find_entities(&Comps::ids())
+ .find_entities(Comps::metadata())
.map((|archetype| archetype.components.as_slice()) as ComponentIterMapFn)
.flatten()
.filter(|components| OptionsT::entity_filter(*components)),
@@ -124,7 +120,7 @@ where
fn get_comparable() -> Box<dyn Any>
{
- Box::new(QueryComponentIds { component_ids: Comps::ids() })
+ Box::new(QueryComponentIds { component_ids: Comps::metadata() })
}
fn prepare(world_data: &WorldData)
@@ -140,18 +136,7 @@ where
std::any::type_name::<Comps>()
);
- component_storage_lock.add_archetype_lookup_entry(
- &Comps::ids()
- .into_iter()
- .filter_map(|(component_id, is_optional)| {
- if is_optional == ComponentIsOptional::Yes {
- return None;
- }
-
- Some(component_id)
- })
- .collect::<Vec<_>>(),
- );
+ component_storage_lock.add_archetype_lookup_entry(Comps::metadata());
}
}
@@ -183,7 +168,7 @@ where
#[derive(Debug)]
struct QueryComponentIds
{
- component_ids: Vec<(ComponentId, ComponentIsOptional)>,
+ component_ids: Vec<ComponentMetadata>,
}
impl QueryComponentIds
@@ -192,13 +177,13 @@ impl QueryComponentIds
where
OtherComps: ComponentSequence,
{
- let other_ids = OtherComps::ids()
+ let other_ids = OtherComps::metadata()
.into_iter()
- .map(|(id, _)| id)
+ .map(|component_metadata| component_metadata.id)
.collect::<HashSet<_>>();
self.component_ids
.iter()
- .all(|(id, _)| other_ids.contains(id))
+ .all(|component_metadata| other_ids.contains(&component_metadata.id))
}
}