From d50a2f6e63c25adf3b64652310c423717bd3966f Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 16 Jun 2024 13:17:57 +0200 Subject: refactor(ecs): add component ID struct --- ecs/src/component.rs | 37 +++++++++++++++++++++----- ecs/src/component/local.rs | 10 +++---- ecs/src/component/storage.rs | 62 ++++++++++++++++++++++---------------------- ecs/src/lib.rs | 4 +-- ecs/src/query.rs | 25 ++++++++---------- ecs/src/system/stateful.rs | 8 +++--- 6 files changed, 84 insertions(+), 62 deletions(-) diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 5c0b9ce..512c60d 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -15,7 +15,7 @@ pub(crate) mod storage; pub trait Component: SystemInput + Any + TypeName { /// The component type in question. Will usually be `Self` - type Component + type Component: Component where Self: Sized; @@ -23,6 +23,9 @@ pub trait Component: SystemInput + Any + TypeName where Self: Sized; + /// Returns the ID of this component's type. + fn id(&self) -> Id; + #[doc(hidden)] fn as_any_mut(&mut self) -> &mut dyn Any; @@ -76,6 +79,11 @@ where type Component = ComponentT; type RefMut<'component> = Option>; + fn id(&self) -> Id + { + Id::of::() + } + fn as_any_mut(&mut self) -> &mut dyn Any { self @@ -104,6 +112,23 @@ where impl SystemInput for Option where ComponentT: Component {} +/// The ID of a [`Component`] type. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id +{ + inner: TypeId, +} + +impl Id +{ + pub fn of() -> Self + where + ComponentT: Component, + { + Self { inner: TypeId::of::() } + } +} + /// A sequence of components. pub trait Sequence { @@ -113,7 +138,7 @@ pub trait Sequence fn into_vec(self) -> Vec>; - fn type_ids() -> Vec<(TypeId, IsOptional)>; + fn ids() -> Vec<(Id, IsOptional)>; fn from_components<'component>( components: impl Iterator, @@ -145,7 +170,7 @@ impl From for IsOptional /// Will return `true` if the component is a [`Option`]. pub fn is_optional() -> bool { - if TypeId::of::() == TypeId::of::>() { + if Id::of::() == Id::of::>() { return true; } @@ -174,11 +199,11 @@ macro_rules! inner { Vec::from_iter([#(Box::new(self.I) as Box,)*]) } - fn type_ids() -> Vec<(TypeId, IsOptional)> + fn ids() -> Vec<(Id, IsOptional)> { vec![ #( - (TypeId::of::(), is_optional::().into()), + (Id::of::(), is_optional::().into()), )* ] } @@ -193,7 +218,7 @@ macro_rules! inner { for comp in components { #( - if comp.id == TypeId::of::() { + if comp.id == Id::of::() { comp_~I = Some(lock_component(comp)); continue; } diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index e1a0c1f..89c3139 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,7 +1,7 @@ -use std::any::{Any, TypeId}; +use std::any::Any; use std::ops::{Deref, DerefMut}; -use crate::component::Component; +use crate::component::{Component, Id}; use crate::system::{ComponentRefMut, Param as SystemParam, System}; use crate::WorldData; @@ -43,16 +43,16 @@ where { let other_comparable = Other::get_comparable(); - let Some(other_type_id) = other_comparable.downcast_ref::() else { + let Some(other_id) = other_comparable.downcast_ref::() else { return true; }; - TypeId::of::() != *other_type_id + Id::of::() != *other_id } fn get_comparable() -> Box { - Box::new(TypeId::of::()) + Box::new(Id::of::()) } } diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index cc9e911..bd53da0 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -1,9 +1,9 @@ -use std::any::{type_name, TypeId}; +use std::any::type_name; use std::collections::{HashMap, HashSet}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::NonNull; -use crate::component::{Component, IsOptional as ComponentIsOptional}; +use crate::component::{Component, Id as ComponentId, IsOptional as ComponentIsOptional}; use crate::lock::Lock; use crate::type_name::TypeName; use crate::EntityComponent; @@ -13,14 +13,14 @@ pub struct ComponentStorage { archetypes: Vec, archetype_lookup: HashMap>>, - pending_archetype_lookup_entries: Vec>, + pending_archetype_lookup_entries: Vec>, } impl ComponentStorage { pub fn find_entities( &self, - component_ids: &[(TypeId, ComponentIsOptional)], + component_ids: &[(ComponentId, ComponentIsOptional)], ) -> Option<&[&Archetype]> { let ids = component_ids @@ -63,7 +63,7 @@ impl ComponentStorage components .iter() .filter(|component| !component.is_optional()) - .map(|component| (*component).type_id()), + .map(|component| component.id()), )) .or_insert_with(|| { self.archetypes.push(Archetype::default()); @@ -83,13 +83,13 @@ impl ComponentStorage archetype .component_ids - .extend(components.iter().map(|component| (*component).type_id())); + .extend(components.iter().map(|component| component.id())); archetype.components.push( components .into_iter() .map(|component| EntityComponent { - id: (*component).type_id(), + id: component.id(), name: component.type_name(), component: Lock::new(component), }) @@ -97,7 +97,7 @@ impl ComponentStorage ); } - pub fn add_archetype_lookup_entry(&mut self, component_ids: &[TypeId]) + pub fn add_archetype_lookup_entry(&mut self, component_ids: &[ComponentId]) { self.pending_archetype_lookup_entries .push(component_ids.to_vec()); @@ -142,7 +142,7 @@ impl TypeName for ComponentStorage #[derive(Debug, Default)] pub struct Archetype { - component_ids: HashSet, + component_ids: HashSet, pub components: Vec>, } @@ -154,7 +154,7 @@ struct ArchetypeComponentsHash impl ArchetypeComponentsHash { - fn new(component_ids: impl IntoIterator) -> Self + fn new(component_ids: impl IntoIterator) -> Self { let mut hasher = DefaultHasher::new(); @@ -181,13 +181,13 @@ const unsafe fn nonnull_slice_to_ref_slice(slice: &[NonNull]) -> &[& #[cfg(test)] mod tests { - use std::any::TypeId; use std::collections::HashSet; use std::ptr::addr_of; use ecs_macros::Component; use super::{Archetype, ArchetypeComponentsHash, ComponentStorage}; + use crate::component::Id as ComponentId; use crate::lock::Lock; use crate::{self as ecs, EntityComponent}; @@ -252,8 +252,8 @@ mod tests let lookup = component_storage .archetype_lookup .get(&ArchetypeComponentsHash::new([ - TypeId::of::(), - TypeId::of::(), + ComponentId::of::(), + ComponentId::of::(), ])) .expect("Expected entry in archetype lookup map"); @@ -274,23 +274,23 @@ mod tests component_storage.archetypes.push(Archetype { component_ids: HashSet::from([ - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), + ComponentId::of::(), + ComponentId::of::(), + ComponentId::of::(), ]), components: vec![ vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Iron boots", component: Lock::new(Box::new(IronBoots)), }], vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Health potion", component: Lock::new(Box::new(HealthPotion { _hp_restoration: 20 })), }], vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Hookshot", component: Lock::new(Box::new(Hookshot { _range: 67 })), }], @@ -299,29 +299,29 @@ mod tests component_storage.archetypes.push(Archetype { component_ids: HashSet::from([ - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), - TypeId::of::(), + ComponentId::of::(), + ComponentId::of::(), + ComponentId::of::(), + ComponentId::of::(), ]), components: vec![ vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Deku nut", component: Lock::new(Box::new(DekuNut { _throwing_damage: 5 })), }], vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Iron boots", component: Lock::new(Box::new(IronBoots)), }], vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Bow", component: Lock::new(Box::new(Bow { _damage: 20 })), }], vec![EntityComponent { - id: TypeId::of::(), + id: ComponentId::of::(), name: "Hookshot", component: Lock::new(Box::new(Hookshot { _range: 67 })), }], @@ -329,8 +329,8 @@ mod tests }); component_storage.add_archetype_lookup_entry(&[ - TypeId::of::(), - TypeId::of::(), + ComponentId::of::(), + ComponentId::of::(), ]); assert_eq!(component_storage.pending_archetype_lookup_entries.len(), 1); @@ -342,8 +342,8 @@ mod tests let archetypes = component_storage .archetype_lookup .get(&ArchetypeComponentsHash::new([ - TypeId::of::(), - TypeId::of::(), + ComponentId::of::(), + ComponentId::of::(), ])) .expect(concat!( "Expected a archetype for IronBoots & Hookshot to be found in the ", diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 0dd1c02..e427150 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -11,7 +11,7 @@ use std::vec::Drain; use crate::actions::Action; use crate::component::storage::ComponentStorage; -use crate::component::{Component, Sequence as ComponentSequence}; +use crate::component::{Component, Id as ComponentId, 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; @@ -212,7 +212,7 @@ pub struct WorldData #[non_exhaustive] pub struct EntityComponent { - pub id: TypeId, + pub id: ComponentId, pub name: &'static str, pub component: Lock>, } diff --git a/ecs/src/query.rs b/ecs/src/query.rs index de6c832..a2edc4d 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -1,4 +1,4 @@ -use std::any::{type_name, Any, TypeId}; +use std::any::{type_name, Any}; use std::collections::HashSet; use std::iter::{Flatten, Map}; use std::marker::PhantomData; @@ -7,6 +7,7 @@ use std::sync::{Arc, Weak}; use crate::component::storage::Archetype; use crate::component::{ + Id as ComponentId, IsOptional as ComponentIsOptional, Sequence as ComponentSequence, }; @@ -43,7 +44,7 @@ where ComponentIter { entities: self .component_storage - .find_entities(&Comps::type_ids()) + .find_entities(&Comps::ids()) .unwrap_or_else(|| panic!("Could not find {:?}", type_name::())) .iter() .map((|archetype| archetype.components.as_slice()) as ComponentIterMapFn) @@ -124,9 +125,7 @@ where fn get_comparable() -> Box { - Box::new(QueryComponentIds { - component_type_ids: Comps::type_ids(), - }) + Box::new(QueryComponentIds { component_ids: Comps::ids() }) } fn handle_pre_run(world_data: &WorldData) @@ -143,7 +142,7 @@ where ); component_storage_lock.add_archetype_lookup_entry( - &Comps::type_ids() + &Comps::ids() .into_iter() .filter_map(|(component_id, is_optional)| { if is_optional == ComponentIsOptional::Yes { @@ -242,7 +241,7 @@ where #[derive(Debug)] struct QueryComponentIds { - component_type_ids: Vec<(TypeId, ComponentIsOptional)>, + component_ids: Vec<(ComponentId, ComponentIsOptional)>, } impl QueryComponentIds @@ -251,15 +250,13 @@ impl QueryComponentIds where OtherComps: ComponentSequence, { - let other_component_type_ids = OtherComps::type_ids() + let other_ids = OtherComps::ids() .into_iter() - .map(|(type_id, _)| type_id) - .collect::>(); + .map(|(id, _)| id) + .collect::>(); - self.component_type_ids + self.component_ids .iter() - .all(|(component_type_id, _)| { - other_component_type_ids.contains(component_type_id) - }) + .all(|(id, _)| other_ids.contains(id)) } } diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index b765ff0..ae6a5b5 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -4,7 +4,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe}; use seq_macro::seq; -use crate::component::Component; +use crate::component::{Component, Id as ComponentId}; use crate::lock::Lock; use crate::system::util::check_params_are_compatible; use crate::system::{ @@ -27,7 +27,7 @@ use crate::WorldData; pub struct Stateful { func: Func, - local_components: HashMap>>, + local_components: HashMap>>, } macro_rules! impl_system { @@ -134,7 +134,7 @@ macro_rules! impl_system { ) -> Option> { let local_component = self.local_components - .get(&TypeId::of::())? + .get(&ComponentId::of::())? .write_nonblock() .expect("Failed to aquire read-write local component lock"); @@ -148,7 +148,7 @@ macro_rules! impl_system { { self.local_components .insert( - TypeId::of::(), + ComponentId::of::(), Lock::new(Box::new(local_component)) ); } -- cgit v1.2.3-18-g5258