diff options
Diffstat (limited to 'ecs/src')
-rw-r--r-- | ecs/src/actions.rs | 56 | ||||
-rw-r--r-- | ecs/src/archetype.rs | 56 | ||||
-rw-r--r-- | ecs/src/component.rs | 490 | ||||
-rw-r--r-- | ecs/src/component/local.rs | 10 | ||||
-rw-r--r-- | ecs/src/component/storage.rs | 1139 | ||||
-rw-r--r-- | ecs/src/component/storage/archetype.rs | 387 | ||||
-rw-r--r-- | ecs/src/component/storage/graph.rs | 432 | ||||
-rw-r--r-- | ecs/src/entity.rs | 133 | ||||
-rw-r--r-- | ecs/src/event/component.rs | 88 | ||||
-rw-r--r-- | ecs/src/lib.rs | 550 | ||||
-rw-r--r-- | ecs/src/lock.rs | 128 | ||||
-rw-r--r-- | ecs/src/pair.rs | 198 | ||||
-rw-r--r-- | ecs/src/phase.rs | 6 | ||||
-rw-r--r-- | ecs/src/query.rs | 494 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 137 | ||||
-rw-r--r-- | ecs/src/query/options.rs | 59 | ||||
-rw-r--r-- | ecs/src/query/term.rs | 115 | ||||
-rw-r--r-- | ecs/src/relationship.rs | 538 | ||||
-rw-r--r-- | ecs/src/sole.rs | 17 | ||||
-rw-r--r-- | ecs/src/system.rs | 232 | ||||
-rw-r--r-- | ecs/src/system/stateful.rs | 16 | ||||
-rw-r--r-- | ecs/src/type_name.rs | 15 | ||||
-rw-r--r-- | ecs/src/uid.rs | 191 | ||||
-rw-r--r-- | ecs/src/util.rs | 180 | ||||
-rw-r--r-- | ecs/src/util/array_vec.rs | 131 |
25 files changed, 3517 insertions, 2281 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index 89fd84a..3dd8755 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -1,11 +1,7 @@ use std::marker::PhantomData; -use std::sync::{Arc, Weak}; +use std::rc::{Rc, Weak}; -use crate::component::{ - Component, - Metadata as ComponentMetadata, - Sequence as ComponentSequence, -}; +use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence}; use crate::system::{Param as SystemParam, System}; use crate::uid::{Kind as UidKind, Uid}; use crate::{ActionQueue, World}; @@ -23,13 +19,11 @@ impl<'world> Actions<'world> /// Queues up a entity to spawn at the end of the current tick. pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps) { - self.action_queue.push(Action::Spawn( - components.into_array().into(), - EventIds { ids: Comps::added_event_ids() }, - )); + self.action_queue + .push(Action::Spawn(components.into_parts_array().into())); } - /// Queues up despawning a entity at the end of the current tick. + /// Queues up despawning a entity at the end of the **next** tick. pub fn despawn(&mut self, entity_uid: Uid) { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); @@ -50,26 +44,28 @@ impl<'world> Actions<'world> self.action_queue.push(Action::AddComponents( entity_uid, - components.into_array().into(), - EventIds { ids: Comps::added_event_ids() }, + components.into_parts_array().into(), )); } - /// Queues up removing component(s) from a entity at the end of the current tick. - pub fn remove_components<Comps>(&mut self, entity_uid: Uid) - where - Comps: ComponentSequence, + /// Queues up removing component(s) from a entity at the end of the **next** tick. + pub fn remove_components( + &mut self, + entity_uid: Uid, + component_ids: impl IntoIterator<Item = Uid>, + ) { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - if Comps::COUNT == 0 { + let mut component_ids = component_ids.into_iter().peekable(); + + if component_ids.peek().is_none() { return; } self.action_queue.push(Action::RemoveComponents( entity_uid, - Comps::metadata().into_iter().collect(), - EventIds { ids: Comps::removed_event_ids() }, + component_ids.collect(), )); } @@ -91,11 +87,11 @@ impl<'world> Actions<'world> } } - fn new(action_queue: &'world Arc<ActionQueue>) -> Self + fn new(action_queue: &'world Rc<ActionQueue>) -> Self { Self { action_queue, - action_queue_weak: Arc::downgrade(action_queue), + action_queue_weak: Rc::downgrade(action_queue), } } } @@ -146,11 +142,11 @@ impl WeakRef #[derive(Debug, Clone)] pub struct Ref<'weak_ref> { - action_queue: Arc<ActionQueue>, + action_queue: Rc<ActionQueue>, _pd: PhantomData<&'weak_ref ()>, } -impl<'weak_ref> Ref<'weak_ref> +impl Ref<'_> { #[must_use] pub fn to_actions(&self) -> Actions<'_> @@ -159,19 +155,13 @@ impl<'weak_ref> Ref<'weak_ref> } } -#[derive(Debug)] -pub(crate) struct EventIds -{ - pub(crate) ids: Vec<Uid>, -} - /// A action for a [`System`] to perform. #[derive(Debug)] pub(crate) enum Action { - Spawn(Vec<Box<dyn Component>>, EventIds), + Spawn(Vec<ComponentParts>), Despawn(Uid), - AddComponents(Uid, Vec<Box<dyn Component>>, EventIds), - RemoveComponents(Uid, Vec<ComponentMetadata>, EventIds), + AddComponents(Uid, Vec<ComponentParts>), + RemoveComponents(Uid, Vec<Uid>), Stop, } diff --git a/ecs/src/archetype.rs b/ecs/src/archetype.rs deleted file mode 100644 index 846e231..0000000 --- a/ecs/src/archetype.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; - -use crate::component::Metadata as ComponentMetadata; - -/// Archetype ID. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Id -{ - hash: u64, -} - -impl Id -{ - pub fn from_components_metadata( - components_metadata: &impl AsRef<[ComponentMetadata]>, - ) -> Self - { - if components_metadata.as_ref().len() == 0 { - return Self { hash: 0 }; - } - - debug_assert!( - components_metadata - .as_ref() - .is_sorted_by_key(|comp_metadata| comp_metadata.id), - "Cannot create archetype ID from a unsorted component metadata list" - ); - - let component_ids = - components_metadata - .as_ref() - .iter() - .filter_map(|component_metadata| { - if component_metadata.is_optional { - return None; - } - - Some(component_metadata.id) - }); - - let mut hasher = DefaultHasher::new(); - - for component_id in component_ids { - component_id.hash(&mut hasher); - } - - let hash = hasher.finish(); - - assert_ne!( - hash, 0, - "Archetype ID 0 is reserved for a archetype with zero components" - ); - - Self { hash } - } -} diff --git a/ecs/src/component.rs b/ecs/src/component.rs index b2ecf80..5a8cd0b 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,87 +1,53 @@ use std::any::{type_name, Any}; use std::fmt::Debug; +use std::ops::{Deref, DerefMut}; +use ecs_macros::Component; +use hashbrown::HashSet; use seq_macro::seq; -use crate::event::component::{ - Added as ComponentAddedEvent, - Kind as ComponentEventKind, - Removed as ComponentRemovedEvent, +use crate::lock::{ + Error as LockError, + MappedReadGuard, + MappedWriteGuard, + ReadGuard, + WriteGuard, }; -use crate::lock::{Error as LockError, Lock, ReadGuard, WriteGuard}; use crate::system::Input as SystemInput; -use crate::type_name::TypeName; use crate::uid::Uid; use crate::util::Array; -use crate::{EntityComponent, World}; +use crate::EntityComponentRef; pub mod local; pub(crate) mod storage; -pub trait Component: SystemInput + Any + TypeName +pub trait Component: SystemInput + Any { - /// The component type in question. Will usually be `Self` - type Component: Component - where - Self: Sized; - - type RefMut<'component>: FromOptionalMut<'component> + FromLockedOptional<'component> - where - Self: Sized; - - type Ref<'component>: FromOptional<'component> + FromLockedOptional<'component> - where - Self: Sized; - /// 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; - - /// Returns the component UID of a component event for this component. - fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid; - - #[doc(hidden)] - fn as_any_mut(&mut self) -> &mut dyn Any; - - #[doc(hidden)] - fn as_any(&self) -> &dyn Any; - - /// Returns whether the component `self` is optional. - fn self_is_optional(&self) -> bool - { - false - } - - /// Returns whether this component is optional. - #[must_use] - fn is_optional() -> bool - where - Self: Sized, - { - false - } + /// Returns the name of this component. + fn name(&self) -> &'static str; } impl dyn Component { pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real> { - self.as_any_mut().downcast_mut() + (self as &mut dyn Any).downcast_mut() } pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real> { - self.as_any().downcast_ref() + (self as &dyn Any).downcast_ref() } pub fn is<Other: 'static>(&self) -> bool { - self.as_any().is::<Other>() + (self as &dyn Any).is::<Other>() } } @@ -93,314 +59,292 @@ impl Debug for dyn Component } } -impl TypeName for Box<dyn Component> +/// A sequence of components. +pub trait Sequence { - fn type_name(&self) -> &'static str - { - self.as_ref().type_name() - } + /// The number of components in this component sequence. + const COUNT: usize; + + type PartsArray: Array<Parts>; + + fn into_parts_array(self) -> Self::PartsArray; } -impl<ComponentT> Component for Option<ComponentT> -where - ComponentT: Component, - for<'a> Option<ComponentT::Ref<'a>>: FromOptional<'a> + FromLockedOptional<'a>, - for<'a> Option<ComponentT::RefMut<'a>>: FromOptionalMut<'a> + FromLockedOptional<'a>, +#[derive(Debug)] +pub struct Handle<'a, ComponentData: 'static> { - type Component = ComponentT; - type Ref<'component> = Option<ComponentT::Ref<'component>>; - type RefMut<'component> = Option<ComponentT::RefMut<'component>>; - - fn id() -> Uid - { - ComponentT::id() - } + inner: MappedReadGuard<'a, ComponentData>, +} - fn self_id(&self) -> Uid +impl<'comp, ComponentData: 'static> Handle<'comp, ComponentData> +{ + /// Creates a new handle instance from a [`EntityComponentRef`]. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn from_entity_component_ref( + entity_component_ref: EntityComponentRef<'comp>, + ) -> Result<Self, HandleError> { - Self::id() + Ok(Self::new( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + )) } - fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid + pub(crate) fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Self { - match event_kind { - ComponentEventKind::Removed => ComponentRemovedEvent::<Self>::id(), + Self { + inner: inner.map(|component| { + component + .downcast_ref::<ComponentData>() + .unwrap_or_else(|| { + panic!( + "Failed to downcast component to type {}", + type_name::<ComponentData>() + ); + }) + }), } } +} - fn as_any_mut(&mut self) -> &mut dyn Any +impl<ComponentData: 'static> Deref for Handle<'_, ComponentData> +{ + type Target = ComponentData; + + fn deref(&self) -> &Self::Target { - self + &self.inner } +} + +#[derive(Debug)] +pub struct HandleMut<'a, ComponentData: 'static> +{ + inner: MappedWriteGuard<'a, ComponentData>, +} - fn as_any(&self) -> &dyn Any +impl<'comp, ComponentData: 'static> HandleMut<'comp, ComponentData> +{ + /// Creates a new handle instance from a [`EntityComponentRef`]. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn from_entity_component_ref( + entity_component_ref: EntityComponentRef<'comp>, + ) -> Result<Self, HandleError> { - self + Ok(Self::new( + entity_component_ref + .component() + .write_nonblock() + .map_err(AcquireLockError)?, + )) } - fn self_is_optional(&self) -> bool + pub(crate) fn new(inner: WriteGuard<'comp, Box<dyn Any>>) -> Self { - true + Self { + inner: inner.map(|component| { + component + .downcast_mut::<ComponentData>() + .unwrap_or_else(|| { + panic!( + "Failed to downcast component to type {}", + type_name::<ComponentData>() + ); + }) + }), + } } +} + +impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData> +{ + type Target = ComponentData; - fn is_optional() -> bool + fn deref(&self) -> &Self::Target { - true + &self.inner } } -impl<ComponentT> TypeName for Option<ComponentT> -where - ComponentT: Component, +impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData> { - fn type_name(&self) -> &'static str + fn deref_mut(&mut self) -> &mut Self::Target { - type_name::<Self>() + &mut self.inner } } -impl<ComponentT> SystemInput for Option<ComponentT> where ComponentT: Component {} - -/// A sequence of components. -pub trait Sequence +#[derive(Debug, thiserror::Error)] +pub enum HandleError { - /// The number of components in this component sequence. - const COUNT: usize; - - type Array: Array<Box<dyn Component>>; + #[error(transparent)] + AcquireLockFailed(#[from] AcquireLockError), +} - fn into_array(self) -> Self::Array; +#[derive(Debug, thiserror::Error)] +#[error("Failed to acquire component lock")] +pub struct AcquireLockError(#[source] LockError); - fn metadata() -> impl Array<Metadata>; +macro_rules! inner { + ($c: tt) => { + seq!(I in 0..=$c { + impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*) + { + const COUNT: usize = $c + 1; - fn added_event_ids() -> Vec<Uid>; + type PartsArray = [Parts; $c + 1]; - fn removed_event_ids() -> Vec<Uid>; + fn into_parts_array(self) -> Self::PartsArray + { + [#({ + self.I.into_parts() + },)*] + } + } + }); + }; } -/// A sequence of references (immutable or mutable) to components. -pub trait RefSequence -{ - type Handles<'component>; - - type Metadata: Array<Metadata>; +seq!(C in 0..=16 { + inner!(C); +}); - fn metadata() -> Self::Metadata; +impl Sequence for () +{ + type PartsArray = [Parts; 0]; - fn from_components<'component>( - components: &'component [EntityComponent], - component_index_lookup: impl Fn(Uid) -> Option<usize>, - world: &'component World, - ) -> Self::Handles<'component>; -} + const COUNT: usize = 0; -/// A mutable or immutable reference to a component. -pub trait Ref -{ - type Component: Component; - type Handle<'component>: FromLockedOptional<'component>; + fn into_parts_array(self) -> Self::PartsArray + { + [] + } } -impl<ComponentT> Ref for &ComponentT -where - ComponentT: Component, +pub trait IntoParts { - type Component = ComponentT; - type Handle<'component> = ComponentT::Ref<'component>; + fn into_parts(self) -> Parts; } -impl<ComponentT> Ref for &mut ComponentT +impl<ComponentT> IntoParts for ComponentT where ComponentT: Component, { - type Component = ComponentT; - type Handle<'component> = ComponentT::RefMut<'component>; + fn into_parts(self) -> Parts + { + Parts::builder() + .name(type_name::<Self>()) + .build(Self::id(), self) + } } -/// [`Component`] metadata. -#[derive(Debug, Clone)] +/// The parts of a component. +#[derive(Debug)] #[non_exhaustive] -pub struct Metadata +pub struct Parts { - pub id: Uid, - pub is_optional: bool, + id: Uid, + name: &'static str, + data: Box<dyn Any>, } -impl Metadata +impl Parts { - pub fn get<ComponentT: Component + ?Sized>(component: &ComponentT) -> Self + #[must_use] + pub fn id(&self) -> Uid { - Self { - id: component.self_id(), - is_optional: component.self_is_optional(), - } + self.id } - pub fn of<ComponentT: Component>() -> Self + #[must_use] + pub fn name(&self) -> &'static str { - Self { - id: ComponentT::id(), - is_optional: ComponentT::is_optional(), - } + self.name } -} -pub trait FromOptionalMut<'comp> -{ - fn from_optional_mut_component( - optional_component: Option<WriteGuard<'comp, Box<dyn Component>>>, - world: &'comp World, - ) -> Self; -} + #[must_use] + pub fn builder() -> PartsBuilder + { + PartsBuilder::default() + } -pub trait FromOptional<'comp> -{ - fn from_optional_component( - optional_component: Option<ReadGuard<'comp, Box<dyn Component>>>, - world: &'comp World, - ) -> Self; + pub(crate) fn into_data(self) -> Box<dyn Any> + { + self.data + } } -pub trait FromLockedOptional<'comp>: Sized +#[derive(Debug)] +pub struct PartsBuilder { - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - world: &'comp World, - ) -> Result<Self, LockError>; + name: &'static str, } -macro_rules! inner { - ($c: tt) => { - seq!(I in 0..=$c { - impl<#(Comp~I: Component,)*> Sequence for (#(Comp~I,)*) - where - #(for<'comp> Comp~I::RefMut<'comp>: FromOptionalMut<'comp>,)* - #(for<'comp> Comp~I::Ref<'comp>: FromOptional<'comp>,)* - { - const COUNT: usize = $c + 1; - - type Array = [Box<dyn Component>; $c + 1]; - - fn into_array(self) -> Self::Array - { - [#(Box::new(self.I) as Box<dyn Component>,)*] - } - - fn metadata() -> impl Array<Metadata> - { - [ - #( - Metadata { - id: Comp~I::id(), - is_optional: Comp~I::is_optional(), - }, - )* - ] - } - - fn added_event_ids() -> Vec<Uid> - { - vec![ - #(ComponentAddedEvent::<Comp~I>::id(),)* - ] - } - - fn removed_event_ids() -> Vec<Uid> - { - vec![ - #(ComponentRemovedEvent::<Comp~I>::id(),)* - ] - } - } - - impl<#(CompRef~I: Ref,)*> RefSequence for (#(CompRef~I,)*) - { - type Handles<'component> = (#(CompRef~I::Handle<'component>,)*); - - type Metadata = [Metadata; $c + 1]; - - fn metadata() -> Self::Metadata - { - [#( - Metadata { - id: CompRef~I::Component::id(), - is_optional: CompRef~I::Component::is_optional(), - }, - )*] - } +impl PartsBuilder +{ + #[must_use] + pub fn name(mut self, name: &'static str) -> Self + { + self.name = name; + self + } - fn from_components<'component>( - components: &'component [EntityComponent], - component_index_lookup: impl Fn(Uid) -> Option<usize>, - world: &'component World, - ) -> Self::Handles<'component> - { - (#( - CompRef~I::Handle::from_locked_optional_component( - component_index_lookup(CompRef~I::Component::id()) - .and_then(|component_index| components.get(component_index)) - .map(|component| &component.component), - world, - ).unwrap_or_else(|err| { - panic!( - "Taking component {} lock failed: {err}", - type_name::<CompRef~I::Component>() - ); - }), - )*) - } - } - }); - }; + #[must_use] + pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts + { + Parts { + id, + name: self.name, + data: Box::new(data), + } + } } -seq!(C in 0..=16 { - inner!(C); -}); - -impl Sequence for () +impl Default for PartsBuilder { - type Array = [Box<dyn Component>; 0]; - - const COUNT: usize = 0; - - fn into_array(self) -> Self::Array + fn default() -> Self { - [] + Self { name: "(unspecified)" } } +} + +/// Pending component removals for a entity. +#[derive(Debug, Clone, Component)] +pub struct Removals +{ + component_ids: HashSet<Uid>, +} - fn metadata() -> impl Array<Metadata> +impl Removals +{ + pub fn contains<ComponentT: Component>(&self) -> bool { - [] + self.contains_id(ComponentT::id()) } - fn added_event_ids() -> Vec<Uid> + pub fn contains_id(&self, component_id: Uid) -> bool { - Vec::new() + self.component_ids.contains(&component_id) } - fn removed_event_ids() -> Vec<Uid> + pub(crate) fn add_ids(&mut self, ids: impl IntoIterator<Item = Uid>) { - Vec::new() + self.component_ids.extend(ids) } } -impl RefSequence for () +impl FromIterator<Uid> for Removals { - type Handles<'component> = (); - type Metadata = [Metadata; 0]; - - fn metadata() -> Self::Metadata - { - [] - } - - fn from_components<'component>( - _components: &'component [EntityComponent], - _component_index_lookup: impl Fn(Uid) -> Option<usize>, - _world: &'component World, - ) -> Self::Handles<'component> + fn from_iter<T: IntoIterator<Item = Uid>>(iter: T) -> Self { - () + Self { + component_ids: iter.into_iter().collect(), + } } } diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index ad79f6f..0f6f641 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,14 +1,14 @@ use std::ops::{Deref, DerefMut}; -use crate::component::Component; -use crate::system::{ComponentRefMut, Param as SystemParam, System}; +use crate::component::{Component, HandleMut as ComponentHandleMut}; +use crate::system::{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>, + local_component: ComponentHandleMut<'world, LocalComponent>, } impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> @@ -38,7 +38,7 @@ where } } -impl<'world, LocalComponent> Deref for Local<'world, LocalComponent> +impl<LocalComponent> Deref for Local<'_, LocalComponent> where LocalComponent: Component, { @@ -50,7 +50,7 @@ where } } -impl<'world, LocalComponent> DerefMut for Local<'world, LocalComponent> +impl<LocalComponent> DerefMut for Local<'_, LocalComponent> where LocalComponent: Component, { diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 0475bf1..b27b552 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -1,656 +1,787 @@ -use std::any::type_name; -use std::borrow::Borrow; +use std::any::Any; +use std::array::IntoIter as ArrayIter; use std::cell::RefCell; -use std::slice::Iter as SliceIter; -use std::vec::IntoIter as OwnedVecIter; +use std::vec::IntoIter as VecIntoIter; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashMap; -use crate::archetype::Id as ArchetypeId; -use crate::component::{Component, Metadata as ComponentMetadata}; -use crate::type_name::TypeName; -use crate::uid::Uid; -use crate::EntityComponent; +use crate::component::storage::archetype::{ + Archetype, + Entity as ArchetypeEntity, + EntityComponent as ArchetypeEntityComponent, + Id as ArchetypeId, +}; +use crate::component::storage::graph::{ + ArchetypeAddEdgeDfsIter, + ArchetypeAddEdgeDfsIterResult, + ArchetypeEdges, + Graph, +}; +use crate::uid::{Kind as UidKind, Uid}; +use crate::util::{BorrowedOrOwned, Either, StreamingIterator, VecExt}; + +pub mod archetype; + +mod graph; + +#[derive(Debug)] +pub struct ArchetypeSearchTerms<'a> +{ + pub required_components: &'a [Uid], + pub excluded_components: &'a [Uid], +} + +impl ArchetypeSearchTerms<'_> +{ + fn excluded_contains(&self, comp_id: Uid) -> bool + { + let comp_id_kind = comp_id.kind(); + + debug_assert!( + comp_id_kind == UidKind::Component + || (comp_id_kind == UidKind::Pair + && comp_id.target_component() != Uid::wildcard()) + ); + + let is_found = self.excluded_components.binary_search(&comp_id).is_ok(); + + if !is_found && comp_id_kind == UidKind::Pair { + return self.excluded_components.iter().any(|excluded_comp_id| { + excluded_comp_id.kind() == UidKind::Pair + && excluded_comp_id.has_same_relation_as(comp_id) + && excluded_comp_id.target_component() == Uid::wildcard() + }); + } + + is_found + } + + fn contains_conflicting(&self) -> bool + { + self.excluded_components.iter().any(|excluded_comp_id| { + self.required_components + .binary_search(excluded_comp_id) + .is_ok() + }) + } + + fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool + { + self.required_components + .iter() + .all(|comp_id| archetype.contains_matching_component(*comp_id)) + } +} #[derive(Debug, Default)] pub struct Storage { - archetypes: Vec<Archetype>, - archetype_lookup: RefCell<HashMap<ArchetypeId, ArchetypeLookupEntry>>, + graph: Graph, entity_archetype_lookup: HashMap<Uid, ArchetypeId>, + imaginary_archetypes: RefCell<Vec<ImaginaryArchetype>>, } impl Storage { - pub fn iter_archetypes_with_comps( + pub fn search_archetypes<'search_terms>( &self, - comp_metadata: impl AsRef<[ComponentMetadata]>, - ) -> ArchetypeRefIter<'_> + search_terms: ArchetypeSearchTerms<'search_terms>, + ) -> ArchetypeRefIter<'_, 'search_terms> { - debug_assert!(comp_metadata - .as_ref() - .is_sorted_by_key(|metadata| metadata.id)); + let archetype_id = ArchetypeId::new(search_terms.required_components); + + if search_terms.contains_conflicting() { + return ArchetypeRefIter { + storage: self, + pre_iter: Either::B(Vec::new().into_iter()), + dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &[]), + search_terms, + }; + } - let archetype_id = ArchetypeId::from_components_metadata(&comp_metadata); + let Some(add_edge_recursive_iter) = + self.graph.dfs_archetype_add_edges(archetype_id) + else { + self.imaginary_archetypes + .borrow_mut() + .push(ImaginaryArchetype { + id: ArchetypeId::new(search_terms.required_components.iter().filter( + |required_comp_id| { + required_comp_id.kind() != UidKind::Pair + || required_comp_id.target_component() != Uid::wildcard() + }, + )), + component_ids: search_terms + .required_components + .iter() + .copied() + .filter(|required_comp_id| { + required_comp_id.kind() != UidKind::Pair + || required_comp_id.target_component() != Uid::wildcard() + }) + .collect(), + }); + + let found_archetypes = self.find_all_archetype_with_comps(&search_terms); + + return ArchetypeRefIter { + storage: self, + pre_iter: Either::B(found_archetypes.clone().into_iter()), + dfs_iter: ArchetypeAddEdgeDfsIter::new(&self.graph, &found_archetypes), + search_terms, + }; + }; - if !self.archetype_lookup.borrow().contains_key(&archetype_id) { - self.archetype_lookup.borrow_mut().insert( - archetype_id, - self.create_populated_archetype_lookup_entry(comp_metadata.as_ref()), - ); + ArchetypeRefIter { + storage: self, + pre_iter: Either::A([archetype_id].into_iter()), + dfs_iter: add_edge_recursive_iter, + search_terms, } + } - self.iter_archetypes_by_lookup(archetype_id) + pub fn get_archetype_by_id(&self, id: ArchetypeId) -> Option<&Archetype> + { + Some(self.graph.get_node_by_id(id)?.archetype()) } - pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype> + pub fn create_entity(&mut self, uid: Uid) -> Result<(), Error> { - let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; + debug_assert_eq!(uid.kind(), UidKind::Entity); + + if self.entity_archetype_lookup.contains_key(&uid) { + return Err(Error::EntityAlreadyExists(uid)); + } + + let empty_archetype_id = ArchetypeId::new_empty(); + + let archetype_node = self.graph.get_or_create_node(empty_archetype_id, &[]); + + archetype_node + .archetype_mut() + .push_entity(ArchetypeEntity::new(uid, [])); - let archetype_index = self.get_archetype_index_by_id(*archetype_id)?; + self.entity_archetype_lookup.insert(uid, empty_archetype_id); - self.archetypes.get(archetype_index) + Ok(()) } - pub fn remove_entity(&mut self, entity_uid: Uid) + pub fn remove_entity(&mut self, entity_uid: Uid) -> Result<ArchetypeEntity, Error> { let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { - return; + return Err(Error::EntityDoesNotExist(entity_uid)); }; - let Some(archetype_index) = self.get_archetype_index_by_id(*archetype_id) else { - return; - }; - - let Some(archetype) = self.archetypes.get_mut(archetype_index) else { - return; - }; + let archetype_node = self + .graph + .get_node_by_id_mut(*archetype_id) + .expect("Archetype should exist"); - archetype.take_entity(entity_uid); + let entity = archetype_node + .archetype_mut() + .remove_entity(entity_uid) + .expect("Entity should exist in archetype"); self.entity_archetype_lookup.remove(&entity_uid); + + Ok(entity) + } + + pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype> + { + let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; + + self.get_archetype_by_id(*archetype_id) } - #[tracing::instrument(skip_all)] - pub fn push_entity( + pub fn add_entity_component( &mut self, entity_uid: Uid, - mut components: Vec<Box<dyn Component>>, - ) -> Result<(ArchetypeId, Uid), Error> + (component_id, component_name, component): (Uid, &'static str, Box<dyn Any>), + ) -> Result<(), Error> { - if self.entity_archetype_lookup.contains_key(&entity_uid) { - return Err(Error::EntityAlreadyExists(entity_uid)); + let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { + return Err(Error::EntityDoesNotExist(entity_uid)); + }; + + let archetype_id = *archetype_id; + + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); + + if archetype_node + .archetype() + .contains_component_with_exact_id(component_id) + { + return Err(Error::ComponentAlreadyInEntity { + entity: entity_uid, + component: component_id, + }); } - components.sort_by_key(|component| component.self_id()); - tracing::trace!( - "Pushing entity with components: ({})", - &components.iter().fold( - String::with_capacity(components.len() * 25), - |mut acc, component| { - acc.extend([", ", component.type_name()]); - acc - } - )[2..] - ); + let add_edge_archetype_id = if let Some(add_edge_id) = archetype_node + .get_or_insert_edges(component_id, ArchetypeEdges::default) + .add + { + if !self.graph.contains_archetype(add_edge_id) { + let (_, add_edge_comp_ids) = self + .graph + .get_node_by_id(archetype_id) + .expect("Archetype should exist") + .make_add_edge(component_id); + + self.graph.create_node(add_edge_id, &add_edge_comp_ids); + } - let archetype_id = ArchetypeId::from_components_metadata( - &components - .iter() - .map(|component| ComponentMetadata::get(&**component)) - .collect::<Vec<_>>(), - ); + add_edge_id + } else { + let archetype_node = self + .graph + .get_node_by_id(archetype_id) + .expect("Archetype should exist"); - let comp_ids_set = create_non_opt_component_id_set( - components - .iter() - .map(|component| ComponentMetadata::get(&**component)), - ); + let (add_edge_id, add_edge_comp_ids) = + archetype_node.make_add_edge(component_id); - let archetype_index = self.get_or_create_archetype(archetype_id, &components); + if !self.graph.contains_archetype(add_edge_id) { + self.graph.create_node(add_edge_id, &add_edge_comp_ids); + } - self.populate_matching_archetype_lookup_entries(&comp_ids_set, archetype_index); + add_edge_id + }; - let archetype = self - .archetypes - .get_mut(archetype_index) - .expect("Archetype is gone"); + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); + + let mut entity = archetype_node + .archetype_mut() + .remove_entity(entity_uid) + .expect("Entity should exist in archetype"); + + let add_edge_archetype = self + .graph + .get_node_by_id_mut(add_edge_archetype_id) + .expect("Add edge archetype should exist") + .archetype_mut(); + + entity.insert_component( + component_id, + ArchetypeEntityComponent::new(component, component_id, component_name), + add_edge_archetype, + ); - archetype.push_entity(entity_uid, components); + add_edge_archetype.push_entity(entity); self.entity_archetype_lookup - .insert(entity_uid, archetype_id); + .insert(entity_uid, add_edge_archetype_id); - Ok((archetype_id, entity_uid)) + Ok(()) } - pub fn add_components_to_entity( + pub fn remove_entity_component( &mut self, entity_uid: Uid, - components: Vec<Box<dyn Component>>, - ) -> Option<()> + component_id: Uid, + ) -> Result<(), Error> { - let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; - - let archetype_index = self.get_archetype_index_by_id(*archetype_id)?; - - let archetype = self.archetypes.get_mut(archetype_index)?; + let Some(archetype_id) = self.entity_archetype_lookup.get(&entity_uid) else { + return Err(Error::EntityDoesNotExist(entity_uid)); + }; - 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 archetype_id = *archetype_id; + + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); + + if !archetype_node + .archetype() + .contains_component_with_exact_id(component_id) + { + return Err(Error::ComponentNotFoundInEntity { + entity: entity_uid, + component: component_id, + }); } - 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 remove_edge_id = archetype_node + .get_or_insert_edges(component_id, ArchetypeEdges::default) + .remove + .unwrap_or_else(|| { + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); + + let (remove_edge_id, remove_edge_comp_ids) = + archetype_node.make_remove_edge(component_id); + + if !self.graph.contains_archetype(remove_edge_id) { + self.graph + .create_node(remove_edge_id, &remove_edge_comp_ids); + } - let archetype_index = self.get_archetype_index_by_id(*archetype_id)?; + remove_edge_id + }); - let archetype = self.archetypes.get_mut(archetype_index)?; + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); - let entity = archetype.take_entity(entity_uid)?; + let mut entity = archetype_node + .archetype_mut() + .remove_entity(entity_uid) + .expect("Entity should exist in archetype"); - let component_ids_set = component_ids.into_iter().collect::<HashSet<_>>(); + entity.remove_component(component_id, archetype_node.archetype()); - self.entity_archetype_lookup.remove(&entity_uid); + self.graph + .get_node_by_id_mut(remove_edge_id) + .expect("Remove edge archetype should exist") + .archetype_mut() + .push_entity(entity); - 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"); + self.entity_archetype_lookup + .insert(entity_uid, remove_edge_id); - Some(()) + Ok(()) } - fn populate_matching_archetype_lookup_entries( - &mut self, - comp_ids_set: &HashSet<Uid>, - archetype_index: usize, - ) + pub fn create_imaginary_archetypes(&mut self) { - 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) { + for imaginary_archetype in self.imaginary_archetypes.get_mut().drain(..) { + if self.graph.contains_archetype(imaginary_archetype.id) { continue; } - if lookup_entry.component_ids.is_subset(comp_ids_set) { - lookup_entry.archetype_indices.push(archetype_index); - } + self.graph + .create_node(imaginary_archetype.id, &imaginary_archetype.component_ids); } } - fn get_or_create_archetype( - &mut self, - archetype_id: ArchetypeId, - components: &[Box<dyn Component>], - ) -> usize + fn find_all_archetype_with_comps( + &self, + search_terms: &ArchetypeSearchTerms<'_>, + ) -> Vec<ArchetypeId> { - let mut archetype_lookup = self.archetype_lookup.borrow_mut(); - - if !archetype_lookup.contains_key(&archetype_id) { - self.archetypes.push(Archetype::new( - components.iter().map(|component| component.self_id()), - )); - } - - let lookup_entry = archetype_lookup.entry(archetype_id).or_insert_with(|| { - self.create_populated_archetype_lookup_entry( - components - .iter() - .map(|component| ComponentMetadata::get(&**component)), - ) - }); - - // SAFETY: Above, we push a archetype index if archetype_indices is empty so this - // cannot fail - unsafe { *lookup_entry.archetype_indices.first().unwrap_unchecked() } - } + let Some(mut search_iter) = + self.graph.dfs_archetype_add_edges(ArchetypeId::new_empty()) + else { + // If the root archetype doesn't exist, no other archetype can exist either + // + // TODO: The above comment is not true. Cases where imaginary archetypes have + // been created should be handled as well + return Vec::new(); + }; - fn get_archetype_index_by_id(&self, archetype_id: ArchetypeId) -> Option<usize> - { - let archetype_lookup = self.archetype_lookup.borrow(); + let mut found = Vec::<ArchetypeId>::new(); - let archetype_lookup_entry = archetype_lookup.get(&archetype_id)?; + while let Some(node_id) = search_iter.streaming_next() { + let ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id: node_id, + add_edge_component_id, + } = node_id + else { + continue; + }; - let index = *archetype_lookup_entry - .archetype_indices - .first() - .expect("No archetype indices in archetype lookup entry"); + if search_terms.excluded_contains(add_edge_component_id) { + search_iter.pop(); + continue; + } - debug_assert!( - self.archetypes.get(index).is_some_and(|archetype| archetype - .component_ids_is(&archetype_lookup_entry.component_ids)), - "Archetype components is not exact match" - ); + let node = self + .graph + .get_node_by_id(node_id) + .expect("Graph node found through DFS doesn't exist"); - Some(index) - } + if node.archetype().component_cnt() < search_terms.required_components.len() { + continue; + } - fn iter_archetypes_by_lookup(&self, archetype_id: ArchetypeId) - -> ArchetypeRefIter<'_> - { - let archetype_lookup = self.archetype_lookup.borrow(); + if !search_terms.archetype_contains_all_required(node.archetype()) { + continue; + } - // 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(); + found.push(node.archetype().id()); - ArchetypeRefIter { - indices: archetype_indices.into_iter(), - archetypes: &self.archetypes, + search_iter.pop(); } + + found } +} - fn create_populated_archetype_lookup_entry<CompMetadataIter>( +#[cfg(feature = "vizoxide")] +impl Storage +{ + pub fn create_vizoxide_archetype_graph( &self, - comp_metadata_iter: CompMetadataIter, - ) -> ArchetypeLookupEntry - where - CompMetadataIter: IntoIterator<Item: Borrow<ComponentMetadata>>, + graph_name: impl AsRef<str>, + params: VizoxideArchetypeGraphParams, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> { - let comp_ids_set = create_non_opt_component_id_set(comp_metadata_iter); - - let mut exact_matching_archetype_index = None; - - let matching_archetype_indices = self - .archetypes - .iter() - .enumerate() - .filter_map(|(index, archetype)| { - if archetype.component_ids_is(&comp_ids_set) { - exact_matching_archetype_index = Some(index); + let viz_graph = vizoxide::Graph::builder(graph_name.as_ref()) + .strict(true) + .directed(true) + .build()?; + + let mut viz_node_lookup = HashMap::new(); + + for node in self.graph.iter_nodes() { + let id = node.archetype().id(); + + if !viz_node_lookup.contains_key(&id) { + let node = self.graph.get_node_by_id(id).unwrap(); + + let viz_node = (params.create_node_cb)( + node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + viz_graph.create_node(&(params.create_node_name)( + node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + )), + ) + .build()?; + + viz_node_lookup.insert(id, viz_node); + } - return None; + for (edge_comp_id, edges) in node.iter_edges() { + if let Some(add_edge) = edges.add { + if !viz_node_lookup.contains_key(&add_edge) { + let viz_node = self.create_vizoxide_archetype_graph_edge_node( + &viz_graph, + node, + add_edge, + *edge_comp_id, + ¶ms, + )?; + + viz_node_lookup.insert(add_edge, viz_node); + } + + (params.create_edge_cb)( + node.archetype(), + *edge_comp_id, + VizoxideArchetypeGraphEdgeKind::Add, + viz_graph.create_edge( + viz_node_lookup.get(&id).unwrap(), + viz_node_lookup.get(&add_edge).unwrap(), + Some(&format!("Add {}", edge_comp_id.id())), + ), + ) + .build()?; } - if archetype.component_ids_is_superset(&comp_ids_set) { - return Some(index); + if let Some(remove_edge) = edges.remove { + if !viz_node_lookup.contains_key(&remove_edge) { + let viz_node = self.create_vizoxide_archetype_graph_edge_node( + &viz_graph, + node, + remove_edge, + *edge_comp_id, + ¶ms, + )?; + + viz_node_lookup.insert(remove_edge, viz_node); + } + + (params.create_edge_cb)( + node.archetype(), + *edge_comp_id, + VizoxideArchetypeGraphEdgeKind::Remove, + viz_graph.create_edge( + viz_node_lookup.get(&id).unwrap(), + viz_node_lookup.get(&remove_edge).unwrap(), + Some(&format!("Remove {}", edge_comp_id.id())), + ), + ) + .build()?; } + } + } - None - }) - .collect::<Vec<_>>(); - - ArchetypeLookupEntry { - component_ids: comp_ids_set, - archetype_indices: exact_matching_archetype_index - .into_iter() - .chain(matching_archetype_indices) - .collect(), + drop(viz_node_lookup); + + Ok(viz_graph) + } + + fn create_vizoxide_archetype_graph_edge_node<'vizoxide_graph>( + &self, + viz_graph: &'vizoxide_graph vizoxide::Graph, + node: &graph::ArchetypeNode, + edge_id: ArchetypeId, + edge_comp_id: Uid, + params: &VizoxideArchetypeGraphParams, + ) -> Result<vizoxide::Node<'vizoxide_graph>, vizoxide::GraphvizError> + { + match self.graph.get_node_by_id(edge_id) { + Some(edge_node) => (params.create_node_cb)( + edge_node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + viz_graph.create_node(&(params.create_node_name)( + edge_node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + )), + ) + .build(), + None => { + let mut comp_ids = + node.archetype().component_ids_sorted().collect::<Vec<_>>(); + + let insert_index = comp_ids.partition_point(|cid| *cid <= edge_comp_id); + + comp_ids.insert(insert_index, edge_comp_id); + + let imaginary_edge_archetype = Archetype::new(edge_id, comp_ids); + + (params.create_node_cb)( + &imaginary_edge_archetype, + ArchetypeMetadata { is_imaginary: true }, + viz_graph.create_node(&(params.create_node_name)( + &imaginary_edge_archetype, + ArchetypeMetadata { is_imaginary: true }, + )), + ) + .build() + } } } } -/// Component storage error -#[derive(Debug, Clone, thiserror::Error)] -pub enum Error +#[cfg(feature = "vizoxide")] +pub struct VizoxideArchetypeGraphParams { - #[error("Entity already exists")] - EntityAlreadyExists(Uid), + pub create_node_name: fn(&Archetype, ArchetypeMetadata) -> std::borrow::Cow<'_, str>, + pub create_node_cb: for<'storage, 'graph> fn( + &'storage Archetype, + ArchetypeMetadata, + vizoxide::NodeBuilder<'graph>, + ) -> vizoxide::NodeBuilder<'graph>, + pub create_edge_cb: for<'storage, 'graph> fn( + &'storage Archetype, + Uid, + VizoxideArchetypeGraphEdgeKind, + vizoxide::EdgeBuilder<'graph>, + ) -> vizoxide::EdgeBuilder<'graph>, } -impl TypeName for Storage +#[cfg(feature = "vizoxide")] +#[derive(Debug, Clone)] +pub struct ArchetypeMetadata { - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } + pub is_imaginary: bool, } -#[derive(Debug)] -struct ArchetypeLookupEntry +#[cfg(feature = "vizoxide")] +#[derive(Debug, Clone, Copy)] +pub enum VizoxideArchetypeGraphEdgeKind { - component_ids: HashSet<Uid>, - archetype_indices: Vec<usize>, + Add, + Remove, } #[derive(Debug)] -pub struct Archetype +pub struct ArchetypeRefIter<'storage, 'search_terms> { - component_ids: HashMap<Uid, usize>, - entity_lookup: HashMap<Uid, usize>, - entity_uid_lookup: Vec<Uid>, - entities: Vec<ArchetypeEntity>, + storage: &'storage Storage, + pre_iter: Either<ArrayIter<ArchetypeId, 1>, VecIntoIter<ArchetypeId>>, + dfs_iter: ArchetypeAddEdgeDfsIter<'storage>, + search_terms: ArchetypeSearchTerms<'search_terms>, } -impl Archetype +impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_> { - 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(), - entity_uid_lookup: Vec::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 - } - } + type Item = &'component_storage Archetype; - pub fn component_ids_is(&self, other_component_ids: &HashSet<Uid>) -> bool + fn next(&mut self) -> Option<Self::Item> { - if other_component_ids.len() == self.component_ids.len() { - other_component_ids - .iter() - .all(|v| self.component_ids.contains_key(v)) - } else { - false + if let Some(pre_iter_archetype_id) = self.pre_iter.next() { + return Some( + self.storage + .get_archetype_by_id(pre_iter_archetype_id) + .expect("Archetype should exist"), + ); } - } - - 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 entity_cnt(&self) -> usize - { - self.entities.len() - } + let archetype_id = loop { + match self.dfs_iter.streaming_find(|res| { + matches!( + res, + ArchetypeAddEdgeDfsIterResult::AddEdge { .. } + | ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { .. } + ) + })? { + ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id, + add_edge_component_id, + } => { + if self.search_terms.excluded_contains(add_edge_component_id) { + self.dfs_iter.pop(); + continue; + } + + break add_edge_archetype_id; + } + ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { + archetype, + add_edge_archetype_id, + add_edge_component_id, + } => { + if self.search_terms.excluded_contains(add_edge_component_id) { + continue; + } + + let mut add_edge_archetype_comps = + archetype.component_ids_sorted().collect::<Vec<_>>(); + + add_edge_archetype_comps.insert_at_partition_point_by_key( + add_edge_component_id, + |comp_id| *comp_id, + ); + + self.storage.imaginary_archetypes.borrow_mut().push( + ImaginaryArchetype { + id: add_edge_archetype_id, + component_ids: add_edge_archetype_comps.clone(), + }, + ); + + let found = + self.find_edges_of_imaginary_archetype(&add_edge_archetype_comps); + + self.dfs_iter.push(( + BorrowedOrOwned::Owned(Archetype::new( + add_edge_archetype_id, + add_edge_archetype_comps.clone(), + )), + found.into_iter(), + )); + } + _ => { + unreachable!(); + } + } + }; - pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize> - { - self.component_ids.get(&component_id).copied() + Some( + self.storage + .get_archetype_by_id(archetype_id) + .expect("Archetype should exist"), + ) } +} - fn push_entity( - &mut self, - entity_uid: Uid, - components: impl IntoIterator<Item = Box<dyn Component>>, - ) +impl ArchetypeRefIter<'_, '_> +{ + fn find_edges_of_imaginary_archetype( + &self, + imaginary_archetype_comps: &[Uid], + ) -> Vec<(Uid, ArchetypeEdges)> { - self.entities.push(ArchetypeEntity { - uid: entity_uid, - components: components.into_iter().map(Into::into).collect(), - }); - - let index = self.entities.len() - 1; - - self.entity_lookup.insert(entity_uid, index); - - self.entity_uid_lookup.push(entity_uid); - } + self.storage + .find_all_archetype_with_comps(&ArchetypeSearchTerms { + required_components: imaginary_archetype_comps, + excluded_components: &[], + }) + .into_iter() + .filter_map(|found_id| { + let found_archetype = self.storage.get_archetype_by_id(found_id).unwrap(); - pub fn take_entity(&mut self, entity_uid: Uid) -> Option<ArchetypeEntity> - { - let entity_index = self.entity_lookup.remove(&entity_uid)?; + if found_archetype.component_cnt() < imaginary_archetype_comps.len() + 1 { + return None; + } - let last_entity_uid = *self - .entity_uid_lookup - .get(self.entities.len() - 1) - .expect("Entity UID lookup contains too few entity UIDS"); + let unique_comp_id = found_archetype + .component_ids_sorted() + .find(|found_archetype_comp_id| { + !imaginary_archetype_comps.iter().any( + |imaginary_archetype_comp_id| { + *imaginary_archetype_comp_id == *found_archetype_comp_id + }, + ) + }) + .expect("Oh noooo"); - // By using swap_remove, no memory reallocation occurs and only one index in the - // entity lookup needs to be updated - let removed_entity = self.entities.swap_remove(entity_index); + let mut add_edge_comp_ids = imaginary_archetype_comps.to_vec(); - self.entity_lookup.insert(last_entity_uid, entity_index); + add_edge_comp_ids + .insert_at_partition_point_by_key(unique_comp_id, |id| *id); - self.entity_uid_lookup.swap_remove(entity_index); + let add_edge = ArchetypeId::new(&add_edge_comp_ids); - Some(removed_entity) + Some(( + unique_comp_id, + ArchetypeEdges { add: Some(add_edge), remove: None }, + )) + }) + .collect::<Vec<_>>() } } -#[derive(Debug)] -pub struct ArchetypeEntity -{ - uid: Uid, - components: Vec<EntityComponent>, -} - -impl ArchetypeEntity +#[derive(Debug, thiserror::Error)] +pub enum Error { - pub fn uid(&self) -> Uid - { - self.uid - } + #[error("Entity with ID {0:?} already exists")] + EntityAlreadyExists(Uid), - pub fn components(&self) -> &[EntityComponent] - { - &self.components - } + #[error("Entity with ID {0:?} does not exist")] + EntityDoesNotExist(Uid), - pub fn get_component(&self, index: usize) -> Option<&EntityComponent> + #[error("Entity with ID {entity:?} already has component with ID {component:?}")] + ComponentAlreadyInEntity { - 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; + entity: Uid, component: Uid + }, - fn next(&mut self) -> Option<Self::Item> + #[error("Entity with ID {entity:?} does not have component with ID {component:?}")] + ComponentNotFoundInEntity { - let archetype_index = self.indices.next()?; - - Some( - self.archetypes - .get(archetype_index) - .expect("Archetype index in archetype lookup entry was not found"), - ) - } + entity: Uid, component: Uid + }, } #[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>, +struct ImaginaryArchetype { - component_metadata_iter - .into_iter() - .filter_map(|item| { - let component_metadata = item.borrow(); - - if component_metadata.is_optional { - return None; - } - - Some(component_metadata.id) - }) - .collect::<HashSet<_>>() + id: ArchetypeId, + component_ids: Vec<Uid>, } #[cfg(test)] mod tests { - - use ecs_macros::Component; - - use super::Storage; - use crate::archetype::Id as ArchetypeId; - use crate::component::{Component, Metadata as ComponentMetadata}; + use crate::component::storage::archetype::Id as ArchetypeId; + use crate::component::storage::Storage; 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() + fn create_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"); + let mut new_storage = Storage::default(); - assert_eq!(archetype.component_ids.len(), 2); + let uid = Uid::new_unique(UidKind::Entity); - // One entity - assert_eq!(archetype.entities.len(), 1); + new_storage.create_entity(uid).expect("Expected Ok"); - let entity_components = archetype - .entities - .first() - .expect("Expected a entity in archetype"); + let archetype_node = new_storage + .graph + .get_node_by_id(ArchetypeId::new_empty()) + .expect("Archetype for entities with no component doesn't exist"); - assert_eq!(entity_components.components.len(), 2); + assert_eq!(archetype_node.archetype().component_cnt(), 0); + assert_eq!(archetype_node.archetype().entity_cnt(), 1); - assert_eq!(component_storage.archetype_lookup.borrow().len(), 1); - - let mut components_metadata = [ - ComponentMetadata { - id: HealthPotion::id(), - is_optional: false, - }, - ComponentMetadata { - id: Hookshot::id(), - is_optional: false, - }, - ]; - - 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); + assert_eq!( + new_storage.entity_archetype_lookup.get(&uid).copied(), + Some(ArchetypeId::new_empty()) + ); } } diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs new file mode 100644 index 0000000..bb29701 --- /dev/null +++ b/ecs/src/component/storage/archetype.rs @@ -0,0 +1,387 @@ +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::lock::Lock; +use crate::uid::{Kind as UidKind, Uid}; +use crate::util::{Either, HashMapExt}; + +#[derive(Debug)] +pub struct Archetype +{ + id: Id, + entities: Vec<Entity>, + entity_index_lookup: HashMap<Uid, usize>, + component_index_lookup: HashMap<Uid, usize>, + component_ids: Vec<Uid>, +} + +impl Archetype +{ + pub fn new(id: Id, component_ids: impl AsRef<[Uid]>) -> Self + { + Self { + id, + entities: Vec::new(), + entity_index_lookup: HashMap::new(), + component_index_lookup: component_ids + .as_ref() + .iter() + .enumerate() + .map(|(index, id)| (*id, index)) + .collect(), + component_ids: component_ids.as_ref().to_vec(), + } + } + + pub fn id(&self) -> Id + { + self.id + } + + pub fn is_superset(&self, other: &Self) -> bool + { + self.component_index_lookup + .keys_is_superset(&other.component_index_lookup) + } + + pub fn is_subset(&self, other: &Self) -> bool + { + self.component_index_lookup + .keys_is_subset(&other.component_index_lookup) + } + + pub fn get_entity_by_id(&self, entity_uid: Uid) -> Option<&Entity> + { + let index = *self.entity_index_lookup.get(&entity_uid)?; + + Some(self.entities.get(index).unwrap_or_else(|| { + panic!( + "In invalid state! Index of entity with ID {entity_uid:?} is out of bounds" + ); + })) + } + + pub fn push_entity(&mut self, entity: Entity) + { + self.entity_index_lookup + .insert(entity.uid, self.entities.len()); + + self.entities.push(entity); + } + + pub fn remove_entity(&mut self, entity_uid: Uid) -> Option<Entity> + { + //debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + + let entity_index = self.entity_index_lookup.remove(&entity_uid)?; + + if self.entities.len() == 1 { + return Some(self.entities.remove(entity_index)); + } + + let last_entity_uid = self + .entities + .last() + .expect(concat!( + "Invalid state. No entities in archetype but entry was ", + "removed successfully from entity index lookup" + )) + .uid; + + // By using swap_remove, no memory reallocation occurs and only one index in the + // entity lookup needs to be updated + let removed_entity = self.entities.swap_remove(entity_index); + + self.entity_index_lookup + .insert(last_entity_uid, entity_index); + + Some(removed_entity) + } + + pub fn entities(&self) -> EntityIter<'_> + { + EntityIter { iter: self.entities.iter() } + } + + pub fn entity_cnt(&self) -> usize + { + self.entities.len() + } + + pub fn component_cnt(&self) -> usize + { + 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() + } + + pub fn component_ids_unsorted(&self) -> impl Iterator<Item = Uid> + '_ + { + self.component_index_lookup.keys().copied() + } + + pub fn component_ids_sorted(&self) -> impl Iterator<Item = Uid> + '_ + { + self.component_ids.iter().copied() + } + + pub fn contains_matching_component(&self, component_id: Uid) -> bool + { + 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, Entity>, +} + +impl<'archetype> Iterator for EntityIter<'archetype> +{ + type Item = &'archetype Entity; + + fn next(&mut self) -> Option<Self::Item> + { + self.iter.next() + } +} + +#[derive(Debug)] +pub struct Entity +{ + uid: Uid, + components: Vec<EntityComponent>, +} + +impl Entity +{ + 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); + } +} + +#[derive(Debug)] +pub struct EntityComponent +{ + id: Uid, + component: Lock<Box<dyn Any>>, +} + +impl EntityComponent +{ + pub fn new( + component: Box<dyn Any>, + component_id: Uid, + component_name: &'static str, + ) -> Self + { + Self { + id: component_id, + component: Lock::new(component, component_name), + } + } + + #[allow(dead_code)] + pub fn id(&self) -> Uid + { + self.id + } + + pub fn component(&self) -> &Lock<Box<dyn Any>> + { + &self.component + } +} + +/// Archetype ID. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Id +{ + hash: u64, +} + +impl Id +{ + pub fn new_empty() -> Self + { + Self { hash: 0 } + } + + 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 component_id_iter = component_ids.into_iter().peekable(); + + if component_id_iter.peek().is_none() { + return Self::new_empty(); + } + + 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_id); + + 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 new file mode 100644 index 0000000..29fa937 --- /dev/null +++ b/ecs/src/component/storage/graph.rs @@ -0,0 +1,432 @@ +use std::vec::IntoIter as VecIntoIter; + +use hashbrown::{HashMap, HashSet}; + +use crate::component::storage::archetype::{Archetype, Id as ArchetypeId}; +use crate::uid::{Kind as UidKind, Uid}; +use crate::util::{BorrowedOrOwned, StreamingIterator}; + +#[derive(Debug, Default)] +pub struct Graph +{ + nodes: Vec<ArchetypeNode>, + archetype_index_lookup: HashMap<ArchetypeId, usize>, +} + +impl Graph +{ + pub fn create_node(&mut self, id: ArchetypeId, component_ids: &impl AsRef<[Uid]>) + { + debug_assert!(!self.contains_archetype(id)); + + let _ = self.get_or_create_node(id, component_ids); + } + + pub fn get_or_create_node( + &mut self, + id: ArchetypeId, + component_ids: &impl AsRef<[Uid]>, + ) -> &mut ArchetypeNode + { + let exists_before = self.archetype_index_lookup.contains_key(&id); + + let index = *self.archetype_index_lookup.entry(id).or_insert_with(|| { + self.nodes.push(ArchetypeNode { + archetype: Archetype::new(id, component_ids.as_ref()), + edges: HashMap::new(), + }); + + self.nodes.len() - 1 + }); + + if !exists_before { + self.create_missing_edges(id); + } + + self.nodes + .get_mut(index) + .expect("Archetype index from lookup is out of bounds") + } + + pub fn contains_archetype(&self, id: ArchetypeId) -> bool + { + self.archetype_index_lookup.contains_key(&id) + } + + pub fn get_node_by_id(&self, id: ArchetypeId) -> Option<&ArchetypeNode> + { + let index = self.archetype_index_lookup.get(&id)?; + + Some(self.nodes.get(*index).unwrap_or_else(|| { + panic!("In invalid state! Index of archetype with ID {id:?} is out of bounds") + })) + } + + pub fn get_node_by_id_mut(&mut self, id: ArchetypeId) -> Option<&mut ArchetypeNode> + { + let index = self.archetype_index_lookup.get(&id)?; + + Some(self.nodes.get_mut(*index).unwrap_or_else(|| { + panic!("In invalid state! Index of archetype with ID {id:?} is out of bounds") + })) + } + + #[cfg(feature = "vizoxide")] + pub fn iter_nodes(&self) -> impl Iterator<Item = &ArchetypeNode> + { + self.nodes.iter() + } + + pub fn dfs_archetype_add_edges( + &self, + archetype_id: ArchetypeId, + ) -> Option<ArchetypeAddEdgeDfsIter> + { + let node = self.get_node_by_id(archetype_id)?; + + Some(ArchetypeAddEdgeDfsIter { + graph: self, + stack: vec![( + BorrowedOrOwned::Borrowned(node.archetype()), + node.edges + .iter() + .map(|(comp_id, edges)| (*comp_id, edges.clone())) + .collect::<Vec<_>>() + .into_iter(), + )], + visited: HashSet::new(), + }) + } + + fn create_missing_edges(&mut self, archetype_id: ArchetypeId) + { + let archetype_node_index = *self + .archetype_index_lookup + .get(&archetype_id) + .expect("Archetype should exist"); + + let (nodes_before, nodes_rest) = self.nodes.split_at_mut(archetype_node_index); + + let ([archetype_node], nodes_after) = nodes_rest.split_at_mut(1) else { + unreachable!(); + }; + + for other_archetype_node in nodes_before.iter_mut().chain(nodes_after.iter_mut()) + { + if archetype_node.archetype().component_cnt() + > other_archetype_node.archetype().component_cnt() + && other_archetype_node + .archetype() + .is_subset(archetype_node.archetype()) + { + Self::create_missing_subset_node_edges( + archetype_node, + other_archetype_node, + ); + + continue; + } + + if other_archetype_node + .archetype() + .is_superset(archetype_node.archetype()) + { + Self::create_missing_superset_node_edges( + archetype_node, + other_archetype_node, + ); + } + } + } + + fn create_missing_subset_node_edges( + target_node: &mut ArchetypeNode, + subset_node: &mut ArchetypeNode, + ) + { + let uniq_comp_id = target_node + .archetype() + .component_ids_sorted() + .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( + target_node: &mut ArchetypeNode, + superset_node: &mut ArchetypeNode, + ) + { + if superset_node.archetype().component_cnt() + > target_node.archetype().component_cnt() + 1 + { + let first_unique_comp_id = superset_node + .archetype() + .component_ids_sorted() + .find(|other_archetype_comp_id| { + !target_node + .archetype() + .contains_component_with_exact_id(*other_archetype_comp_id) + }) + .or_else(|| { + if target_node.archetype().component_cnt() != 0 { + return None; + } + + superset_node.archetype().component_ids_sorted().next() + }) + .expect("Not possible"); + + target_node + .get_or_insert_edges(first_unique_comp_id, ArchetypeEdges::default) + .add = Some(target_node.make_add_edge(first_unique_comp_id).0); + + return; + } + + if superset_node.archetype().component_cnt() + != target_node.archetype().component_cnt() + 1 + { + return; + } + + let extra_comp_id = superset_node + .archetype() + .component_ids_unsorted() + .find(|comp_id| { + !target_node + .archetype() + .contains_component_with_exact_id(*comp_id) + }) + .expect("Archetype should contain one extra component ID"); + + superset_node + .get_or_insert_edges(extra_comp_id, ArchetypeEdges::default) + .remove = Some(target_node.archetype().id()); + + target_node + .get_or_insert_edges(extra_comp_id, ArchetypeEdges::default) + .add = Some(superset_node.archetype().id()); + } +} + +#[derive(Debug)] +pub struct ArchetypeNode +{ + archetype: Archetype, + edges: HashMap<Uid, ArchetypeEdges>, +} + +impl ArchetypeNode +{ + pub fn archetype(&self) -> &Archetype + { + &self.archetype + } + + pub fn archetype_mut(&mut self) -> &mut Archetype + { + &mut self.archetype + } + + pub fn get_or_insert_edges( + &mut self, + component_id: Uid, + insert_fn: impl FnOnce() -> ArchetypeEdges, + ) -> &mut ArchetypeEdges + { + debug_assert!(matches!( + component_id.kind(), + UidKind::Component | UidKind::Pair + )); + + self.edges.entry(component_id).or_insert_with(insert_fn) + } + + #[cfg(feature = "vizoxide")] + pub fn iter_edges(&self) -> impl Iterator<Item = (&Uid, &ArchetypeEdges)> + { + self.edges.iter() + } + + pub fn make_add_edge(&self, component_id: Uid) -> (ArchetypeId, Vec<Uid>) + { + let mut edge_comp_ids = self + .archetype() + .component_ids_unsorted() + .chain([component_id]) + .collect::<Vec<_>>(); + + edge_comp_ids.sort(); + + let add_edge_id = ArchetypeId::new(&edge_comp_ids); + + (add_edge_id, edge_comp_ids) + } + + pub fn make_remove_edge(&self, component_id: Uid) -> (ArchetypeId, Vec<Uid>) + { + let mut edge_comp_ids = self + .archetype() + .component_ids_unsorted() + .filter(|id| *id != component_id) + .collect::<Vec<_>>(); + + edge_comp_ids.sort(); + + let remove_edge_id = ArchetypeId::new(&edge_comp_ids); + + (remove_edge_id, edge_comp_ids) + } +} + +#[derive(Debug, Default, Clone)] +pub struct ArchetypeEdges +{ + pub add: Option<ArchetypeId>, + pub remove: Option<ArchetypeId>, +} + +type ArchetypeAddEdgeDfsIterStackElem<'graph> = ( + BorrowedOrOwned<'graph, Archetype>, + VecIntoIter<(Uid, ArchetypeEdges)>, +); + +#[derive(Debug)] +pub struct ArchetypeAddEdgeDfsIter<'graph> +{ + graph: &'graph Graph, + stack: Vec<ArchetypeAddEdgeDfsIterStackElem<'graph>>, + visited: HashSet<ArchetypeId>, +} + +impl<'graph> ArchetypeAddEdgeDfsIter<'graph> +{ + pub fn new(graph: &'graph Graph, start_nodes: &[ArchetypeId]) -> Self + { + Self { + graph, + stack: start_nodes + .iter() + .map(|start_node_id| { + let start_node = graph + .get_node_by_id(*start_node_id) + .expect("Start node does not exist"); + + ( + BorrowedOrOwned::Borrowned(start_node.archetype()), + start_node + .edges + .iter() + .map(|(comp_id, edges)| (*comp_id, edges.clone())) + .collect::<Vec<_>>() + .into_iter(), + ) + }) + .collect(), + visited: start_nodes.iter().copied().collect::<HashSet<_>>(), + } + } + + pub fn push( + &mut self, + item: ( + BorrowedOrOwned<'graph, Archetype>, + VecIntoIter<(Uid, ArchetypeEdges)>, + ), + ) + { + self.stack.push(item); + } + + pub fn pop(&mut self) + { + self.stack.pop(); + } +} + +impl<'graph> StreamingIterator for ArchetypeAddEdgeDfsIter<'graph> +{ + type Item<'a> + = ArchetypeAddEdgeDfsIterResult<'graph, 'a> + where + Self: 'a; + + fn streaming_next(&mut self) -> Option<Self::Item<'_>> + { + let (_, edges_iter) = self.stack.last_mut()?; + + let Some((component_id, edges)) = edges_iter.next() else { + self.stack.pop(); + + return Some(ArchetypeAddEdgeDfsIterResult::NoEdgesLeftForArchetype); + }; + + let Some(add_edge) = edges.add else { + return Some(ArchetypeAddEdgeDfsIterResult::NoAddEdge); + }; + + if self.visited.contains(&add_edge) { + return Some(ArchetypeAddEdgeDfsIterResult::AddEdgeAlreadyVisited); + } + + self.visited.insert(add_edge); + + let Some(add_edge_archetype) = self.graph.get_node_by_id(add_edge) else { + return Some(ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound { + archetype: &self.stack.last().unwrap().0, + add_edge_archetype_id: add_edge, + add_edge_component_id: component_id, + }); + }; + + self.stack.push(( + BorrowedOrOwned::Borrowned(add_edge_archetype.archetype()), + add_edge_archetype + .edges + .iter() + .map(|(comp_id, edges)| (*comp_id, edges.clone())) + .collect::<Vec<_>>() + .into_iter(), + )); + + Some(ArchetypeAddEdgeDfsIterResult::AddEdge { + add_edge_archetype_id: add_edge, + add_edge_component_id: component_id, + }) + } +} + +#[derive(Debug)] +pub enum ArchetypeAddEdgeDfsIterResult<'graph, 'iter> +{ + AddEdge + { + add_edge_archetype_id: ArchetypeId, + add_edge_component_id: Uid, + }, + NoEdgesLeftForArchetype, + NoAddEdge, + AddEdgeAlreadyVisited, + AddEdgeArchetypeNotFound + { + archetype: &'iter BorrowedOrOwned<'graph, Archetype>, + add_edge_archetype_id: ArchetypeId, + add_edge_component_id: Uid, + }, +} diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 3de9cd5..bab3d61 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -1,6 +1,133 @@ +use std::any::type_name; + use linkme::distributed_slice; -use crate::World; +use crate::component::storage::archetype::{ + Archetype, + Entity as ArchetypeEntity, + MatchingComponentIter as ArchetypeMatchingComponentIter, +}; +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, +}; +use crate::uid::{Kind as UidKind, Uid}; +use crate::{EntityComponentRef, World}; + +/// A handle to a entity. +#[derive(Debug)] +pub struct Handle<'a> +{ + archetype: &'a Archetype, + entity: &'a ArchetypeEntity, +} + +impl<'a> Handle<'a> +{ + /// Returns the [`Uid`] of this entity. + #[inline] + #[must_use] + pub fn uid(&self) -> Uid + { + self.entity.uid() + } + + /// Returns a reference to the specified component in this entity. `None` is + /// returned if the component isn't found in the entity. + /// + /// # Panics + /// Will panic if: + /// - The component's ID is not a component ID + /// - The component is mutably borrowed elsewhere + #[must_use] + pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'_, ComponentT>> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let component = self.get_matching_components(ComponentT::id()).next()?; + + Some( + ComponentHandle::from_entity_component_ref(component).unwrap_or_else(|err| { + panic!( + "Taking component {} lock failed: {err}", + type_name::<ComponentT>() + ); + }), + ) + } + + /// Returns a mutable reference to the specified component in this entity. `None` is + /// returned if the component isn't found in the entity. + /// + /// # Panics + /// Will panic if: + /// - The component's ID is not a component ID + /// - The component is borrowed elsewhere + #[must_use] + pub fn get_mut<ComponentT: Component>( + &self, + ) -> Option<ComponentHandleMut<'_, ComponentT>> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let component = self.get_matching_components(ComponentT::id()).next()?; + + Some( + ComponentHandleMut::from_entity_component_ref(component).unwrap_or_else( + |err| { + panic!( + "Taking component {} lock failed: {err}", + type_name::<ComponentT>() + ); + }, + ), + ) + } + + #[inline] + #[must_use] + pub fn get_matching_components(&self, component_uid: Uid) + -> MatchingComponentIter<'a> + { + MatchingComponentIter { + inner: self.archetype.get_matching_component_indices(component_uid), + entity: self.entity, + } + } + + pub fn component_ids(&self) -> impl Iterator<Item = Uid> + '_ + { + self.archetype.component_ids_sorted() + } + + pub(crate) fn new(archetype: &'a Archetype, entity: &'a ArchetypeEntity) -> Self + { + Self { archetype, entity } + } +} + +#[derive(Debug)] +pub struct MatchingComponentIter<'a> +{ + inner: ArchetypeMatchingComponentIter<'a>, + entity: &'a ArchetypeEntity, +} + +impl<'a> Iterator for MatchingComponentIter<'a> +{ + type Item = EntityComponentRef<'a>; + + fn next(&mut self) -> Option<Self::Item> + { + let (matching_component_id, index) = self.inner.next()?; + + Some(EntityComponentRef::new( + matching_component_id, + self.entity.components().get(index).unwrap(), + )) + } +} #[allow(clippy::module_name_repetitions)] #[macro_export] @@ -19,7 +146,7 @@ macro_rules! static_entity { $crate::entity::CREATE_STATIC_ENTITIES )] #[linkme(crate=$crate::private::linkme)] - static CREATE_STATIC_ENTITY: fn(&$crate::World) = |world| { + static CREATE_STATIC_ENTITY: fn(&mut $crate::World) = |world| { world.create_entity_with_uid($components, *$ident); }; } @@ -29,4 +156,4 @@ macro_rules! static_entity { #[distributed_slice] #[doc(hidden)] -pub static CREATE_STATIC_ENTITIES: [fn(&World)]; +pub static CREATE_STATIC_ENTITIES: [fn(&mut World)]; diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs index b4edffc..72a78a3 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -1,84 +1,12 @@ //! Component events. -use std::fmt::{Debug, Formatter}; -use std::marker::PhantomData; +use std::convert::Infallible; +use std::fmt::Debug; -use ecs_macros::Component; +use crate::Component; -use crate::component::Component; - -/// Event emitted when: -/// a) A entity with component `ComponentT` is spawned. -/// b) A component `ComponentT` is added to a entity. -#[derive(Clone, Component)] -pub struct Added<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Debug for Added<ComponentT> -where - ComponentT: Component, -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("Added") - .field("_pd", &self._pd) - .finish() - } -} - -impl<ComponentT> Default for Added<ComponentT> -where - ComponentT: Component, -{ - fn default() -> Self - { - Self { _pd: PhantomData } - } -} - -/// Event emitted when: -/// a) A `ComponentT` component is removed from a entity. -/// b) A entity with component `ComponentT` is despawned. -#[derive(Clone, Component)] -pub struct Removed<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Debug for Removed<ComponentT> -where - ComponentT: Component, -{ - fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result - { - formatter - .debug_struct("Removed") - .field("_pd", &self._pd) - .finish() - } -} - -impl<ComponentT> Default for Removed<ComponentT> -where - ComponentT: Component, -{ - fn default() -> Self - { - Self { _pd: PhantomData } - } -} - -/// Specifies a kind of component event UID. -#[derive(Debug, Clone, Copy)] -#[non_exhaustive] -pub enum Kind -{ - Removed, -} +/// Pair relation for events emitted when: +/// a) A entity with the target component is spawned. +/// b) The target component is added to a entity. +#[derive(Debug, Component)] +pub struct Added(Infallible); diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 72b5cf9..07b1cba 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,58 +1,64 @@ #![deny(clippy::all, clippy::pedantic)] -use std::any::{type_name, TypeId}; +use std::any::{type_name, Any, TypeId}; use std::cell::RefCell; use std::fmt::Debug; use std::mem::ManuallyDrop; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use hashbrown::HashMap; use crate::actions::Action; +use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; use crate::component::storage::Storage as ComponentStorage; use crate::component::{ Component, - Metadata as ComponentMetadata, - RefSequence as ComponentRefSequence, + IntoParts, + Parts as ComponentParts, + Removals as ComponentRemovals, Sequence as ComponentSequence, }; -use crate::entity::CREATE_STATIC_ENTITIES; -use crate::event::component::Kind as ComponentEventKind; +use crate::entity::{Handle as EntityHandle, CREATE_STATIC_ENTITIES}; +use crate::event::component::Added as ComponentAddedEvent; use crate::extension::{Collector as ExtensionCollector, Extension}; -use crate::lock::{Lock, WriteGuard}; +use crate::lock::Lock; +use crate::pair::{ChildOf, DependsOn, Pair}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; -use crate::query::options::{Not, Options as QueryOptions, With}; -use crate::relationship::{ChildOf, DependsOn, Relationship}; +use crate::query::term::Without; +use crate::query::{ + Iter as QueryIter, + TermWithFieldTuple as QueryTermWithFieldTuple, + TermWithoutFieldTuple as QueryTermWithoutFieldTuple, + Terms as QueryTerms, + TermsBuilderInterface, +}; use crate::sole::Sole; use crate::stats::Stats; use crate::system::{System, SystemComponent}; -use crate::type_name::TypeName; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::Sortable; +use crate::uid::{Kind as UidKind, Uid, Wildcard}; pub mod actions; pub mod component; pub mod entity; pub mod event; pub mod extension; -pub mod lock; +pub mod pair; pub mod phase; pub mod query; -pub mod relationship; pub mod sole; pub mod stats; pub mod system; pub mod tuple; -pub mod type_name; pub mod uid; pub mod util; #[doc(hidden)] pub mod private; -mod archetype; +mod lock; pub use ecs_macros::{Component, Sole}; @@ -80,16 +86,13 @@ impl World world.add_sole(Stats::default()).ok(); for create_static_entity in CREATE_STATIC_ENTITIES { - create_static_entity(&world); + create_static_entity(&mut world); } world } /// Creates a new entity with the given components. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid where Comps: ComponentSequence, @@ -103,27 +106,25 @@ impl World #[tracing::instrument(skip_all)] #[doc(hidden)] - pub fn create_entity_with_uid<Comps>(&self, components: Comps, entity_uid: Uid) + pub fn create_entity_with_uid<Comps>(&mut self, components: Comps, entity_uid: Uid) where Comps: ComponentSequence, { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - #[allow(unused_variables)] - if let Err(err) = self - .data - .component_storage - .write_nonblock() - .expect("Failed to acquire read-write component storage lock") - .push_entity(entity_uid, components.into_array().into()) - { - tracing::error!("Failed to create entity: {err}"); - + if let Err(err) = self.data.component_storage.create_entity(entity_uid) { + tracing::warn!("Failed to create entity: {err}"); return; - }; + } + + let added_component_ids = Self::add_entity_components( + entity_uid, + components.into_parts_array(), + &mut self.data.component_storage, + ); - for added_event_id in Comps::added_event_ids() { - self.emit_event_by_id(added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } @@ -146,27 +147,23 @@ impl World { self.create_entity(( SystemComponent { system: system.into_type_erased() }, - Relationship::<DependsOn, Phase>::new(phase_euid), + Pair::new::<DependsOn>(phase_euid), )); } - pub fn register_observer_system<'this, SystemImpl, Event>( + pub fn register_observer_system<'this, SystemImpl>( &'this mut self, system: impl System<'this, SystemImpl>, - event: Event, - ) where - Event: Component, + event: Pair<Uid, Uid>, + ) { - self.create_entity::<(SystemComponent, Event)>(( + self.create_entity(( SystemComponent { system: system.into_type_erased() }, event, )); } /// Adds a extensions. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. pub fn add_extension(&mut self, extension: impl Extension) { let extension_collector = ExtensionCollector::new(self); @@ -174,29 +171,26 @@ impl World extension.collect(extension_collector); } - pub fn query<Comps, OptionsT>(&self) -> Query<Comps, OptionsT> + pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> where - Comps: ComponentRefSequence, - OptionsT: QueryOptions, + FieldTerms: QueryTermWithFieldTuple, + FieldlessTerms: QueryTermWithoutFieldTuple, { Query::new(self) } - pub fn flexible_query<CompMetadata>( + pub fn flexible_query<const MAX_TERM_CNT: usize>( &self, - comp_metadata: CompMetadata, - ) -> FlexibleQuery<CompMetadata> - where - CompMetadata: Sortable<Item = ComponentMetadata> + AsRef<[ComponentMetadata]>, + terms: QueryTerms<MAX_TERM_CNT>, + ) -> FlexibleQuery<'_, MAX_TERM_CNT> { - FlexibleQuery::new(self, comp_metadata) + FlexibleQuery::new(self, terms) } /// Performs a single tick. - /// /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn step(&self) -> StepResult + /// Will panic if mutable internal lock cannot be acquired. + pub fn step(&mut self) -> StepResult { if self.stop.load(Ordering::Relaxed) { return StepResult::Stop; @@ -212,8 +206,14 @@ impl World self.perform_phases(); + self.data.component_storage.create_imaginary_archetypes(); + + let prev_pending_removals = std::mem::take(&mut self.data.pending_removals); + self.perform_queued_actions(); + self.perform_removals(prev_pending_removals); + if self.stop.load(Ordering::Relaxed) { return StepResult::Stop; } @@ -236,26 +236,72 @@ impl World } /// Starts a loop which calls [`Self::step`] until the world is stopped. - /// - /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn start_loop(&self) + pub fn start_loop(&mut self) { while let StepResult::Continue = self.step() {} } - fn query_and_run_systems(&self, phase_euid: Uid) + #[cfg(feature = "vizoxide")] + pub fn create_vizoxide_archetype_graph( + &self, + name: impl AsRef<str>, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> { - let system_comps_query = - self.query::<(&SystemComponent, &Relationship<DependsOn, Phase>), ()>(); + use std::borrow::Cow; - let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| { - phase_rel - .target_uids() - .any(|target_uid| target_uid == phase_euid) - }); + use crate::component::storage::{ + VizoxideArchetypeGraphEdgeKind, + VizoxideArchetypeGraphParams, + }; + + self.data.component_storage.create_vizoxide_archetype_graph( + name, + VizoxideArchetypeGraphParams { + create_node_name: |archetype, _| { + Cow::Owned(format!( + "[{}]", + archetype + .component_ids_sorted() + .into_iter() + .map(|comp_id| comp_id.to_string()) + .collect::<Vec<_>>() + .join(", ") + )) + }, + create_node_cb: |_archetype, archetype_metadata, node_builder| { + if archetype_metadata.is_imaginary { + return node_builder.attribute("shape", "ellipse"); + } + + node_builder.attribute("shape", "box") + }, + create_edge_cb: |_, _, edge_kind, edge_builder| { + edge_builder.attribute( + "color", + match edge_kind { + VizoxideArchetypeGraphEdgeKind::Add => "green", + VizoxideArchetypeGraphEdgeKind::Remove => "red", + }, + ) + }, + }, + ) + } - for (system_component, _) in system_iter { + fn query_and_run_systems(&self, phase_euid: Uid) + { + let system_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + SystemComponent::id(), + Pair::new::<DependsOn>(phase_euid).id(), + ]) + .build(), + ); + + for (system_component,) in + QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter()) + { // SAFETY: The world lives long enough unsafe { system_component.system.run(self); @@ -265,38 +311,37 @@ impl World fn perform_child_phases(&self, parent_phase_euid: Uid) { - let phase_query = self.query::<(&Phase, &Relationship<ChildOf, Phase>), ()>(); - - for (child_phase_euid, (_, phase_rel)) in phase_query.iter_with_euids() { - if !phase_rel - .target_uids() - .any(|phase_euid| phase_euid == parent_phase_euid) - { - continue; - } + let phase_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + Phase::id(), + Pair::new::<ChildOf>(parent_phase_euid).id(), + ]) + .build(), + ); - self.query_and_run_systems(child_phase_euid); - self.perform_child_phases(child_phase_euid); + for child_phase_entity in &phase_query { + self.query_and_run_systems(child_phase_entity.uid()); + self.perform_child_phases(child_phase_entity.uid()); } } fn perform_phases(&self) { - let phase_query = - self.query::<(&Phase,), Not<With<Relationship<ChildOf, Phase>>>>(); + let phase_query = self.query::<(&Phase,), (Without<Pair<ChildOf, Wildcard>>,)>(); - for (phase_euid, (_,)) in phase_query.iter_with_euids() { - if phase_euid == *START_PHASE { + for (phase_entity_id, _) in phase_query.iter_with_euids() { + if phase_entity_id == *START_PHASE { continue; } - self.query_and_run_systems(phase_euid); - self.perform_child_phases(phase_euid); + self.query_and_run_systems(phase_entity_id); + self.perform_child_phases(phase_entity_id); } } #[tracing::instrument(skip_all)] - fn perform_queued_actions(&self) + fn perform_queued_actions(&mut self) { let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() { @@ -313,76 +358,64 @@ impl World let mut has_swapped_active_queue = false; + // TODO: Figure out a good way to handle situations where there are multiple + // AddComponents/RemoveComponents actions that affect the same entity. for action in active_action_queue.drain(..) { match action { - Action::Spawn(components, component_added_event_ids) => { - let mut component_storage_lock = self.lock_component_storage_rw(); + Action::Spawn(components) => { + let new_entity_uid = Uid::new_unique(UidKind::Entity); - #[allow(unused_variables)] - if let Err(err) = component_storage_lock - .push_entity(Uid::new_unique(UidKind::Entity), components) + if let Err(err) = + self.data.component_storage.create_entity(new_entity_uid) { - tracing::error!("Failed to create entity: {err}"); - + tracing::warn!("Failed to create entity: {err}"); continue; } - drop(component_storage_lock); + let added_component_ids = Self::add_entity_components( + new_entity_uid, + components, + &mut self.data.component_storage, + ); if !has_swapped_active_queue { self.swap_event_queue(&mut has_swapped_active_queue); } - for comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } Action::Despawn(entity_uid) => { - self.despawn_entity(entity_uid, &mut has_swapped_active_queue); + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, + entity_uid, + PendingRemoval::Entity, + ); } - Action::AddComponents( - entity_uid, - components, - component_added_event_ids, - ) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - component_storage_lock - .add_components_to_entity(entity_uid, components); - - drop(component_storage_lock); + Action::AddComponents(entity_uid, components) => { + let added_component_ids = Self::add_entity_components( + entity_uid, + components, + &mut self.data.component_storage, + ); if !has_swapped_active_queue { self.swap_event_queue(&mut has_swapped_active_queue); } - for comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } - Action::RemoveComponents( - entity_uid, - components_metadata, - component_removed_event_ids, - ) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - component_storage_lock.remove_components_from_entity( + Action::RemoveComponents(entity_uid, component_ids) => { + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, entity_uid, - components_metadata - .iter() - .map(|component_metadata| component_metadata.id), + PendingRemoval::Components(component_ids), ); - - drop(component_storage_lock); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_removed_event_id in component_removed_event_ids.ids { - self.emit_event_by_id(comp_removed_event_id); - } } Action::Stop => { self.stop.store(true, Ordering::Relaxed); @@ -391,62 +424,132 @@ impl World } } - fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool) + fn perform_removals(&mut self, removals: Vec<(Uid, PendingRemoval)>) + { + for (entity_id, removal) in removals { + match removal { + PendingRemoval::Components(component_ids) => { + Self::remove_entity_components( + entity_id, + component_ids.into_iter().chain([ComponentRemovals::id()]), + &mut self.data.component_storage, + ); + } + PendingRemoval::Entity => { + if let Err(err) = self.data.component_storage.remove_entity(entity_id) + { + tracing::error!("Failed to remove entity {entity_id}: {err}"); + } + } + } + } + } + + #[tracing::instrument(skip(component_storage, pending_removals))] + fn schedule_removal( + component_storage: &mut ComponentStorage, + pending_removals: &mut Vec<(Uid, PendingRemoval)>, + entity_uid: Uid, + removal: PendingRemoval, + ) { - let mut component_storage_lock = self.lock_component_storage_rw(); + let Some(ent_handle) = Self::get_entity(component_storage, entity_uid) else { + tracing::warn!("Cannot schedule removal. Entity does not exist"); + return; + }; - let Some(archetype) = component_storage_lock.get_entity_archetype(entity_uid) + let component_ids = match removal { + PendingRemoval::Components(ref component_ids) => component_ids, + PendingRemoval::Entity => &ent_handle.component_ids().collect::<Vec<_>>(), + }; + + let Some(mut component_removals) = ent_handle.get_mut::<ComponentRemovals>() else { - tracing::error!("No archetype for entity {entity_uid:?} was found"); + Self::add_entity_components( + entity_uid, + [ComponentRemovals::from_iter(component_ids.iter().copied()) + .into_parts()], + component_storage, + ); + + pending_removals.push((entity_uid, removal)); return; }; - let entity = archetype - .get_entity(entity_uid) - .expect("Entity archetype was found but the entity is not in the archetype"); - - let component_removed_event_uids = entity - .components() - .iter() - .map(|component| { - component - .component - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to acquire read-only {} component lock", - component.name - ) - }) - .get_event_uid(ComponentEventKind::Removed) - }) - .collect::<Vec<_>>(); - - component_storage_lock.remove_entity(entity_uid); - - drop(component_storage_lock); - - if !*has_swapped_active_queue { - self.swap_event_queue(has_swapped_active_queue); + component_removals.add_ids(component_ids.iter().copied()); + + drop(component_removals); + + pending_removals.push((entity_uid, removal)); + } + + fn add_entity_components( + entity_uid: Uid, + components: impl IntoIterator<Item = ComponentParts>, + component_storage: &mut ComponentStorage, + ) -> Vec<Uid> + { + let component_iter = components.into_iter(); + + let mut added_component_ids = + Vec::<Uid>::with_capacity(component_iter.size_hint().0); + + for component_parts in component_iter { + let comp_id = component_parts.id(); + + if let Err(err) = component_storage.add_entity_component( + entity_uid, + (comp_id, component_parts.name(), component_parts.into_data()), + ) { + tracing::error!("Failed to add component to entity: {err}"); + continue; + } + + added_component_ids.push(comp_id); } - for comp_removed_event_id in component_removed_event_uids { - self.emit_event_by_id(comp_removed_event_id); + added_component_ids + } + + fn remove_entity_components( + entity_uid: Uid, + component_ids: impl IntoIterator<Item = Uid>, + component_storage: &mut ComponentStorage, + ) -> Vec<Uid> + { + let component_id_iter = component_ids.into_iter(); + + let mut removed_component_ids = + Vec::<Uid>::with_capacity(component_id_iter.size_hint().0); + + for component_id in component_id_iter { + if let Err(err) = + component_storage.remove_entity_component(entity_uid, component_id) + { + tracing::error!("Failed to remove component to entity: {err}"); + continue; + } + + removed_component_ids.push(component_id); } + + removed_component_ids } - fn emit_event_by_id(&self, event_id: Uid) + fn emit_event_by_id<Event: Component>(&self, target: Uid) { - let query = self.flexible_query([ - ComponentMetadata::of::<SystemComponent>(), - ComponentMetadata { id: event_id, is_optional: false }, - ]); + if target.kind() == UidKind::Pair { + return; + } - for (system,) in query - .iter::<()>() - .into_component_iter::<(&SystemComponent,)>(self) - { + let query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()]) + .build(), + ); + + for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { unsafe { system.system.run(self); } @@ -465,12 +568,19 @@ impl World *has_swapped_active_queue = true; } - fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage> + fn get_entity( + component_storage: &mut ComponentStorage, + entity_uid: Uid, + ) -> Option<EntityHandle<'_>> { - self.data - .component_storage - .write_nonblock() - .expect("Failed to acquire read-write component storage lock") + let archetype = component_storage.get_entity_archetype(entity_uid)?; + + Some(EntityHandle::new( + archetype, + archetype + .get_entity_by_id(entity_uid) + .expect("Not possible"), + )) } } @@ -492,32 +602,58 @@ pub enum StepResult Stop, } -#[derive(Debug, Default)] -pub struct WorldData +#[derive(Debug)] +struct WorldData { - component_storage: Arc<Lock<ComponentStorage>>, + component_storage: ComponentStorage, sole_storage: SoleStorage, - action_queue: Arc<ActionQueue>, + action_queue: Rc<ActionQueue>, + pending_removals: Vec<(Uid, PendingRemoval)>, +} + +impl Default for WorldData +{ + fn default() -> Self + { + Self { + component_storage: ComponentStorage::default(), + sole_storage: SoleStorage::default(), + action_queue: Rc::new(ActionQueue::default()), + pending_removals: Vec::new(), + } + } +} + +#[derive(Debug)] +enum PendingRemoval +{ + Components(Vec<Uid>), + Entity, } #[derive(Debug)] -#[non_exhaustive] -pub struct EntityComponent +pub struct EntityComponentRef<'a> { - pub id: Uid, - pub name: &'static str, - pub component: Lock<Box<dyn Component>>, + component_id: Uid, + component: &'a ArchetypeEntityComponent, } -impl From<Box<dyn Component>> for EntityComponent +impl<'a> EntityComponentRef<'a> { - fn from(component: Box<dyn Component>) -> Self + fn component(&self) -> &'a Lock<Box<dyn Any>> { - Self { - id: component.self_id(), - name: component.type_name(), - component: Lock::new(component), - } + self.component.component() + } + + #[must_use] + pub fn id(&self) -> Uid + { + self.component_id + } + + fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self + { + Self { component_id, component: comp } } } @@ -529,7 +665,7 @@ enum ActiveActionQueue B, } -#[derive(Debug, Default)] +#[derive(Debug)] struct ActionQueue { queue_a: Lock<Vec<Action>>, @@ -556,11 +692,15 @@ impl ActionQueue } } -impl TypeName for ActionQueue +impl Default for ActionQueue { - fn type_name(&self) -> &'static str + fn default() -> Self { - type_name::<Self>() + Self { + queue_a: Lock::new(Vec::new(), type_name::<Vec<Action>>()), + queue_b: Lock::new(Vec::new(), type_name::<Vec<Action>>()), + active_queue: RefCell::new(ActiveActionQueue::default()), + } } } @@ -605,7 +745,7 @@ impl SoleStorage self.storage.insert( sole_type_id, ManuallyDrop::new(StoredSole { - sole: Arc::new(Lock::new(Box::new(sole))), + sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())), drop_last, }), ); @@ -622,18 +762,9 @@ impl Drop for SoleStorage for sole in self.storage.values_mut() { if sole.drop_last { - tracing::trace!( - "Sole {} pushed to dropping last queue", - sole.sole.read_nonblock().unwrap().type_name() - ); - soles_to_drop_last.push(sole); continue; } - tracing::trace!( - "Dropping sole {}", - sole.sole.read_nonblock().unwrap().type_name() - ); unsafe { ManuallyDrop::drop(sole); @@ -641,11 +772,6 @@ impl Drop for SoleStorage } for sole in &mut soles_to_drop_last { - tracing::trace!( - "Dropping sole {} last", - sole.sole.read_nonblock().unwrap().type_name() - ); - unsafe { ManuallyDrop::drop(sole); } diff --git a/ecs/src/lock.rs b/ecs/src/lock.rs index c700098..0b36922 100644 --- a/ecs/src/lock.rs +++ b/ecs/src/lock.rs @@ -1,4 +1,4 @@ -use std::mem::{forget, transmute}; +use std::mem::forget; use std::ops::{Deref, DerefMut}; use parking_lot::{ @@ -9,23 +9,21 @@ use parking_lot::{ RwLockWriteGuard, }; -use crate::type_name::TypeName; - -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Lock<Value> -where - Value: TypeName, { inner: RwLock<Value>, + value_type_name: &'static str, } impl<Value> Lock<Value> -where - Value: TypeName, { - pub fn new(value: Value) -> Self + pub fn new(value: Value, value_type_name: &'static str) -> Self { - Self { inner: RwLock::new(value) } + Self { + inner: RwLock::new(value), + value_type_name, + } } /// Tries to a acquire a handle to the resource with read access. @@ -36,9 +34,12 @@ where { let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?; - tracing::trace!("Acquired lock to value of type {}", guard.type_name()); + tracing::trace!("Acquired lock to value of type {}", self.value_type_name); - Ok(ReadGuard { inner: guard }) + Ok(ReadGuard { + inner: guard, + value_type_name: self.value_type_name, + }) } /// Tries to a acquire a handle to the resource with mutable access. @@ -51,15 +52,13 @@ where tracing::trace!( "Acquired mutable lock to value of type {}", - guard.type_name() + self.value_type_name ); - Ok(WriteGuard { inner: guard }) - } - - pub fn into_inner(self) -> Value - { - self.inner.into_inner() + Ok(WriteGuard { + inner: guard, + value_type_name: self.value_type_name, + }) } } @@ -75,23 +74,20 @@ pub enum Error #[derive(Debug)] pub struct ReadGuard<'guard, Value> -where - Value: TypeName, { inner: RwLockReadGuard<'guard, Value>, + value_type_name: &'static str, } impl<'guard, Value> ReadGuard<'guard, Value> -where - Value: TypeName, { pub fn map<NewValue>( self, func: impl FnOnce(&Value) -> &NewValue, ) -> MappedReadGuard<'guard, NewValue> - where - NewValue: TypeName, { + let value_type_name = self.value_type_name; + // The 'inner' field cannot be moved out of ReadGuard in a normal way since // ReadGuard implements Drop let inner = unsafe { std::ptr::read(&self.inner) }; @@ -99,24 +95,12 @@ where MappedReadGuard { inner: RwLockReadGuard::map(inner, func), + value_type_name, } } - - /// Converts the `ReadGuard` to a `ReadGuard` with a possibly longer lifetime. - /// - /// # Safety - /// The returned `ReadGuard` must **NOT** be used for longer than the original - /// lifetime. - #[must_use] - pub unsafe fn upgrade_lifetime<'new>(self) -> ReadGuard<'new, Value> - { - unsafe { transmute(self) } - } } -impl<'guard, Value> Deref for ReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Deref for ReadGuard<'_, Value> { type Target = Value; @@ -126,27 +110,22 @@ where } } -impl<'guard, Value> Drop for ReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for ReadGuard<'_, Value> { fn drop(&mut self) { - tracing::trace!("Dropped lock to value of type {}", self.type_name()); + tracing::trace!("Dropped lock to value of type {}", self.value_type_name); } } #[derive(Debug)] pub struct MappedReadGuard<'guard, Value> -where - Value: TypeName, { inner: MappedRwLockReadGuard<'guard, Value>, + value_type_name: &'static str, } -impl<'guard, Value> Deref for MappedReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Deref for MappedReadGuard<'_, Value> { type Target = Value; @@ -156,35 +135,33 @@ where } } -impl<'guard, Value> Drop for MappedReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for MappedReadGuard<'_, Value> { fn drop(&mut self) { - tracing::trace!("Dropped mapped lock to value of type {}", self.type_name()); + tracing::trace!( + "Dropped mapped lock to value of type {}", + self.value_type_name + ); } } #[derive(Debug)] pub struct WriteGuard<'guard, Value> -where - Value: TypeName, { inner: RwLockWriteGuard<'guard, Value>, + value_type_name: &'static str, } impl<'guard, Value> WriteGuard<'guard, Value> -where - Value: TypeName, { pub fn map<NewValue>( self, func: impl FnOnce(&mut Value) -> &mut NewValue, ) -> MappedWriteGuard<'guard, NewValue> - where - NewValue: TypeName, { + let value_type_name = self.value_type_name; + // The 'inner' field cannot be moved out of ReadGuard in a normal way since // ReadGuard implements Drop let inner = unsafe { std::ptr::read(&self.inner) }; @@ -192,13 +169,12 @@ where MappedWriteGuard { inner: RwLockWriteGuard::map(inner, func), + value_type_name, } } } -impl<'guard, Value> Deref for WriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Deref for WriteGuard<'_, Value> { type Target = Value; @@ -208,9 +184,7 @@ where } } -impl<'guard, Value> DerefMut for WriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> DerefMut for WriteGuard<'_, Value> { fn deref_mut(&mut self) -> &mut Self::Target { @@ -218,27 +192,25 @@ where } } -impl<'guard, Value> Drop for WriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for WriteGuard<'_, Value> { fn drop(&mut self) { - tracing::trace!("Dropped mutable lock to value of type {}", self.type_name()); + tracing::trace!( + "Dropped mutable lock to value of type {}", + self.value_type_name + ); } } #[derive(Debug)] pub struct MappedWriteGuard<'guard, Value> -where - Value: TypeName, { inner: MappedRwLockWriteGuard<'guard, Value>, + value_type_name: &'static str, } -impl<'guard, Value> Deref for MappedWriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Deref for MappedWriteGuard<'_, Value> { type Target = Value; @@ -248,9 +220,7 @@ where } } -impl<'guard, Value> DerefMut for MappedWriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> DerefMut for MappedWriteGuard<'_, Value> { fn deref_mut(&mut self) -> &mut Self::Target { @@ -258,15 +228,13 @@ where } } -impl<'guard, Value> Drop for MappedWriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for MappedWriteGuard<'_, Value> { fn drop(&mut self) { tracing::trace!( "Dropped mapped mutable lock to value of type {}", - self.type_name() + self.value_type_name ); } } diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs new file mode 100644 index 0000000..4ff4995 --- /dev/null +++ b/ecs/src/pair.rs @@ -0,0 +1,198 @@ +use std::convert::Infallible; + +use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts}; +use crate::entity::{ + Handle as EntityHandle, + MatchingComponentIter as EntityMatchingComponentIter, +}; +use crate::query::{ + TermWithField as QueryTermWithField, + TermsBuilder as QueryTermsBuilder, + TermsBuilderInterface, +}; +use crate::uid::{PairParams as UidPairParams, Uid, Wildcard, With as WithUid}; +use crate::{Component, World}; + +#[derive(Debug)] +pub struct Pair<RelationElem: Element, TargetElem: Element> +{ + relation: RelationElem, + target: TargetElem, +} + +impl Pair<Uid, Uid> +{ + #[must_use] + pub fn new<Relation: WithUid>(target: Uid) -> Self + { + Self { relation: Relation::uid(), target } + } + + #[must_use] + pub fn id(&self) -> Uid + { + Uid::new_pair(&UidPairParams { + relation: self.relation, + target: self.target, + }) + } +} + +impl IntoComponentParts for Pair<Uid, Uid> +{ + fn into_parts(self) -> ComponentParts + { + ComponentParts::builder().name("Pair").build(self.id(), ()) + } +} + +impl<Relation, Target> QueryTermWithField for Pair<Relation, Target> +where + Relation: WithUid, + Target: WithUid, +{ + type Field<'a> = Handle<'a>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Self::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world> + { + let first_matching_comp = entity_handle + .get_matching_components(Self::uid()) + .next() + .expect("Not possible"); + + Handle { + world, + pair_uid: first_matching_comp.id(), + } + } +} + +impl<Relation, Target> WithUid for Pair<Relation, Target> +where + Relation: WithUid, + Target: WithUid, +{ + fn uid() -> Uid + { + Uid::new_pair(&UidPairParams { + relation: Relation::uid(), + target: Target::uid(), + }) + } +} + +impl<Relation> QueryTermWithField for &'static [Pair<Relation, Wildcard>] +where + Relation: WithUid, +{ + type Field<'a> = HandleIter<'a>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Pair::<Relation, Wildcard>::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world> + { + HandleIter { + inner: entity_handle + .get_matching_components(Pair::<Relation, Wildcard>::uid()), + world, + } + } +} + +pub struct Handle<'world> +{ + world: &'world World, + pair_uid: Uid, +} + +impl Handle<'_> +{ + #[must_use] + pub fn get_target_entity(&self) -> Option<EntityHandle<'_>> + { + let archetype = self + .world + .data + .component_storage + .get_entity_archetype(self.pair_uid.target_entity())?; + + let Some(archetype_entity) = + archetype.get_entity_by_id(self.pair_uid.target_entity()) + else { + unreachable!(); + }; + + Some(EntityHandle::new(archetype, archetype_entity)) + } +} + +pub struct HandleIter<'a> +{ + inner: EntityMatchingComponentIter<'a>, + world: &'a World, +} + +impl<'a> Iterator for HandleIter<'a> +{ + type Item = Handle<'a>; + + fn next(&mut self) -> Option<Self::Item> + { + let matching_comp = self.inner.next()?; + + Some(Handle { + world: self.world, + pair_uid: matching_comp.id(), + }) + } +} + +pub trait Element: sealed::Sealed +{ + type Value; +} + +impl Element for Uid +{ + type Value = Uid; +} + +impl sealed::Sealed for Uid {} + +impl<WithUidT: WithUid> Element for WithUidT +{ + type Value = Infallible; +} + +impl<WithUidT: WithUid> sealed::Sealed for WithUidT {} + +/// Relation denoting a dependency to another entity +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct DependsOn; + +/// Relation denoting being the child of another entity. +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct ChildOf; + +mod sealed +{ + pub trait Sealed {} +} diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs index b8660f2..9f47fb8 100644 --- a/ecs/src/phase.rs +++ b/ecs/src/phase.rs @@ -1,6 +1,6 @@ use ecs_macros::Component; -use crate::relationship::{ChildOf, Relationship}; +use crate::pair::{ChildOf, Pair}; use crate::static_entity; #[derive(Debug, Default, Clone, Copy, Component)] @@ -10,6 +10,4 @@ static_entity!(pub START, (Phase,)); static_entity!(pub PRE_UPDATE, (Phase,)); -static_entity!(pub UPDATE, (Phase, <Relationship<ChildOf, Phase>>::new(*PRE_UPDATE))); - -static_entity!(pub PRESENT, (Phase, <Relationship<ChildOf, Phase>>::new(*UPDATE))); +static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE))); diff --git a/ecs/src/query.rs b/ecs/src/query.rs index ca9345c..ccb7add 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -1,62 +1,69 @@ +use std::any::type_name; use std::marker::PhantomData; -use crate::component::RefSequence as ComponentRefSequence; -use crate::query::flexible::{ - EntityHandle, - Iter as FlexibleQueryIter, - Query as FlexibleQuery, +use seq_macro::seq; + +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, }; -use crate::query::options::Options; +use crate::entity::Handle as EntityHandle; +use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; use crate::system::{Param as SystemParam, System}; -use crate::uid::Uid; +use crate::uid::{Kind as UidKind, Uid, With as WithUid}; +use crate::util::array_vec::ArrayVec; +use crate::util::Array; use crate::World; pub mod flexible; -pub mod options; +pub mod term; #[derive(Debug)] -pub struct Query<'world, Comps, OptionsT = ()> +pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where - Comps: ComponentRefSequence, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { world: &'world World, - inner: FlexibleQuery<'world, Comps::Metadata>, - _pd: PhantomData<(Comps, OptionsT)>, + // A term tuple type can have a maximum of 17 elements + inner: FlexibleQuery<'world, 17>, + _pd: PhantomData<(FieldTerms, FieldlessTerms)>, } -impl<'world, Comps, OptionsT> Query<'world, Comps, OptionsT> +impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms> where - Comps: ComponentRefSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { /// Iterates over the entities matching this query, the iterator item being the entity /// components. #[must_use] pub fn iter<'query>( &'query self, - ) -> ComponentIter<'query, 'world, Comps, FlexibleQueryIter<'query>> + ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - tracing::trace!("Searching for {}", std::any::type_name::<Comps>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - ComponentIter { + Iter { world: self.world, - iter: self.inner.iter::<OptionsT>(), + iter: self.inner.iter(), comps_pd: PhantomData, } } /// Iterates over the entities matching this query, the iterator item being the entity - /// [`Uid`] and the entity components. + /// [`Uid`] and the matching entity components. #[must_use] pub fn iter_with_euids<'query>( &'query self, - ) -> ComponentAndEuidIter<'query, 'world, Comps, FlexibleQueryIter<'query>> + ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - tracing::trace!("Searching for {}", std::any::type_name::<Comps>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentAndEuidIter { world: self.world, - iter: self.inner.iter::<OptionsT>(), + iter: self.inner.iter(), comps_pd: PhantomData, } } @@ -65,20 +72,20 @@ where /// `func`. /// /// This function exists so that a custom [`EntityHandle`] iterator can be given to - /// [`ComponentIter`] without giving the user access to a reference to the [`World`]. + /// [`Iter`] without giving the user access to a reference to the [`World`]. #[must_use] pub fn iter_with<'query, OutIter>( &'query self, func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter, - ) -> ComponentIter<'query, 'world, Comps, OutIter> + ) -> Iter<'query, 'world, FieldTerms, OutIter> where OutIter: Iterator<Item = EntityHandle<'query>>, { - tracing::trace!("Searching for {}", std::any::type_name::<Comps>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - ComponentIter { + Iter { world: self.world, - iter: func(self.inner.iter::<OptionsT>()), + iter: func(self.inner.iter()), comps_pd: PhantomData, } } @@ -87,27 +94,32 @@ where #[must_use] pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid> { - Some(self.inner.iter::<OptionsT>().nth(entity_index)?.uid()) + Some(self.inner.iter().nth(entity_index)?.uid()) } pub(crate) fn new(world: &'world World) -> Self { + let mut terms_builder = Terms::builder(); + + FieldTerms::apply_terms_to_builder(&mut terms_builder); + FieldlessTerms::apply_terms_to_builder(&mut terms_builder); + Self { world, - inner: world.flexible_query(Comps::metadata()), + inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } } } -impl<'query, 'world, Comps, OptionsT> IntoIterator - for &'query Query<'world, Comps, OptionsT> +impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator + for &'query Query<'world, FieldTerms, FieldlessTerms> where - Comps: ComponentRefSequence + 'world, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { - type IntoIter = ComponentIter<'query, 'world, Comps, FlexibleQueryIter<'query>>; - type Item = Comps::Handles<'query>; + type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; + type Item = FieldTerms::Fields<'query>; fn into_iter(self) -> Self::IntoIter { @@ -115,10 +127,11 @@ where } } -impl<'world, Comps, OptionsT> SystemParam<'world> for Query<'world, Comps, OptionsT> +impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world> + for Query<'world, FieldTerms, FieldlessTerms> where - Comps: ComponentRefSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { type Input = (); @@ -138,66 +151,350 @@ where } } -pub struct ComponentIter<'query, 'world, Comps, EntityHandleIter> +#[derive(Debug)] +pub struct Terms<const MAX_TERM_CNT: usize> +{ + required_components: ArrayVec<Uid, MAX_TERM_CNT>, + excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, +} + +impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT> +{ + pub fn builder() -> TermsBuilder<MAX_TERM_CNT> + { + TermsBuilder::default() + } +} + +#[derive(Debug, Default)] +#[must_use] +pub struct TermsBuilder<const MAX_TERM_CNT: usize> +{ + required_components: ArrayVec<Uid, MAX_TERM_CNT>, + excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, +} + +#[allow(clippy::return_self_not_must_use)] +pub trait TermsBuilderInterface +{ + fn with<WithUidT: WithUid>(self) -> Self; + + fn without<WithUidT: WithUid>(self) -> Self; + + fn with_required(self, ids: impl Array<Uid>) -> Self; + + fn without_ids(self, ids: impl Array<Uid>) -> Self; +} + +macro_rules! impl_terms_builder { + ($($impl_content: tt)*) => { + impl<const MAX_TERM_CNT: usize> + TermsBuilderInterface for TermsBuilder<MAX_TERM_CNT> + { + $($impl_content)* + } + + impl<const MAX_TERM_CNT: usize> + TermsBuilderInterface for &mut TermsBuilder<MAX_TERM_CNT> + { + $($impl_content)* + } + }; +} + +impl_terms_builder! { + #[allow(unused_mut)] + fn with<WithUidT: WithUid>(mut self) -> Self + { + let insert_index = self.required_components + .partition_point(|id| *id <= WithUidT::uid()); + + self.required_components + .insert(insert_index, WithUidT::uid()); + + self + } + + #[allow(unused_mut)] + fn without<WithUidT: WithUid>(mut self) -> Self + { + let insert_index = self.excluded_components + .partition_point(|id| *id <= WithUidT::uid()); + + self.excluded_components + .insert(insert_index, WithUidT::uid()); + + self + } + + #[allow(unused_mut)] + fn with_required(mut self, mut ids: impl Array<Uid>) -> Self + { + if !ids.as_ref().is_sorted() { + ids.as_mut().sort(); + } + + if self.required_components.is_empty() { + self.required_components.extend(ids); + return self; + } + + let mut id_iter = ids.into_iter(); + + while let Some(id) = id_iter.next() { + let insert_index = self.required_components + .partition_point(|other_id| *other_id <= id); + + if insert_index == self.required_components.len() { + self.required_components.extend([id].into_iter().chain(id_iter)); + + return self; + } + + self.required_components + .insert(insert_index, id); + + } + + self + } + + #[allow(unused_mut)] + fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self + { + if !ids.as_ref().is_sorted() { + ids.as_mut().sort(); + } + + if self.excluded_components.is_empty() { + self.excluded_components.extend(ids); + return self; + } + + let mut id_iter = ids.into_iter(); + + while let Some(id) = id_iter.next() { + let insert_index = self.excluded_components + .partition_point(|other_id| *other_id <= id); + + if insert_index == self.excluded_components.len() { + self.excluded_components.extend([id].into_iter().chain(id_iter)); + + return self; + } + + self.excluded_components + .insert(insert_index, id); + + } + + self + } +} + +impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT> +{ + #[must_use] + pub fn build(self) -> Terms<MAX_TERM_CNT> + { + debug_assert!(self.required_components.is_sorted()); + debug_assert!(self.excluded_components.is_sorted()); + + Terms { + required_components: self.required_components, + excluded_components: self.excluded_components, + } + } +} + +pub trait TermWithoutField +{ + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); +} + +pub trait TermWithField +{ + type Field<'a>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world>; +} + +impl<ComponentT: Component> TermWithField for &ComponentT +{ + type Field<'a> = ComponentHandle<'a, ComponentT>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with::<ComponentT>(); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + _world: &'world World, + ) -> Self::Field<'world> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { + panic!( + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(component).unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + }) + } +} + +impl<ComponentT: Component> TermWithField for &mut ComponentT +{ + type Field<'a> = ComponentHandleMut<'a, ComponentT>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with::<ComponentT>(); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + _world: &'world World, + ) -> Self::Field<'world> + { + assert_eq!(ComponentT::id().kind(), UidKind::Component); + + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { + panic!( + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(component).unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + }) + } +} + +pub trait TermWithoutFieldTuple +{ + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); +} + +pub trait TermWithFieldTuple +{ + type Fields<'component>; + + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ); + + fn get_fields<'component>( + entity_handle: &EntityHandle<'component>, + world: &'component World, + ) -> Self::Fields<'component>; +} + +pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter> where + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, iter: EntityHandleIter, - comps_pd: PhantomData<Comps>, + comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, Comps, EntityHandleIter> - ComponentIter<'query, 'world, Comps, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> + Iter<'query, 'world, FieldTerms, EntityHandleIter> where - Comps: ComponentRefSequence + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { - pub(crate) fn new(world: &'world World, iter: EntityHandleIter) -> Self + /// Creates a new iterator from the given entity handle iterator. + /// + /// # Important + /// All of the yielded entities of the entity handle iterator should match the + /// terms `Terms`. The [`Self::next`] function will panic if it encounters a + /// entity that does not match the terms `Terms`. + pub fn new(world: &'world World, iter: EntityHandleIter) -> Self { Self { world, iter, comps_pd: PhantomData } } } -impl<'query, 'world, Comps, EntityHandleIter> Iterator - for ComponentIter<'query, 'world, Comps, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator + for Iter<'query, 'world, FieldTerms, EntityHandleIter> where - Comps: ComponentRefSequence + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { - type Item = Comps::Handles<'query>; + type Item = FieldTerms::Fields<'query>; fn next(&mut self) -> Option<Self::Item> { let entity_handle = self.iter.next()?; - Some(Comps::from_components( - entity_handle.components(), - |component_uid| entity_handle.get_component_index(component_uid), - self.world, - )) + Some(FieldTerms::get_fields(&entity_handle, self.world)) } } -pub struct ComponentAndEuidIter<'query, 'world, Comps, EntityHandleIter> +pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, iter: EntityHandleIter, - comps_pd: PhantomData<Comps>, + comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, Comps, EntityHandleIter> Iterator - for ComponentAndEuidIter<'query, 'world, Comps, EntityHandleIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator + for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - Comps: ComponentRefSequence + 'world, + FieldTerms: TermWithFieldTuple, EntityHandleIter: Iterator<Item = EntityHandle<'query>>, 'world: 'query, { - type Item = (Uid, Comps::Handles<'query>); + type Item = (Uid, FieldTerms::Fields<'query>); fn next(&mut self) -> Option<Self::Item> { @@ -205,11 +502,78 @@ where Some(( entity_handle.uid(), - Comps::from_components( - entity_handle.components(), - |component_uid| entity_handle.get_component_index(component_uid), - self.world, - ), + FieldTerms::get_fields(&entity_handle, self.world), )) } } + +macro_rules! impl_term_sequence { + ($c: tt) => { + seq!(I in 0..=$c { + impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*) + { + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT> + ) + { + #( + Term~I::apply_to_terms_builder(terms_builder); + )* + } + } + + impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*) + { + type Fields<'component> = (#(Term~I::Field<'component>,)*); + + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT> + ) + { + #( + Term~I::apply_to_terms_builder(terms_builder); + )* + } + + fn get_fields<'component>( + entity_handle: &EntityHandle<'component>, + world: &'component World, + ) -> Self::Fields<'component> + { + (#(Term~I::get_field(entity_handle, world),)*) + } + } + }); + }; +} + +seq!(C in 0..=16 { + impl_term_sequence!(C); +}); + +impl TermWithoutFieldTuple for () +{ + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } +} + +impl TermWithFieldTuple for () +{ + type Fields<'component> = (); + + fn apply_terms_to_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } + + fn get_fields<'component>( + _entity_handle: &EntityHandle<'_>, + _world: &'component World, + ) -> Self::Fields<'component> + { + } +} diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index 5c23e68..add30b0 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -1,145 +1,84 @@ //! Low-level querying. -use std::iter::{repeat_n, Filter, Flatten, Map, RepeatN, Zip}; +use std::iter::{repeat_n, FlatMap, RepeatN, Zip}; -use crate::component::storage::{ - Archetype, - ArchetypeEntity, - ArchetypeRefIter, - EntityIter, - Storage as ComponentStorage, -}; -use crate::component::{ - Metadata as ComponentMetadata, - RefSequence as ComponentRefSequence, -}; -use crate::lock::ReadGuard; -use crate::query::options::Options; -use crate::query::ComponentIter; -use crate::uid::Uid; -use crate::util::Sortable; -use crate::{EntityComponent, World}; +use crate::component::storage::archetype::{Archetype, EntityIter}; +use crate::component::storage::{ArchetypeRefIter, ArchetypeSearchTerms}; +use crate::entity::Handle as EntityHandle; +use crate::query::Terms; +use crate::World; /// Low-level entity query structure. #[derive(Debug)] -pub struct Query<'world, CompMetadata> -where - CompMetadata: Sortable<Item = ComponentMetadata> + AsRef<[ComponentMetadata]>, +pub struct Query<'world, const MAX_TERM_CNT: usize> { - component_storage: ReadGuard<'world, ComponentStorage>, - comp_metadata: CompMetadata, + world: &'world World, + terms: Terms<MAX_TERM_CNT>, } -impl<'world, CompMetadata> Query<'world, CompMetadata> -where - CompMetadata: Sortable<Item = ComponentMetadata> + AsRef<[ComponentMetadata]>, +impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> { /// Iterates over the entities matching this query. #[must_use] - pub fn iter<'query, OptionsT: Options>(&'query self) -> Iter<'query> + pub fn iter(&self) -> Iter<'_> { Iter { iter: self + .world + .data .component_storage - .iter_archetypes_with_comps(&self.comp_metadata) - .map( + .search_archetypes(ArchetypeSearchTerms { + required_components: &self.terms.required_components, + excluded_components: &self.terms.excluded_components, + }) + .flat_map( (|archetype| { repeat_n(archetype, archetype.entity_cnt()) .zip(archetype.entities()) }) as ComponentIterMapFn, - ) - .flatten() - .filter(|(_, entity)| OptionsT::entity_filter(entity.components())), + ), } } - pub(crate) fn new(world: &'world World, mut comp_metadata: CompMetadata) -> Self - { - comp_metadata.sort_by_key_b(|metadata| metadata.id); - - Self { - component_storage: world - .data - .component_storage - .read_nonblock() - .expect("Failed to acquire read-only component storage lock"), - comp_metadata, - } - } -} - -pub struct Iter<'query> -{ - iter: QueryEntityIter<'query>, -} - -impl<'query> Iter<'query> -{ - /// Converts this iterator into a [`ComponentIter`]. - /// - /// Note: The matching entities of this iterator should have all of the non-[`Option`] - /// components in `Comps`, otherwise iterating the [`ComponentIter`] will cause a - /// panic. - #[must_use] - #[inline] - pub fn into_component_iter<'world, Comps>( - self, - world: &'world World, - ) -> ComponentIter<'query, 'world, Comps, Self> - where - Comps: ComponentRefSequence + 'world, - 'world: 'query, + pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self { - ComponentIter::new(world, self) + Self { world, terms } } } -impl<'query> Iterator for Iter<'query> +impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT> { + type IntoIter = Iter<'query>; type Item = EntityHandle<'query>; - fn next(&mut self) -> Option<Self::Item> + fn into_iter(self) -> Self::IntoIter { - let (archetype, entity) = self.iter.next()?; - - Some(EntityHandle { archetype, entity }) + self.iter() } } -pub struct EntityHandle<'query> +pub struct Iter<'query> { - archetype: &'query Archetype, - entity: &'query ArchetypeEntity, + iter: QueryEntityIter<'query>, } -impl<'query> EntityHandle<'query> +impl<'query> Iterator for Iter<'query> { - /// Returns the [`Uid`] of this entity. - #[inline] - pub fn uid(&self) -> Uid - { - self.entity.uid() - } + type Item = EntityHandle<'query>; - #[inline] - pub fn components(&self) -> &'query [EntityComponent] + fn next(&mut self) -> Option<Self::Item> { - self.entity.components() - } + let (archetype, entity) = self.iter.next()?; - #[inline] - pub fn get_component_index(&self, component_uid: Uid) -> Option<usize> - { - self.archetype.get_index_for_component(component_uid) + Some(EntityHandle::new(archetype, entity)) } } -type ComponentIterMapFn = - for<'a> fn(&'a Archetype) -> Zip<RepeatN<&'a Archetype>, EntityIter<'a>>; +type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>; -type ComponentIterFilterFn = - for<'a, 'b> fn(&'a (&'b Archetype, &'b ArchetypeEntity)) -> bool; +type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>; -type QueryEntityIter<'query> = Filter< - Flatten<Map<ArchetypeRefIter<'query>, ComponentIterMapFn>>, - ComponentIterFilterFn, +type QueryEntityIter<'query> = FlatMap< + ArchetypeRefIter<'query, 'query>, + ComponentIterMapFnOutput<'query>, + ComponentIterMapFn, >; diff --git a/ecs/src/query/options.rs b/ecs/src/query/options.rs deleted file mode 100644 index 772d091..0000000 --- a/ecs/src/query/options.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::marker::PhantomData; - -use hashbrown::HashSet; - -use crate::component::Component; -use crate::EntityComponent; - -/// Query options. -pub trait Options -{ - fn entity_filter<'component>(components: &'component [EntityComponent]) -> bool; -} - -impl Options for () -{ - fn entity_filter<'component>(_components: &'component [EntityComponent]) -> bool - { - true - } -} - -pub struct With<ComponentT> -where - ComponentT: Component, -{ - _pd: PhantomData<ComponentT>, -} - -impl<ComponentT> Options for With<ComponentT> -where - ComponentT: Component, -{ - fn entity_filter<'component>(components: &'component [EntityComponent]) -> bool - { - let ids_set = components - .into_iter() - .map(|component| component.id) - .collect::<HashSet<_>>(); - - ids_set.contains(&ComponentT::id()) - } -} - -pub struct Not<OptionsT> -where - OptionsT: Options, -{ - _pd: PhantomData<OptionsT>, -} - -impl<OptionsT> Options for Not<OptionsT> -where - OptionsT: Options, -{ - fn entity_filter<'component>(components: &'component [EntityComponent]) -> bool - { - !OptionsT::entity_filter(components) - } -} diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs new file mode 100644 index 0000000..9c772da --- /dev/null +++ b/ecs/src/query/term.rs @@ -0,0 +1,115 @@ +use std::any::type_name; +use std::marker::PhantomData; + +use crate::component::{ + Component, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, +}; +use crate::query::{ + TermWithField, + TermWithoutField, + TermsBuilder, + TermsBuilderInterface, +}; +use crate::uid::With as WithUid; + +pub struct With<WithUidT> +where + WithUidT: WithUid, +{ + _pd: PhantomData<WithUidT>, +} + +impl<WithUidT> TermWithoutField for With<WithUidT> +where + WithUidT: WithUid, +{ + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with::<WithUidT>(); + } +} + +pub struct Without<WithUidT> +where + WithUidT: WithUid, +{ + _pd: PhantomData<WithUidT>, +} + +impl<WithUidT> TermWithoutField for Without<WithUidT> +where + WithUidT: WithUid, +{ + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.without::<WithUidT>(); + } +} + +impl<ComponentT: Component> TermWithField for Option<&ComponentT> +{ + type Field<'a> = Option<ComponentHandle<'a, ComponentT>>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } + + fn get_field<'world>( + entity_handle: &crate::entity::Handle<'world>, + _world: &'world crate::World, + ) -> Self::Field<'world> + { + Some( + ComponentHandle::<'world, ComponentT>::from_entity_component_ref( + entity_handle + .get_matching_components(ComponentT::id()) + .next()?, + ) + .unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + }), + ) + } +} + +impl<ComponentT: Component> TermWithField for Option<&mut ComponentT> +{ + type Field<'a> = Option<ComponentHandleMut<'a, ComponentT>>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>, + ) + { + } + + fn get_field<'world>( + entity_handle: &crate::entity::Handle<'world>, + _world: &'world crate::World, + ) -> Self::Field<'world> + { + Some( + ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref( + entity_handle + .get_matching_components(ComponentT::id()) + .next()?, + ) + .unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentT>() + ); + }), + ) + } +} diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs deleted file mode 100644 index 143b589..0000000 --- a/ecs/src/relationship.rs +++ /dev/null @@ -1,538 +0,0 @@ -use std::any::type_name; -use std::marker::PhantomData; - -use ecs_macros::Component; - -use crate::component::storage::Storage as ComponentStorage; -use crate::component::{ - Component, - FromLockedOptional as FromLockedOptionalComponent, - FromOptional as FromOptionalComponent, - FromOptionalMut as FromOptionalMutComponent, -}; -use crate::lock::{Error as LockError, Lock, ReadGuard, WriteGuard}; -use crate::system::{ComponentRef, ComponentRefMut}; -use crate::uid::{Kind as UidKind, Uid}; -use crate::World; - -/// A relationship to one or more targets. -#[derive(Debug, Component)] -#[component( - ref_type = Relation<'component, Kind, ComponentT>, - ref_mut_type = RelationMut<'component, Kind, ComponentT>, -)] -pub struct Relationship<Kind, ComponentT: Component> -where - Kind: 'static, -{ - entity_uid: SingleOrMultiple<Uid>, - _pd: PhantomData<(Kind, ComponentT)>, -} - -impl<Kind, ComponentT> Relationship<Kind, ComponentT> -where - ComponentT: Component, -{ - /// Creates a new `Relationship` with a single target. - #[must_use] - pub fn new(entity_uid: Uid) -> Self - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - Self { - entity_uid: SingleOrMultiple::Single(entity_uid), - _pd: PhantomData, - } - } - - /// Creates a new `Relationship` with multiple targets. - #[must_use] - pub fn new_multiple(entity_uids: impl IntoIterator<Item = Uid>) -> Self - { - let uids = entity_uids.into_iter().collect::<Vec<_>>(); - - for euid in &uids { - debug_assert_eq!(euid.kind(), UidKind::Entity); - } - - Self { - entity_uid: SingleOrMultiple::Multiple(uids), - _pd: PhantomData, - } - } -} - -pub struct RelationMut<'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - component_storage_lock: ReadGuard<'static, ComponentStorage>, - relationship_comp: ComponentRefMut<'rel_comp, Relationship<Kind, ComponentT>>, -} - -impl<'rel_comp, Kind, ComponentT> FromOptionalMutComponent<'rel_comp> - for RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_optional_mut_component( - optional_component: Option< - crate::lock::WriteGuard<'rel_comp, Box<dyn Component>>, - >, - world: &'rel_comp World, - ) -> Self - { - let relationship_comp = - ComponentRefMut::<Relationship<Kind, ComponentT>>::from_optional_mut_component( - optional_component, - world, - ); - - let component_storage_lock = world - .data - .component_storage - .read_nonblock() - .expect("Failed to aquire read-only component storage lock"); - - Self { - relationship_comp, - // SAFETY: The component lock is not used for longer than the original - // lifetime - component_storage_lock: unsafe { component_storage_lock.upgrade_lifetime() }, - } - } -} - -impl<'rel_comp, Kind, ComponentT> FromOptionalMutComponent<'rel_comp> - for Option<RelationMut<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_optional_mut_component( - optional_component: Option<WriteGuard<'rel_comp, Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Self - { - optional_component.map(|component| { - RelationMut::from_optional_mut_component(Some(component), world) - }) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp crate::lock::Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, LockError> - { - Ok(Self::from_optional_mut_component( - optional_component - .map(|lock| lock.write_nonblock()) - .transpose()?, - world, - )) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Option<RelationMut<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, crate::lock::Error> - { - optional_component - .map(|component| { - RelationMut::from_locked_optional_component(Some(component), world) - }) - .transpose() - } -} - -impl<'rel_comp, Kind, ComponentT> RelationMut<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - /// Returns the component of the target at the specified index. - /// - /// # Panics - /// Will panic if the entity does not exist in the archetype it belongs to. This - /// should hopefully never happend. - #[must_use] - pub fn get(&self, index: usize) -> Option<ComponentRefMut<'_, ComponentT>> - { - let target = self.get_target(index)?; - - let archetype = self.component_storage_lock.get_entity_archetype(*target)?; - - let entity = archetype - .get_entity(*target) - .expect("Target entity is gone from archetype"); - - let component_index = archetype.get_index_for_component(ComponentT::id())?; - - let component = ComponentRefMut::new( - entity - .get_component(component_index)? - .component - .write_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock of component {}", - type_name::<ComponentT>() - ) - }), - ); - - Some(component) - } - - /// Returns a reference to the target at the specified index. - #[must_use] - 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), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get(index), - SingleOrMultiple::Single(_) => None, - } - } - - /// Returns a mutable reference to the target at the specified index. - #[must_use] - 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), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get_mut(index), - SingleOrMultiple::Single(_) => None, - } - } - - /// Adds a target to the relationship. - 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 = - SingleOrMultiple::Multiple(vec![*prev_entity_uid, entity_uid]); - } - SingleOrMultiple::Multiple(entity_uids) => entity_uids.push(entity_uid), - } - } - - /// Removes a target to the relationship, returning it. - pub fn remove_target(&mut self, index: usize) -> Option<Uid> - { - match &mut self.relationship_comp.entity_uid { - SingleOrMultiple::Single(entity_uid) => { - let prev_entity_uid = *entity_uid; - - self.relationship_comp.entity_uid = - SingleOrMultiple::Multiple(Vec::new()); - - Some(prev_entity_uid) - } - SingleOrMultiple::Multiple(entity_uids) => { - if index >= entity_uids.len() { - return None; - } - - Some(entity_uids.remove(index)) - } - } - } - - #[must_use] - pub fn target_count(&self) -> usize - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(_) => 1, - SingleOrMultiple::Multiple(entity_uids) => entity_uids.len(), - } - } - - /// Returns a iterator of the components of the targets of this relationship. - #[must_use] - pub fn iter(&self) -> TargetComponentIterMut<'_, 'rel_comp, Kind, ComponentT> - { - TargetComponentIterMut { relation: self, index: 0 } - } -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> IntoIterator - for &'relationship RelationMut<'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - ComponentT: Component, -{ - type IntoIter = TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT>; - type Item = ComponentRefMut<'rel_comp, ComponentT>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -/// Iterator of the components of the targets of a relationship. -pub struct TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - relation: &'relationship RelationMut<'rel_comp, Kind, ComponentT>, - index: usize, -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> Iterator - for TargetComponentIterMut<'relationship, 'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - Kind: 'static, - ComponentT: Component, -{ - type Item = ComponentRefMut<'rel_comp, ComponentT>; - - fn next(&mut self) -> Option<Self::Item> - { - let index = self.index; - - self.index += 1; - - self.relation.get(index) - } -} - -#[derive(Debug)] -enum SingleOrMultiple<Value> -{ - Single(Value), - Multiple(Vec<Value>), -} - -pub struct Relation<'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - component_storage_lock: ReadGuard<'static, ComponentStorage>, - relationship_comp: ComponentRef<'rel_comp, Relationship<Kind, ComponentT>>, -} - -impl<'rel_comp, Kind, ComponentT> FromOptionalComponent<'rel_comp> - for Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_optional_component( - optional_component: Option<ReadGuard<'rel_comp, Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Self - { - let relationship_comp = - ComponentRef::<Relationship<Kind, ComponentT>>::from_optional_component( - optional_component, - world, - ); - - let component_storage_lock = world - .data - .component_storage - .read_nonblock() - .expect("Failed to aquire read-only component storage lock"); - - Self { - relationship_comp, - // SAFETY: The component lock is not used for longer than the original - // lifetime - component_storage_lock: unsafe { component_storage_lock.upgrade_lifetime() }, - } - } -} - -impl<'rel_comp, Kind, ComponentT> FromOptionalComponent<'rel_comp> - for Option<Relation<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_optional_component( - optional_component: Option<ReadGuard<'rel_comp, Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Self - { - optional_component - .map(|component| Relation::from_optional_component(Some(component), world)) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, LockError> - { - Ok(Self::from_optional_component( - optional_component - .map(|lock| lock.read_nonblock()) - .transpose()?, - world, - )) - } -} - -impl<'rel_comp, Kind, ComponentT> FromLockedOptionalComponent<'rel_comp> - for Option<Relation<'rel_comp, Kind, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'rel_comp Lock<Box<dyn Component>>>, - world: &'rel_comp World, - ) -> Result<Self, crate::lock::Error> - { - optional_component - .map(|component| { - Relation::from_locked_optional_component(Some(component), world) - }) - .transpose() - } -} - -impl<'rel_comp, Kind, ComponentT> Relation<'rel_comp, Kind, ComponentT> -where - ComponentT: Component, -{ - /// Returns the component of the target at the specified index. - /// - /// # Panics - /// Will panic if the entity does not exist in the archetype it belongs to. This - /// should hopefully never happend. - #[must_use] - pub fn get(&self, index: usize) -> Option<ComponentRef<'_, ComponentT>> - { - let target = self.get_target(index)?; - - let archetype = self.component_storage_lock.get_entity_archetype(*target)?; - - let entity = archetype - .get_entity(*target) - .expect("Target entity is gone from archetype"); - - let component_index = archetype.get_index_for_component(ComponentT::id())?; - - let component = ComponentRef::new( - entity - .get_component(component_index)? - .component - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to aquire read-write lock of component {}", - type_name::<ComponentT>() - ) - }), - ); - - Some(component) - } - - /// Returns a reference to the target at the specified index. - #[must_use] - 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), - SingleOrMultiple::Multiple(entity_uids) => entity_uids.get(index), - SingleOrMultiple::Single(_) => None, - } - } - - #[must_use] - pub fn target_count(&self) -> usize - { - match &self.relationship_comp.entity_uid { - SingleOrMultiple::Single(_) => 1, - SingleOrMultiple::Multiple(entity_uids) => entity_uids.len(), - } - } - - pub fn target_uids(&self) -> impl Iterator<Item = Uid> + '_ - { - (0..self.target_count()) - .map_while(|target_index| self.get_target(target_index).copied()) - } - - /// Returns a iterator of the components of the targets of this relationship. - #[must_use] - pub fn iter(&self) -> TargetComponentIter<'_, 'rel_comp, Kind, ComponentT> - { - TargetComponentIter { relation: self, index: 0 } - } -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> IntoIterator - for &'relationship Relation<'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - ComponentT: Component, -{ - type IntoIter = TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT>; - type Item = ComponentRef<'rel_comp, ComponentT>; - - fn into_iter(self) -> Self::IntoIter - { - self.iter() - } -} - -/// Iterator of the components of the targets of a relationship. -pub struct TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT> -where - Kind: 'static, - ComponentT: Component, -{ - relation: &'relationship Relation<'rel_comp, Kind, ComponentT>, - index: usize, -} - -impl<'relationship, 'rel_comp, Kind, ComponentT> Iterator - for TargetComponentIter<'relationship, 'rel_comp, Kind, ComponentT> -where - 'relationship: 'rel_comp, - Kind: 'static, - ComponentT: Component, -{ - type Item = ComponentRef<'rel_comp, ComponentT>; - - fn next(&mut self) -> Option<Self::Item> - { - let index = self.index; - - self.index += 1; - - self.relation.get(index) - } -} - -/// Relationship kind denoting a dependency to another entity -#[derive(Debug, Default, Clone, Copy)] -pub struct DependsOn; - -/// Relationship kind denoting being the child of another entity. -#[derive(Debug, Default, Clone, Copy)] -pub struct ChildOf; diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs index a35b520..1cce419 100644 --- a/ecs/src/sole.rs +++ b/ecs/src/sole.rs @@ -6,11 +6,10 @@ use std::sync::{Arc, Weak}; use crate::lock::{Lock, WriteGuard}; use crate::system::{Param as SystemParam, System}; -use crate::type_name::TypeName; use crate::World; /// A type which has a single instance and is shared globally. -pub trait Sole: Any + TypeName +pub trait Sole: Any { fn drop_last(&self) -> bool; @@ -40,14 +39,6 @@ impl Debug for dyn Sole } } -impl TypeName for Box<dyn Sole> -{ - fn type_name(&self) -> &'static str - { - self.as_ref().type_name() - } -} - /// Holds a reference to a globally shared singleton value. #[derive(Debug)] pub struct Single<'world, SoleT: Sole> @@ -114,7 +105,7 @@ where } } -impl<'world, SoleT> Deref for Single<'world, SoleT> +impl<SoleT> Deref for Single<'_, SoleT> where SoleT: Sole, { @@ -126,7 +117,7 @@ where } } -impl<'world, SoleT> DerefMut for Single<'world, SoleT> +impl<SoleT> DerefMut for Single<'_, SoleT> where SoleT: Sole, { @@ -173,7 +164,7 @@ where _pd: PhantomData<&'weak_ref SoleT>, } -impl<'weak_ref, SoleT> SingleRef<'weak_ref, SoleT> +impl<SoleT> SingleRef<'_, SoleT> where SoleT: Sole, { diff --git a/ecs/src/system.rs b/ecs/src/system.rs index b410d8f..603c015 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,26 +1,11 @@ -use std::any::{type_name, Any}; +use std::any::Any; use std::convert::Infallible; use std::fmt::Debug; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; use ecs_macros::Component; use seq_macro::seq; -use crate::component::{ - Component, - FromLockedOptional as FromLockedOptionalComponent, - FromOptional as FromOptionalComponent, - FromOptionalMut as FromOptionalMutComponent, -}; -use crate::lock::{ - Error as LockError, - Lock, - MappedReadGuard, - MappedWriteGuard, - ReadGuard, - WriteGuard, -}; +use crate::component::{Component, HandleMut as ComponentHandleMut}; use crate::tuple::{ReduceElement as TupleReduceElement, Tuple}; use crate::World; @@ -41,7 +26,7 @@ pub trait System<'world, Impl>: 'static fn get_local_component_mut<LocalComponent: Component>( &self, - ) -> Option<ComponentRefMut<LocalComponent>>; + ) -> Option<ComponentHandleMut<LocalComponent>>; fn set_local_component<LocalComponent: Component>( &mut self, @@ -100,7 +85,7 @@ macro_rules! impl_system { fn get_local_component_mut<LocalComponent: Component>( &self, - ) -> Option<ComponentRefMut<LocalComponent>> + ) -> Option<ComponentHandleMut<LocalComponent>> { panic!("System does not have any local components"); } @@ -194,215 +179,6 @@ impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () type Return = Accumulator; } -#[derive(Debug)] -pub struct ComponentRefMut<'a, ComponentT: Component> -{ - inner: MappedWriteGuard<'a, ComponentT>, - _ph: PhantomData<ComponentT>, -} - -impl<'a, ComponentT: Component> ComponentRefMut<'a, ComponentT> -{ - pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Component>>) -> Self - { - Self { - inner: inner.map(|component| { - let component_type_name = component.type_name(); - - component.downcast_mut::<ComponentT>().unwrap_or_else(|| { - panic!( - "Cannot downcast component {component_type_name} to type {}", - type_name::<ComponentT>() - ); - }) - }), - _ph: PhantomData, - } - } -} - -impl<'component, ComponentT: Component> FromOptionalMutComponent<'component> - for ComponentRefMut<'component, ComponentT> -{ - fn from_optional_mut_component( - inner: Option<WriteGuard<'component, Box<dyn Component>>>, - _world: &'component World, - ) -> Self - { - Self::new(inner.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - })) - } -} - -impl<'component, ComponentT: Component> FromLockedOptionalComponent<'component> - for ComponentRefMut<'component, ComponentT> -{ - fn from_locked_optional_component( - optional_component: Option<&'component crate::lock::Lock<Box<dyn Component>>>, - world: &'component World, - ) -> Result<Self, LockError> - { - Ok(Self::from_optional_mut_component( - optional_component - .map(|lock| lock.write_nonblock()) - .transpose()?, - world, - )) - } -} - -impl<'comp, ComponentT> FromOptionalMutComponent<'comp> - for Option<ComponentRefMut<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_optional_mut_component( - optional_component: Option<WriteGuard<'comp, Box<dyn Component>>>, - _world: &'comp World, - ) -> Self - { - optional_component.map(|component| ComponentRefMut::new(component)) - } -} - -impl<'comp, ComponentT> FromLockedOptionalComponent<'comp> - for Option<ComponentRefMut<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - _world: &'comp World, - ) -> Result<Self, LockError> - { - optional_component - .map(|lock| Ok(ComponentRefMut::new(lock.write_nonblock()?))) - .transpose() - } -} - -impl<'a, ComponentT: Component> Deref for ComponentRefMut<'a, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - -impl<'a, ComponentT: Component> DerefMut for ComponentRefMut<'a, ComponentT> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - &mut self.inner - } -} - -#[derive(Debug)] -pub struct ComponentRef<'a, ComponentT: Component> -{ - inner: MappedReadGuard<'a, ComponentT>, - _ph: PhantomData<ComponentT>, -} - -impl<'a, ComponentT: Component> ComponentRef<'a, ComponentT> -{ - pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Component>>) -> Self - { - Self { - inner: inner.map(|component| { - component.downcast_ref::<ComponentT>().unwrap_or_else(|| { - panic!( - "Cannot downcast component {} to type {}", - component.type_name(), - type_name::<ComponentT>() - ); - }) - }), - _ph: PhantomData, - } - } -} - -impl<'component, ComponentT: Component> FromOptionalComponent<'component> - for ComponentRef<'component, ComponentT> -{ - fn from_optional_component( - inner: Option<ReadGuard<'component, Box<dyn Component>>>, - _world: &'component World, - ) -> Self - { - Self::new(inner.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - })) - } -} - -impl<'component, ComponentT: Component> FromLockedOptionalComponent<'component> - for ComponentRef<'component, ComponentT> -{ - fn from_locked_optional_component( - optional_component: Option<&'component crate::lock::Lock<Box<dyn Component>>>, - world: &'component World, - ) -> Result<Self, LockError> - { - Ok(Self::from_optional_component( - optional_component - .map(|lock| lock.read_nonblock()) - .transpose()?, - world, - )) - } -} - -impl<'comp, ComponentT> FromOptionalComponent<'comp> - for Option<ComponentRef<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_optional_component( - optional_component: Option<ReadGuard<'comp, Box<dyn Component>>>, - _world: &'comp World, - ) -> Self - { - optional_component.map(|component| ComponentRef::new(component)) - } -} - -impl<'comp, ComponentT> FromLockedOptionalComponent<'comp> - for Option<ComponentRef<'comp, ComponentT>> -where - ComponentT: Component, -{ - fn from_locked_optional_component( - optional_component: Option<&'comp Lock<Box<dyn Component>>>, - _world: &'comp World, - ) -> Result<Self, LockError> - { - optional_component - .map(|lock| Ok(ComponentRef::new(lock.read_nonblock()?))) - .transpose() - } -} - -impl<'a, ComponentT: Component> Deref for ComponentRef<'a, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - &self.inner - } -} - #[derive(Debug, Component)] pub(crate) struct SystemComponent { diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 80ac346..54f6979 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,13 +1,12 @@ -use std::any::{Any, TypeId}; +use std::any::{type_name, Any, TypeId}; use std::panic::{RefUnwindSafe, UnwindSafe}; use hashbrown::HashMap; use seq_macro::seq; -use crate::component::Component; +use crate::component::{Component, HandleMut as ComponentHandleMut}; use crate::lock::Lock; use crate::system::{ - ComponentRefMut, Into as IntoSystem, Param, ParamWithInputFilter, @@ -26,7 +25,7 @@ use crate::World; pub struct Stateful<Func> { func: Func, - local_components: HashMap<Uid, Lock<Box<dyn Component>>>, + local_components: HashMap<Uid, Lock<Box<dyn Any>>>, } macro_rules! impl_system { @@ -110,14 +109,14 @@ macro_rules! impl_system { fn get_local_component_mut<LocalComponent: Component>( &self, - ) -> Option<ComponentRefMut<LocalComponent>> + ) -> Option<ComponentHandleMut<LocalComponent>> { let local_component = self.local_components .get(&LocalComponent::id())? .write_nonblock() .expect("Failed to aquire read-write local component lock"); - Some(ComponentRefMut::new(local_component)) + Some(ComponentHandleMut::new(local_component)) } fn set_local_component<LocalComponent: Component>( @@ -128,7 +127,10 @@ macro_rules! impl_system { self.local_components .insert( LocalComponent::id(), - Lock::new(Box::new(local_component)) + Lock::new( + Box::new(local_component), + type_name::<LocalComponent>() + ) ); } } diff --git a/ecs/src/type_name.rs b/ecs/src/type_name.rs deleted file mode 100644 index 54179be..0000000 --- a/ecs/src/type_name.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::any::type_name; - -pub trait TypeName -{ - /// Returns the name of this type. - fn type_name(&self) -> &'static str; -} - -impl<Item> TypeName for Vec<Item> -{ - fn type_name(&self) -> &'static str - { - type_name::<Self>() - } -} diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs index bcef73e..feed62c 100644 --- a/ecs/src/uid.rs +++ b/ecs/src/uid.rs @@ -1,23 +1,28 @@ -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::mem::transmute; use std::sync::atomic::{AtomicU32, Ordering}; +use crate::component::Component; use crate::util::{gen_mask_64, BitMask, NumberExt}; -static NEXT: AtomicU32 = AtomicU32::new(1); +static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID); + +static WILDCARD_ID: u32 = 1; const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63)); +const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31)); const KIND_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(0..=1)); #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] pub enum Kind { + Pair = 3, Entity = 2, Component = 1, } -/// Unique entity/component ID. +/// A unique identifier. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Uid { @@ -26,28 +31,150 @@ pub struct Uid impl Uid { + /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are + /// reserved. + pub const FIRST_UNIQUE_ID: u32 = 5; + /// Returns a new unique entity/component ID. pub fn new_unique(kind: Kind) -> Self { let id = NEXT.fetch_add(1, Ordering::Relaxed); Self { - inner: ID_BITS.field_prep(id as u64) | KIND_BITS.field_prep(kind as u64), + inner: ID_BITS.field_prep(u64::from(id)) | KIND_BITS.field_prep(kind as u64), + } + } + + #[must_use] + pub fn wildcard() -> Self + { + Self { + inner: ID_BITS.field_prep(u64::from(WILDCARD_ID)) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + /// Returns a new pair UID. + /// + /// # Panics + /// Will panic if either the given relation or target is a pair UID. + #[must_use] + pub fn new_pair(params: &PairParams) -> Self + { + assert_ne!( + params.relation.kind(), + Kind::Pair, + "Pair relation cannot be a pair" + ); + + assert_ne!( + params.target.kind(), + Kind::Pair, + "Pair target cannot be a pair" + ); + + Self { + inner: ID_BITS.field_prep(u64::from(params.target.id())) + | RELATION_BITS.field_prep(u64::from(params.relation.id())) + | KIND_BITS.field_prep(Kind::Pair as u64), } } #[must_use] pub fn id(&self) -> u32 { - self.inner.field_get(ID_BITS) as u32 + let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else { + unreachable!("Uid id does not fit in u32"); + }; + + id } #[must_use] pub fn kind(&self) -> Kind { + let Ok(kind) = u8::try_from(self.inner.field_get(KIND_BITS)) else { + unreachable!("Uid kind does not fit in u8"); + }; + // SAFETY: The kind bits cannot be invalid since they are set using the Kind enum // in the new_unique function - unsafe { transmute(self.inner.field_get(KIND_BITS) as u8) } + unsafe { transmute::<u8, Kind>(kind) } + } + + /// If this `Uid` is a pair, returns the relation as a component `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + #[must_use] + pub fn relation_component(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.relation())) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + #[must_use] + pub fn has_same_relation_as(&self, other: Self) -> bool + { + self.relation() == other.relation() + } + + /// If this `Uid` is a pair, returns the relation as a entity `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + #[must_use] + pub fn relation_entity(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.relation())) + | KIND_BITS.field_prep(Kind::Entity as u64), + } + } + + /// If this `Uid` is a pair, returns the target as a component `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + #[must_use] + pub fn target_component(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.id())) + | KIND_BITS.field_prep(Kind::Component as u64), + } + } + + /// If this `Uid` is a pair, returns the target as a entity `Uid`. + /// + /// # Panics + /// Will panic if this `Uid` is not a pair. + #[must_use] + pub fn target_entity(&self) -> Self + { + assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); + + Self { + inner: ID_BITS.field_prep(u64::from(self.id())) + | KIND_BITS.field_prep(Kind::Entity as u64), + } + } + + fn relation(self) -> u32 + { + let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else { + unreachable!("Uid relation does not fit in u32"); + }; + + relation } } @@ -62,3 +189,55 @@ impl Debug for Uid .finish_non_exhaustive() } } + +impl Display for Uid +{ + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result + { + if self.kind() == Kind::Pair { + return write!( + formatter, + "({}, {})", + self.relation(), + self.target_component() + ); + } + + if *self == Uid::wildcard() { + return write!(formatter, "*"); + } + + write!(formatter, "{}", self.id()) + } +} + +#[derive(Debug, Clone)] +pub struct PairParams +{ + pub relation: Uid, + pub target: Uid, +} + +pub trait With: 'static +{ + fn uid() -> Uid; +} + +impl<ComponentT: Component> With for ComponentT +{ + fn uid() -> Uid + { + Self::id() + } +} + +#[derive(Debug)] +pub enum Wildcard {} + +impl With for Wildcard +{ + fn uid() -> Uid + { + Uid::wildcard() + } +} diff --git a/ecs/src/util.rs b/ecs/src/util.rs index 0273b18..9ab4dc6 100644 --- a/ecs/src/util.rs +++ b/ecs/src/util.rs @@ -1,4 +1,171 @@ -use std::ops::BitAnd; +use std::hash::Hash; +use std::mem::transmute; +use std::ops::{BitAnd, Deref}; + +use hashbrown::HashMap; + +pub(crate) mod array_vec; + +pub trait VecExt<Item> +{ + fn insert_at_partition_point_by_key<Key>( + &mut self, + item: Item, + func: impl FnMut(&Item) -> Key, + ) where + Key: Ord; +} + +impl<Item> VecExt<Item> for Vec<Item> +{ + fn insert_at_partition_point_by_key<Key>( + &mut self, + item: Item, + mut func: impl FnMut(&Item) -> Key, + ) where + Key: Ord, + { + let key = func(&item); + + let insert_index = self.partition_point(|other_item| func(other_item) <= key); + + self.insert(insert_index, item); + } +} + +pub trait StreamingIterator +{ + type Item<'a> + where + Self: 'a; + + fn streaming_next(&mut self) -> Option<Self::Item<'_>>; + + fn streaming_map<NewItem, Func>(self, func: Func) -> StreamingMap<Self, Func> + where + Self: Sized, + Func: FnMut(Self::Item<'_>) -> NewItem, + { + StreamingMap { iter: self, func } + } + + fn streaming_find<'this, Predicate>( + &'this mut self, + mut predicate: Predicate, + ) -> Option<Self::Item<'this>> + where + Self: Sized, + Predicate: FnMut(&Self::Item<'this>) -> bool, + { + while let Some(item) = unsafe { + transmute::<Option<Self::Item<'_>>, Option<Self::Item<'_>>>( + self.streaming_next(), + ) + } { + if predicate(&item) { + return Some(item); + } + } + + None + } +} + +pub struct StreamingMap<Iter, Func> +{ + iter: Iter, + func: Func, +} + +impl<Iter, Func, Item> StreamingIterator for StreamingMap<Iter, Func> +where + Iter: StreamingIterator, + Func: FnMut(Iter::Item<'_>) -> Item, +{ + type Item<'a> + = Item + where + Iter: 'a, + Func: 'a; + + fn streaming_next(&mut self) -> Option<Self::Item<'_>> + { + Some((self.func)(self.iter.streaming_next()?)) + } +} + +#[derive(Debug)] +pub enum BorrowedOrOwned<'a, Value> +{ + Borrowned(&'a Value), + Owned(Value), +} + +impl<Value> Deref for BorrowedOrOwned<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + match self { + Self::Borrowned(value) => value, + Self::Owned(value) => value, + } + } +} + +#[derive(Debug, Clone)] +pub enum Either<A, B> +{ + A(A), + B(B), +} + +impl<A, B> Iterator for Either<A, B> +where + A: Iterator, + B: Iterator<Item = A::Item>, +{ + type Item = A::Item; + + fn next(&mut self) -> Option<Self::Item> + { + match self { + Self::A(a) => a.next(), + Self::B(b) => b.next(), + } + } +} + +pub trait HashMapExt<Key, Value> +{ + /// Returns true if the keys are a subset of another [`HashMap`]'s keys, i.e., `other` + /// contains at least all the keys in `self`. + fn keys_is_subset(&self, other: &Self) -> bool; + + /// Returns true if the keys are a superset of another [`HashMap`]'s keys, i.e., + /// `self` contains at least all the keys in `other`. + fn keys_is_superset(&self, other: &Self) -> bool; +} + +impl<Key, Value> HashMapExt<Key, Value> for HashMap<Key, Value> +where + Key: Eq + Hash, +{ + fn keys_is_subset(&self, other: &Self) -> bool + { + if self.len() <= other.len() { + self.keys().all(|key| other.contains_key(key)) + } else { + false + } + } + + fn keys_is_superset(&self, other: &Self) -> bool + { + other.keys_is_subset(self) + } +} pub trait Array<Item>: AsRef<[Item]> @@ -77,6 +244,7 @@ impl BitMask<u64> Self { mask } } + #[must_use] pub const fn value(self) -> u64 { self.mask @@ -86,6 +254,8 @@ impl BitMask<u64> #[must_use] pub const fn field_prep(self, field_value: u64) -> u64 { + debug_assert!(field_value < 1 << self.mask.count_ones()); + ((field_value) << self.mask.trailing_zeros()) & (self.mask) } } @@ -103,6 +273,7 @@ impl BitAnd<u64> for BitMask<u64> pub trait NumberExt: Sized { /// Returns a range of bits (field) specified by the provided [`BitMask`]. + #[must_use] fn field_get(self, field_mask: BitMask<Self>) -> Self; } @@ -152,4 +323,11 @@ mod tests { assert_eq!(BitMask::new(0b11000).field_prep(3), 0b11000); } + + #[test] + #[should_panic] + fn bitmask_field_prep_too_large_value_panics() + { + let _ = BitMask::new(0b001110).field_prep(9); + } } diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs new file mode 100644 index 0000000..a37b1f9 --- /dev/null +++ b/ecs/src/util/array_vec.rs @@ -0,0 +1,131 @@ +use std::mem::MaybeUninit; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug)] +pub struct ArrayVec<Item, const CAPACITY: usize> +{ + items: [MaybeUninit<Item>; CAPACITY], + len: usize, +} + +impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY> +{ + #[inline] + #[must_use] + pub fn len(&self) -> usize + { + self.len + } + + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool + { + self.len == 0 + } + + pub fn push(&mut self, item: Item) + { + assert!(self.len < CAPACITY); + + self.items[self.len].write(item); + + self.len += 1; + } + + pub fn insert(&mut self, index: usize, item: Item) + { + assert!(index <= self.len); + assert!(self.len < CAPACITY); + + if index == self.len { + self.push(item); + return; + } + + unsafe { + std::ptr::copy( + &self.items[index], + &mut self.items[index + 1], + self.len - index, + ); + } + + self.items[index].write(item); + + self.len += 1; + } +} + +impl<Item, const CAPACITY: usize> Extend<Item> for ArrayVec<Item, CAPACITY> +{ + fn extend<IntoIter: IntoIterator<Item = Item>>(&mut self, iter: IntoIter) + { + for item in iter { + self.push(item); + } + } +} + +impl<Item, const CAPACITY: usize> AsRef<[Item]> for ArrayVec<Item, CAPACITY> +{ + fn as_ref(&self) -> &[Item] + { + let ptr = &raw const self.items[..self.len]; + + unsafe { &*(ptr as *const [Item]) } + } +} + +impl<Item, const CAPACITY: usize> AsMut<[Item]> for ArrayVec<Item, CAPACITY> +{ + fn as_mut(&mut self) -> &mut [Item] + { + let ptr = &raw mut self.items[..self.len]; + + unsafe { &mut *(ptr as *mut [Item]) } + } +} + +impl<Item, const CAPACITY: usize> Deref for ArrayVec<Item, CAPACITY> +{ + type Target = [Item]; + + fn deref(&self) -> &Self::Target + { + self.as_ref() + } +} + +impl<Item, const CAPACITY: usize> DerefMut for ArrayVec<Item, CAPACITY> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + self.as_mut() + } +} + +impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY> +{ + fn default() -> Self + { + Self { + items: [const { MaybeUninit::uninit() }; CAPACITY], + len: 0, + } + } +} + +impl<Item, const CAPACITY: usize> Drop for ArrayVec<Item, CAPACITY> +{ + fn drop(&mut self) + { + for item in &mut self.items[..self.len] { + // SAFETY: The items from index 0 to the length index will always be + // initialized and satisfy all the invariants of the Item type. + unsafe { + item.assume_init_drop(); + } + } + } +} |