From daf0bc236df25c0e9f44bc3e30839c16cda3f638 Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 11 Nov 2024 00:11:22 +0100 Subject: refactor(ecs): use same ID for entities & components --- ecs/src/actions.rs | 14 ++++--- ecs/src/archetype.rs | 4 +- ecs/src/component.rs | 89 +++++++++++++++++++++----------------------- ecs/src/component/local.rs | 9 +++-- ecs/src/component/storage.rs | 72 +++++++++++++++++------------------ ecs/src/entity.rs | 37 ++---------------- ecs/src/event/component.rs | 11 +++--- ecs/src/lib.rs | 24 +++++++----- ecs/src/query.rs | 4 +- ecs/src/query/options.rs | 4 +- ecs/src/relationship.rs | 52 +++++++++++++++++--------- ecs/src/system/stateful.rs | 9 +++-- ecs/src/uid.rs | 42 +++++++++++++++++++++ 13 files changed, 202 insertions(+), 169 deletions(-) create mode 100644 ecs/src/uid.rs diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index 5cd7b00..72cc95d 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -7,8 +7,8 @@ use crate::component::{ Metadata as ComponentMetadata, Sequence as ComponentSequence, }; -use crate::entity::Uid as EntityUid; use crate::system::{NoInitParamFlag, Param as SystemParam, System}; +use crate::uid::{Kind as UidKind, Uid}; use crate::{ActionQueue, World}; /// Used to to queue up actions for a [`World`] to perform. @@ -28,19 +28,23 @@ impl<'world> Actions<'world> } /// Adds component(s) to a entity. - pub fn add_components(&mut self, entity_uid: EntityUid, components: Comps) + pub fn add_components(&mut self, entity_uid: Uid, components: Comps) where Comps: ComponentSequence, { + debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + self.action_queue .push(Action::AddComponents(entity_uid, components.into_vec())); } /// Removes component(s) from a entity. - pub fn remove_components(&mut self, entity_uid: EntityUid) + pub fn remove_components(&mut self, entity_uid: Uid) where Comps: ComponentSequence, { + debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + self.action_queue .push(Action::RemoveComponents(entity_uid, Comps::metadata())); } @@ -149,8 +153,8 @@ impl<'weak_ref> Ref<'weak_ref> pub(crate) enum Action { Spawn(Vec>), - AddComponents(EntityUid, Vec>), - RemoveComponents(EntityUid, Vec), + AddComponents(Uid, Vec>), + RemoveComponents(Uid, Vec), Stop, } diff --git a/ecs/src/archetype.rs b/ecs/src/archetype.rs index 808d006..5c104b7 100644 --- a/ecs/src/archetype.rs +++ b/ecs/src/archetype.rs @@ -1,10 +1,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use crate::component::{ - Id as ComponentId, IsOptional as ComponentIsOptional, Metadata as ComponentMetadata, }; +use crate::uid::Uid; /// Archetype ID. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -33,7 +33,7 @@ impl Id } /// Returns the ID of a archetype with the given components. - fn new(component_ids: impl IntoIterator) -> Self + fn new(component_ids: impl IntoIterator) -> Self { let mut hasher = DefaultHasher::new(); diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 0506346..e1f2858 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,8 +1,9 @@ -use std::any::{type_name, Any, TypeId}; +use std::any::{type_name, Any}; use std::fmt::Debug; use seq_macro::seq; +use crate::uid::Uid; use crate::lock::{ReadGuard, WriteGuard}; use crate::system::{ComponentRef, ComponentRefMut, Input as SystemInput}; use crate::type_name::TypeName; @@ -27,8 +28,13 @@ pub trait Component: SystemInput + Any + TypeName where Self: Sized; - /// Returns the ID of this component's type. - fn id(&self) -> Id; + /// Returns the ID of this component. + fn id() -> Uid + where + Self: Sized; + + /// The ID of the component `self`. Returns the same value as [`Component::id`]. + fn self_id(&self) -> Uid; #[doc(hidden)] fn as_any_mut(&mut self) -> &mut dyn Any; @@ -36,9 +42,19 @@ pub trait Component: SystemInput + Any + TypeName #[doc(hidden)] fn as_any(&self) -> &dyn Any; - fn is_optional(&self) -> bool + /// Whether the component `self` is optional. Returns the same value as + /// [`Component::is_optional`]. + fn self_is_optional(&self) -> IsOptional { - false + IsOptional::No + } + + /// Returns whether this component is optional. + fn is_optional() -> IsOptional + where + Self: Sized, + { + IsOptional::No } } @@ -84,9 +100,14 @@ where type Ref<'component> = Option>; type RefMut<'component> = Option>; - fn id(&self) -> Id + fn id() -> Uid + { + ComponentT::id() + } + + fn self_id(&self) -> Uid { - Id::of::() + Self::id() } fn as_any_mut(&mut self) -> &mut dyn Any @@ -99,9 +120,14 @@ where self } - fn is_optional(&self) -> bool + fn self_is_optional(&self) -> IsOptional + { + Self::is_optional() + } + + fn is_optional() -> IsOptional { - true + IsOptional::Yes } } @@ -117,24 +143,6 @@ 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 -{ - #[must_use] - pub fn of() -> Self - where - ComponentT: Component, - { - Self { inner: TypeId::of::() } - } -} - /// A sequence of components. pub trait Sequence { @@ -172,7 +180,7 @@ pub trait Sequence #[non_exhaustive] pub struct Metadata { - pub id: Id, + pub id: Uid, pub is_optional: IsOptional, } @@ -181,8 +189,8 @@ impl Metadata pub fn of(component: &ComponentT) -> Self { Self { - id: component.id(), - is_optional: component.is_optional().into(), + id: component.self_id(), + is_optional: component.self_is_optional(), } } } @@ -207,19 +215,6 @@ impl From for IsOptional } } -/// Returns whether the given component type is a optional component. -/// -/// Will return `true` if the component is a [`Option`]. -#[must_use] -pub fn is_optional() -> bool -{ - if Id::of::() == Id::of::>() { - return true; - } - - false -} - pub trait FromOptionalMut<'comp> { fn from_optional_mut_component( @@ -260,8 +255,8 @@ macro_rules! inner { vec![ #( Metadata { - id: Id::of::(), - is_optional: is_optional::().into() + id: Comp~I::id(), + is_optional: Comp~I::is_optional() }, )* ] @@ -281,7 +276,7 @@ macro_rules! inner { for comp in components { #( - if comp.id == Id::of::() { + if comp.id == Comp~I::Component::id() { comp_~I = Some(lock_component(comp)); continue; } @@ -308,7 +303,7 @@ macro_rules! inner { for comp in components { #( - if comp.id == Id::of::() { + if comp.id == Comp~I::Component::id() { comp_~I = Some(lock_component(comp)); continue; } diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index a365efe..9d32d47 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,7 +1,8 @@ use std::any::Any; use std::ops::{Deref, DerefMut}; -use crate::component::{Component, Id}; +use crate::component::Component; +use crate::uid::Uid; use crate::system::{ComponentRefMut, Param as SystemParam, System}; use crate::World; @@ -43,16 +44,16 @@ where { let other_comparable = Other::get_comparable(); - let Some(other_id) = other_comparable.downcast_ref::() else { + let Some(other_id) = other_comparable.downcast_ref::() else { return true; }; - Id::of::() != *other_id + LocalComponent::id() != *other_id } fn get_comparable() -> Box { - Box::new(Id::of::()) + Box::new(LocalComponent::id()) } } diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 041c124..141fea7 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -8,12 +8,11 @@ use std::vec::IntoIter as OwnedVecIter; use crate::archetype::Id as ArchetypeId; use crate::component::{ Component, - Id as ComponentId, IsOptional as ComponentIsOptional, Metadata as ComponentMetadata, }; -use crate::entity::Uid as EntityUid; use crate::type_name::TypeName; +use crate::uid::Uid; use crate::util::Sortable; use crate::EntityComponent; @@ -22,7 +21,7 @@ pub struct Storage { archetypes: Vec, archetype_lookup: RefCell>, - entity_archetype_lookup: HashMap, + entity_archetype_lookup: HashMap, } impl Storage @@ -72,7 +71,7 @@ impl Storage self.iter_archetypes_by_lookup(archetype_id) } - pub fn get_entity_archetype(&self, entity_uid: EntityUid) -> Option<&Archetype> + pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype> { let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; @@ -85,15 +84,15 @@ impl Storage #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] pub fn push_entity( &mut self, - entity_uid: EntityUid, + entity_uid: Uid, mut components: Vec>, - ) -> Result<(ArchetypeId, EntityUid), Error> + ) -> 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.id()); + components.sort_by_key(|component| component.self_id()); #[cfg(feature = "debug")] tracing::debug!( @@ -141,7 +140,7 @@ impl Storage pub fn add_components_to_entity( &mut self, - entity_uid: EntityUid, + entity_uid: Uid, components: Vec>, ) -> Option<()> { @@ -154,7 +153,7 @@ impl Storage let contains_component_already = components .iter() - .any(|component| archetype.component_ids.contains_key(&component.id())); + .any(|component| archetype.component_ids.contains_key(&component.self_id())); if contains_component_already { let component_cnt = components.len(); @@ -193,8 +192,8 @@ impl Storage pub fn remove_components_from_entity( &mut self, - entity_uid: EntityUid, - component_ids: impl IntoIterator, + entity_uid: Uid, + component_ids: impl IntoIterator, ) -> Option<()> { let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; @@ -216,7 +215,7 @@ impl Storage .components .into_iter() .map(|component| component.component.into_inner()) - .filter(|component| !component_ids_set.contains(&component.id())) + .filter(|component| !component_ids_set.contains(&component.self_id())) .collect(), ) .expect("Not supposed to return Err since the entity is removed"); @@ -226,7 +225,7 @@ impl Storage fn populate_matching_archetype_lookup_entries( &mut self, - comp_ids_set: &HashSet, + comp_ids_set: &HashSet, archetype_index: usize, ) { @@ -251,7 +250,7 @@ impl Storage fn get_or_create_archetype( &mut self, archetype_id: ArchetypeId, - comp_ids_set: &HashSet, + comp_ids_set: &HashSet, components: &[Box], ) -> usize { @@ -266,7 +265,7 @@ impl Storage if lookup_entry.archetype_indices.is_empty() { self.archetypes.push(Archetype::new( - components.iter().map(|component| component.id()), + components.iter().map(|component| component.self_id()), )); lookup_entry @@ -282,7 +281,7 @@ impl Storage fn find_archetype_index_with_entity( &self, archetype_id: ArchetypeId, - entity_uid: EntityUid, + entity_uid: Uid, ) -> Option { let archetype_lookup = self.archetype_lookup.borrow_mut(); @@ -330,7 +329,7 @@ impl Storage pub enum Error { #[error("Entity already exists")] - EntityAlreadyExists(EntityUid), + EntityAlreadyExists(Uid), } impl TypeName for Storage @@ -344,21 +343,21 @@ impl TypeName for Storage #[derive(Debug)] struct ArchetypeLookupEntry { - component_ids: HashSet, + component_ids: HashSet, archetype_indices: Vec, } #[derive(Debug)] pub struct Archetype { - component_ids: HashMap, - entity_lookup: HashMap, + component_ids: HashMap, + entity_lookup: HashMap, entities: Vec, } impl Archetype { - fn new(component_ids: impl IntoIterator) -> Self + fn new(component_ids: impl IntoIterator) -> Self { Self { component_ids: component_ids @@ -371,10 +370,7 @@ impl Archetype } } - pub fn component_ids_is_superset( - &self, - other_component_ids: &HashSet, - ) -> bool + pub fn component_ids_is_superset(&self, other_component_ids: &HashSet) -> bool { if other_component_ids.len() <= self.component_ids.len() { other_component_ids @@ -385,7 +381,7 @@ impl Archetype } } - pub fn get_entity(&self, entity_uid: EntityUid) -> Option<&ArchetypeEntity> + pub fn get_entity(&self, entity_uid: Uid) -> Option<&ArchetypeEntity> { let entity_index = *self.entity_lookup.get(&entity_uid)?; @@ -397,14 +393,14 @@ impl Archetype EntityIter { iter: self.entities.iter() } } - pub fn get_index_for_component(&self, component_id: &ComponentId) -> Option + pub fn get_index_for_component(&self, component_id: &Uid) -> Option { self.component_ids.get(component_id).copied() } fn push_entity( &mut self, - entity_uid: EntityUid, + entity_uid: Uid, components: impl IntoIterator>, ) { @@ -414,7 +410,7 @@ impl Archetype }); } - pub fn take_entity(&mut self, entity_uid: EntityUid) -> Option + pub fn take_entity(&mut self, entity_uid: Uid) -> Option { let entity_index = self.entity_lookup.remove(&entity_uid)?; @@ -429,7 +425,7 @@ impl Archetype Some(entity) } - fn has_entity(&self, entity_uid: EntityUid) -> bool + fn has_entity(&self, entity_uid: Uid) -> bool { self.entity_lookup.contains_key(&entity_uid) } @@ -438,13 +434,13 @@ impl Archetype #[derive(Debug)] pub struct ArchetypeEntity { - uid: EntityUid, + uid: Uid, components: Vec, } impl ArchetypeEntity { - pub fn uid(&self) -> EntityUid + pub fn uid(&self) -> Uid { self.uid } @@ -501,7 +497,7 @@ impl<'archetype> Iterator for EntityIter<'archetype> fn create_non_opt_component_id_set( component_metadata_iter: impl IntoIterator, -) -> HashSet +) -> HashSet where Item: Borrow, { @@ -528,11 +524,11 @@ mod tests use super::Storage; use crate::archetype::Id as ArchetypeId; use crate::component::{ - Id as ComponentId, + Component, IsOptional as ComponentIsOptional, Metadata as ComponentMetadata, }; - use crate::entity::Uid as EntityUid; + use crate::uid::{Kind as UidKind, Uid}; use crate::{self as ecs}; #[derive(Debug, Component)] @@ -569,7 +565,7 @@ mod tests component_storage .push_entity( - EntityUid::new_unique(), + Uid::new_unique(UidKind::Entity), vec![ Box::new(HealthPotion { _hp_restoration: 12 }), Box::new(Hookshot { _range: 50 }), @@ -600,11 +596,11 @@ mod tests let mut components_metadata = [ ComponentMetadata { - id: ComponentId::of::(), + id: HealthPotion::id(), is_optional: ComponentIsOptional::No, }, ComponentMetadata { - id: ComponentId::of::(), + id: Hookshot::id(), is_optional: ComponentIsOptional::No, }, ]; diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 18f229a..fff66f5 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -1,43 +1,14 @@ -use std::sync::atomic::{AtomicU64, Ordering}; - use linkme::distributed_slice; use crate::World; -static NEXT_UID: AtomicU64 = AtomicU64::new(0); - -/// Unique entity ID. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Uid -{ - inner: u64, -} - -impl Uid -{ - pub fn new(uid: u64) -> Self - { - debug_assert!( - uid < NEXT_UID.load(Ordering::Relaxed), - "Invalid entity UID {uid}" - ); - - Self { inner: uid } - } - - pub fn new_unique() -> Self - { - Self { - inner: NEXT_UID.fetch_add(1, Ordering::Relaxed), - } - } -} - #[macro_export] macro_rules! static_entity { ($visibility: vis $ident: ident, $components: expr) => { - $visibility static $ident: ::std::sync::LazyLock<$crate::entity::Uid> = - ::std::sync::LazyLock::new(|| $crate::entity::Uid::new_unique()); + $visibility static $ident: ::std::sync::LazyLock<$crate::uid::Uid> = + ::std::sync::LazyLock::new(|| { + $crate::uid::Uid::new_unique($crate::uid::Kind::Entity) + }); $crate::private::paste::paste! { mod [<__ecs_ $ident:lower _static_entity_priv>] { diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs index 40cd20d..8b066a7 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -5,7 +5,8 @@ use std::marker::PhantomData; use ecs_macros::Component; -use crate::component::{Component, Id as ComponentId}; +use crate::component::Component; +use crate::uid::Uid; use crate::event::{Event, Id}; use crate::tuple::{ReduceElement as TupleReduceElement, With as TupleWith}; @@ -50,7 +51,7 @@ where where Self: Sized, { - Id::new::, _>(Some(ComponentId::of::())) + Id::new::, _>(Some(ComponentT::id())) } } @@ -93,18 +94,18 @@ where where Self: Sized, { - Id::new::, _>(Some(ComponentId::of::())) + Id::new::, _>(Some(ComponentT::id())) } } #[must_use] -pub fn create_added_id(component_id: ComponentId) -> Id +pub fn create_added_id(component_id: Uid) -> Id { Id::new::, _>(Some(component_id)) } #[must_use] -pub fn create_removed_id(component_id: ComponentId) -> Id +pub fn create_removed_id(component_id: Uid) -> Id { Id::new::, _>(Some(component_id)) } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 6725d81..a643bec 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use crate::actions::Action; use crate::component::storage::Storage as ComponentStorage; -use crate::component::{Component, Id as ComponentId, Sequence as ComponentSequence}; -use crate::entity::{Uid as EntityUid, CREATE_STATIC_ENTITIES}; +use crate::component::{Component, Sequence as ComponentSequence}; +use crate::entity::CREATE_STATIC_ENTITIES; use crate::event::component::{ create_added_id as create_component_added_event_id, create_removed_id as create_component_removed_event_id, @@ -27,6 +27,7 @@ use crate::stats::Stats; use crate::system::{System, TypeErased as TypeErasedSystem}; use crate::tuple::Reduce as TupleReduce; use crate::type_name::TypeName; +use crate::uid::{Kind as UidKind, Uid}; pub mod actions; pub mod component; @@ -41,6 +42,7 @@ pub mod stats; pub mod system; pub mod tuple; pub mod type_name; +pub mod uid; #[doc(hidden)] pub mod private; @@ -78,12 +80,12 @@ impl World /// /// # Panics /// Will panic if mutable internal lock cannot be acquired. - pub fn create_entity(&mut self, components: Comps) -> EntityUid + pub fn create_entity(&mut self, components: Comps) -> Uid where Comps: ComponentSequence + TupleReduce, Comps::Out: EventSequence, { - let entity_uid = EntityUid::new_unique(); + let entity_uid = Uid::new_unique(UidKind::Entity); self.create_entity_with_uid(components, entity_uid); @@ -91,11 +93,13 @@ impl World } #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] - pub fn create_entity_with_uid(&self, components: Comps, entity_uid: EntityUid) + pub fn create_entity_with_uid(&self, components: Comps, entity_uid: Uid) where Comps: ComponentSequence + TupleReduce, Comps::Out: EventSequence, { + debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + #[allow(unused_variables)] if let Err(err) = self .data @@ -206,12 +210,12 @@ impl World let component_ids = components .iter() - .map(|component| component.id()) + .map(|component| component.self_id()) .collect::>(); #[allow(unused_variables)] if let Err(err) = component_storage_lock - .push_entity(EntityUid::new_unique(), components) + .push_entity(Uid::new_unique(UidKind::Entity), components) { #[cfg(feature = "debug")] tracing::error!("Failed to create entity: {err}"); @@ -236,7 +240,7 @@ impl World let component_ids = components .iter() - .map(|component| component.id()) + .map(|component| component.self_id()) .collect::>(); component_storage_lock @@ -375,7 +379,7 @@ pub struct WorldData #[non_exhaustive] pub struct EntityComponent { - pub id: ComponentId, + pub id: Uid, pub name: &'static str, pub component: Lock>, } @@ -385,7 +389,7 @@ impl From> for EntityComponent fn from(component: Box) -> Self { Self { - id: component.id(), + id: component.self_id(), name: component.type_name(), component: Lock::new(component), } diff --git a/ecs/src/query.rs b/ecs/src/query.rs index e7fd7a6..69bb35d 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -15,7 +15,6 @@ use crate::component::{ Metadata as ComponentMetadata, Sequence as ComponentSequence, }; -use crate::entity::Uid as EntityUid; use crate::lock::{ReadGuard, WriteGuard}; use crate::query::options::Options; use crate::system::{ @@ -23,6 +22,7 @@ use crate::system::{ Param as SystemParam, System, }; +use crate::uid::Uid; use crate::{EntityComponent, World}; pub mod options; @@ -86,7 +86,7 @@ where /// Returns the UID of the entity at the given query iteration index. #[must_use] - pub fn entity_uid(&self, entity_index: usize) -> Option + pub fn get_entity_uid(&self, entity_index: usize) -> Option { Some( self.component_storage diff --git a/ecs/src/query/options.rs b/ecs/src/query/options.rs index d895073..bbbe0a8 100644 --- a/ecs/src/query/options.rs +++ b/ecs/src/query/options.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::marker::PhantomData; -use crate::component::{Component, Id as ComponentId}; +use crate::component::Component; use crate::EntityComponent; /// Query options. @@ -42,7 +42,7 @@ where .map(|component| component.id) .collect::>(); - ids_set.contains(&ComponentId::of::()) + ids_set.contains(&ComponentT::id()) } } diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs index 4db29da..9f2a81e 100644 --- a/ecs/src/relationship.rs +++ b/ecs/src/relationship.rs @@ -1,24 +1,24 @@ use std::any::{type_name, Any}; use std::marker::PhantomData; +use std::sync::LazyLock; use crate::component::storage::Storage as ComponentStorage; use crate::component::{ Component, FromOptional as FromOptionalComponent, FromOptionalMut as FromOptionalMutComponent, - Id as ComponentId, }; -use crate::entity::Uid as EntityUid; use crate::lock::ReadGuard; use crate::system::{ComponentRef, ComponentRefMut, Input as SystemInput}; use crate::type_name::TypeName; +use crate::uid::{Kind as UidKind, Uid}; use crate::World; /// A relationship to one or more targets. #[derive(Debug)] pub struct Relationship { - entity_uid: SingleOrMultiple, + entity_uid: SingleOrMultiple, _pd: PhantomData<(Kind, ComponentT)>, } @@ -28,8 +28,10 @@ where { /// Creates a new `Relationship` with a single target. #[must_use] - pub fn new(entity_uid: EntityUid) -> Self + pub fn new(entity_uid: Uid) -> Self { + debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + Self { entity_uid: SingleOrMultiple::Single(entity_uid), _pd: PhantomData, @@ -38,15 +40,24 @@ where /// Creates a new `Relationship` with multiple targets. #[must_use] - pub fn new_multiple(entity_uids: impl IntoIterator) -> Self + pub fn new_multiple(entity_uids: impl IntoIterator) -> Self { + let uids = entity_uids.into_iter().collect::>(); + + for euid in &uids { + debug_assert_eq!(euid.kind(), UidKind::Entity); + } + Self { - entity_uid: SingleOrMultiple::Multiple(entity_uids.into_iter().collect()), + entity_uid: SingleOrMultiple::Multiple(uids), _pd: PhantomData, } } } +static COMPONENT_EUID: LazyLock = + LazyLock::new(|| Uid::new_unique(UidKind::Component)); + impl Component for Relationship where Kind: 'static, @@ -56,9 +67,16 @@ where type Ref<'component> = Relation<'component, Kind, ComponentT>; type RefMut<'component> = RelationMut<'component, Kind, ComponentT>; - fn id(&self) -> ComponentId + fn id() -> Uid + where + Self: Sized, { - ComponentId::of::() + *COMPONENT_EUID + } + + fn self_id(&self) -> Uid + { + Self::id() } fn as_any_mut(&mut self) -> &mut dyn Any @@ -151,8 +169,7 @@ where .get_entity(*target) .expect("Target entity is gone from archetype"); - let component_index = - archetype.get_index_for_component(&ComponentId::of::())?; + let component_index = archetype.get_index_for_component(&ComponentT::id())?; let component = ComponentRefMut::new( entity @@ -172,7 +189,7 @@ where /// Returns a reference to the target at the specified index. #[must_use] - pub fn get_target(&self, index: usize) -> Option<&EntityUid> + pub fn get_target(&self, index: usize) -> Option<&Uid> { match &self.relationship_comp.entity_uid { SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), @@ -183,7 +200,7 @@ where /// Returns a mutable reference to the target at the specified index. #[must_use] - pub fn get_target_mut(&mut self, index: usize) -> Option<&mut EntityUid> + pub fn get_target_mut(&mut self, index: usize) -> Option<&mut Uid> { match &mut self.relationship_comp.entity_uid { SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), @@ -193,8 +210,10 @@ where } /// Adds a target to the relationship. - pub fn add_target(&mut self, entity_uid: EntityUid) + pub fn add_target(&mut self, entity_uid: Uid) { + debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + match &mut self.relationship_comp.entity_uid { SingleOrMultiple::Single(prev_entity_uid) => { self.relationship_comp.entity_uid = @@ -205,7 +224,7 @@ where } /// Removes a target to the relationship, returning it. - pub fn remove_target(&mut self, index: usize) -> Option + pub fn remove_target(&mut self, index: usize) -> Option { match &mut self.relationship_comp.entity_uid { SingleOrMultiple::Single(entity_uid) => { @@ -354,8 +373,7 @@ where .get_entity(*target) .expect("Target entity is gone from archetype"); - let component_index = - archetype.get_index_for_component(&ComponentId::of::())?; + let component_index = archetype.get_index_for_component(&ComponentT::id())?; let component = ComponentRef::new( entity @@ -375,7 +393,7 @@ where /// Returns a reference to the target at the specified index. #[must_use] - pub fn get_target(&self, index: usize) -> Option<&EntityUid> + pub fn get_target(&self, index: usize) -> Option<&Uid> { match &self.relationship_comp.entity_uid { SingleOrMultiple::Single(entity_uid) if index == 0 => Some(entity_uid), diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 99b56c1..810a071 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, Id as ComponentId}; +use crate::component::Component; use crate::lock::Lock; use crate::system::util::check_params_are_compatible; use crate::system::{ @@ -21,13 +21,14 @@ use crate::tuple::{ TakeOptionElementResult as TupleTakeOptionElementResult, WithOptionElements as TupleWithOptionElements, }; +use crate::uid::Uid; use crate::World; /// A stateful system. pub struct Stateful { func: Func, - local_components: HashMap>>, + local_components: HashMap>>, } macro_rules! impl_system { @@ -125,7 +126,7 @@ macro_rules! impl_system { ) -> Option> { let local_component = self.local_components - .get(&ComponentId::of::())? + .get(&LocalComponent::id())? .write_nonblock() .expect("Failed to aquire read-write local component lock"); @@ -139,7 +140,7 @@ macro_rules! impl_system { { self.local_components .insert( - ComponentId::of::(), + LocalComponent::id(), Lock::new(Box::new(local_component)) ); } diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs new file mode 100644 index 0000000..56d84f9 --- /dev/null +++ b/ecs/src/uid.rs @@ -0,0 +1,42 @@ +use std::mem::transmute; +use std::sync::atomic::{AtomicU32, Ordering}; + +static NEXT: AtomicU32 = AtomicU32::new(1); + +// Bit 0 and 1 for the kind +const KIND_BITS: u64 = 0x03; + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +pub enum Kind +{ + Entity = 2, + Component = 1, +} + +/// Unique entity/component ID. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Uid +{ + inner: u64, +} + +impl Uid +{ + /// Returns a new unique entity/component ID. + pub fn new_unique(kind: Kind) -> Self + { + let id_part = NEXT.fetch_add(1, Ordering::Relaxed); + + Self { + inner: ((id_part as u64) << 32) | kind as u64, + } + } + + pub fn kind(&self) -> Kind + { + // SAFETY: The kind bits cannot be invalid since they are set using the Kind enum + // in the new_unique function + unsafe { transmute((self.inner & KIND_BITS) as u8) } + } +} -- cgit v1.2.3-18-g5258