diff options
Diffstat (limited to 'ecs/src')
-rw-r--r-- | ecs/src/actions.rs | 81 | ||||
-rw-r--r-- | ecs/src/archetype.rs | 61 | ||||
-rw-r--r-- | ecs/src/component.rs | 469 | ||||
-rw-r--r-- | ecs/src/component/local.rs | 13 | ||||
-rw-r--r-- | ecs/src/component/storage.rs | 1118 | ||||
-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.rs | 95 | ||||
-rw-r--r-- | ecs/src/event/component.rs | 126 | ||||
-rw-r--r-- | ecs/src/event/start.rs | 7 | ||||
-rw-r--r-- | ecs/src/extension.rs | 16 | ||||
-rw-r--r-- | ecs/src/lib.rs | 672 | ||||
-rw-r--r-- | ecs/src/lock.rs | 210 | ||||
-rw-r--r-- | ecs/src/pair.rs | 198 | ||||
-rw-r--r-- | ecs/src/phase.rs | 13 | ||||
-rw-r--r-- | ecs/src/query.rs | 622 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 84 | ||||
-rw-r--r-- | ecs/src/query/options.rs | 66 | ||||
-rw-r--r-- | ecs/src/query/term.rs | 115 | ||||
-rw-r--r-- | ecs/src/relationship.rs | 418 | ||||
-rw-r--r-- | ecs/src/sole.rs | 22 | ||||
-rw-r--r-- | ecs/src/system.rs | 162 | ||||
-rw-r--r-- | ecs/src/system/stateful.rs | 60 | ||||
-rw-r--r-- | ecs/src/tuple.rs | 204 | ||||
-rw-r--r-- | ecs/src/type_name.rs | 15 | ||||
-rw-r--r-- | ecs/src/uid.rs | 216 | ||||
-rw-r--r-- | ecs/src/util.rs | 285 | ||||
-rw-r--r-- | ecs/src/util/array_vec.rs | 131 |
29 files changed, 4170 insertions, 2261 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index f7b00d3..3dd8755 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -1,12 +1,8 @@ use std::marker::PhantomData; -use std::sync::{Arc, Weak}; - -use crate::component::{ - Component, - Metadata as ComponentMetadata, - Sequence as ComponentSequence, -}; -use crate::system::{NoInitParamFlag, Param as SystemParam, System}; +use std::rc::{Rc, Weak}; + +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}; @@ -20,36 +16,61 @@ pub struct Actions<'world> impl<'world> Actions<'world> { - /// Adds a spawning a new entity to the action queue. + /// 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_vec())); + self.action_queue + .push(Action::Spawn(components.into_parts_array().into())); } - /// Adds component(s) to a entity. + /// 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); + + self.action_queue.push(Action::Despawn(entity_uid)); + } + + /// Queues up adding component(s) to a entity at the end of the current tick. pub fn add_components<Comps>(&mut self, entity_uid: Uid, components: Comps) where Comps: ComponentSequence, { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - self.action_queue - .push(Action::AddComponents(entity_uid, components.into_vec())); + if Comps::COUNT == 0 { + return; + } + + self.action_queue.push(Action::AddComponents( + entity_uid, + components.into_parts_array().into(), + )); } - /// Removes component(s) from a entity. - 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); - self.action_queue - .push(Action::RemoveComponents(entity_uid, Comps::metadata())); + let mut component_ids = component_ids.into_iter().peekable(); + + if component_ids.peek().is_none() { + return; + } + + self.action_queue.push(Action::RemoveComponents( + entity_uid, + component_ids.collect(), + )); } - /// Adds stopping the loop in [`Engine::event_loop`] at the next opportune time to the - /// action queue. + /// Stops the [`World`]. The world will finish the current tick and that tick will be + /// the last. pub fn stop(&mut self) { self.action_queue.push(Action::Stop); @@ -66,18 +87,17 @@ 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), } } } -unsafe impl<'world> SystemParam<'world> for Actions<'world> +impl<'world> SystemParam<'world> for Actions<'world> { - type Flags = NoInitParamFlag; type Input = (); fn initialize<SystemImpl>( @@ -122,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<'_> @@ -139,8 +159,9 @@ impl<'weak_ref> Ref<'weak_ref> #[derive(Debug)] pub(crate) enum Action { - Spawn(Vec<Box<dyn Component>>), - AddComponents(Uid, Vec<Box<dyn Component>>), - RemoveComponents(Uid, Vec<ComponentMetadata>), + Spawn(Vec<ComponentParts>), + Despawn(Uid), + 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 d2ee36a..0000000 --- a/ecs/src/archetype.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; - -use crate::component::{ - IsOptional as ComponentIsOptional, - Metadata as ComponentMetadata, -}; -use crate::uid::Uid; - -/// 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 - { - debug_assert!( - components_metadata.as_ref().len() > 0, - "Cannot create a archetype ID from a empty component metadata list" - ); - - 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" - ); - - Self::new( - components_metadata - .as_ref() - .iter() - .filter_map(|component_metadata| { - if component_metadata.is_optional == ComponentIsOptional::Yes { - return None; - } - - Some(component_metadata.id) - }), - ) - } - - /// Returns the ID of a archetype with the given components. - fn new(component_ids: impl IntoIterator<Item = Uid>) -> Self - { - let mut hasher = DefaultHasher::new(); - - for component_id in component_ids { - component_id.hash(&mut hasher); - } - - let hash = hasher.finish(); - - Self { hash } - } -} diff --git a/ecs/src/component.rs b/ecs/src/component.rs index a9894b7..5a8cd0b 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,79 +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::lock::{ReadGuard, WriteGuard}; -use crate::system::{ComponentRef, ComponentRefMut, Input as SystemInput}; -use crate::type_name::TypeName; +use crate::lock::{ + Error as LockError, + MappedReadGuard, + MappedWriteGuard, + ReadGuard, + WriteGuard, +}; +use crate::system::Input as SystemInput; use crate::uid::Uid; -use crate::{EntityComponent, World}; +use crate::util::Array; +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> - where - Self: Sized; - - type Ref<'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; - - #[doc(hidden)] - fn as_any_mut(&mut self) -> &mut dyn Any; - - #[doc(hidden)] - fn as_any(&self) -> &dyn Any; - - /// Whether the component `self` is optional. Returns the same value as - /// [`Component::is_optional`]. - fn self_is_optional(&self) -> IsOptional - { - IsOptional::No - } - - /// Returns whether this component is optional. - #[must_use] - fn is_optional() -> IsOptional - where - Self: Sized, - { - IsOptional::No - } + /// 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>() } } @@ -85,249 +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, +#[derive(Debug)] +pub struct Handle<'a, ComponentData: 'static> { - type Component = ComponentT; - type Ref<'component> = Option<ComponentRef<'component, ComponentT>>; - type RefMut<'component> = Option<ComponentRefMut<'component, ComponentT>>; + inner: MappedReadGuard<'a, ComponentData>, +} - fn id() -> 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> { - ComponentT::id() + Ok(Self::new( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + )) } - fn self_id(&self) -> Uid + pub(crate) fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Self { - Self::id() + Self { + inner: inner.map(|component| { + component + .downcast_ref::<ComponentData>() + .unwrap_or_else(|| { + panic!( + "Failed to downcast component to type {}", + type_name::<ComponentData>() + ); + }) + }), + } } +} + +impl<ComponentData: 'static> Deref for Handle<'_, ComponentData> +{ + type Target = ComponentData; - fn as_any_mut(&mut self) -> &mut dyn Any + fn deref(&self) -> &Self::Target { - self + &self.inner } +} - fn as_any(&self) -> &dyn Any +#[derive(Debug)] +pub struct HandleMut<'a, ComponentData: 'static> +{ + inner: MappedWriteGuard<'a, ComponentData>, +} + +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) -> IsOptional + pub(crate) fn new(inner: WriteGuard<'comp, Box<dyn Any>>) -> Self { - Self::is_optional() + Self { + inner: inner.map(|component| { + component + .downcast_mut::<ComponentData>() + .unwrap_or_else(|| { + panic!( + "Failed to downcast component to type {}", + type_name::<ComponentData>() + ); + }) + }), + } } +} - fn is_optional() -> IsOptional +impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData> +{ + type Target = ComponentData; + + fn deref(&self) -> &Self::Target { - IsOptional::Yes + &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 {} +#[derive(Debug, thiserror::Error)] +pub enum HandleError +{ + #[error(transparent)] + AcquireLockFailed(#[from] AcquireLockError), +} -/// A sequence of components. -pub trait Sequence +#[derive(Debug, thiserror::Error)] +#[error("Failed to acquire component lock")] +pub struct AcquireLockError(#[source] LockError); + +macro_rules! inner { + ($c: tt) => { + seq!(I in 0..=$c { + impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*) + { + const COUNT: usize = $c + 1; + + type PartsArray = [Parts; $c + 1]; + + fn into_parts_array(self) -> Self::PartsArray + { + [#({ + self.I.into_parts() + },)*] + } + } + }); + }; +} + +seq!(C in 0..=16 { + inner!(C); +}); + +impl Sequence for () { - type MutRefs<'component> - where - Self: 'component; + type PartsArray = [Parts; 0]; - type Refs<'component> - where - Self: 'component; - - fn into_vec(self) -> Vec<Box<dyn Component>>; - - fn metadata() -> Vec<Metadata>; - - fn from_components_mut<'component>( - components: impl Iterator<Item = &'component EntityComponent>, - world: &'component World, - lock_component: fn( - entity_component: &EntityComponent, - ) -> WriteGuard<'_, Box<dyn Component>>, - ) -> Self::MutRefs<'component>; - - fn from_components<'component>( - components: impl Iterator<Item = &'component EntityComponent>, - world: &'component World, - lock_component: fn( - entity_component: &EntityComponent, - ) -> ReadGuard<'_, Box<dyn Component>>, - ) -> Self::Refs<'component>; + const COUNT: usize = 0; + + fn into_parts_array(self) -> Self::PartsArray + { + [] + } +} + +pub trait IntoParts +{ + fn into_parts(self) -> Parts; +} + +impl<ComponentT> IntoParts for ComponentT +where + ComponentT: 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: IsOptional, + 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 + } + + #[must_use] + pub fn builder() -> PartsBuilder + { + PartsBuilder::default() + } + + pub(crate) fn into_data(self) -> Box<dyn Any> + { + self.data } } -/// Whether or not a `Component` is optional. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IsOptional +#[derive(Debug)] +pub struct PartsBuilder { - Yes, - No, + name: &'static str, } -impl From<bool> for IsOptional +impl PartsBuilder { - fn from(is_optional: bool) -> Self + #[must_use] + pub fn name(mut self, name: &'static str) -> Self { - if is_optional { - return IsOptional::Yes; - } + self.name = name; + self + } - IsOptional::No + #[must_use] + pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts + { + Parts { + id, + name: self.name, + data: Box::new(data), + } } } -pub trait FromOptionalMut<'comp> +impl Default for PartsBuilder { - fn from_optional_mut_component( - optional_component: Option<WriteGuard<'comp, Box<dyn Component>>>, - world: &'comp World, - ) -> Self; + fn default() -> Self + { + Self { name: "(unspecified)" } + } } -pub trait FromOptional<'comp> +/// Pending component removals for a entity. +#[derive(Debug, Clone, Component)] +pub struct Removals { - fn from_optional_component( - optional_component: Option<ReadGuard<'comp, Box<dyn Component>>>, - world: &'comp World, - ) -> Self; + component_ids: HashSet<Uid>, } -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>,)* - { - type MutRefs<'component> = (#(Comp~I::RefMut<'component>,)*) - where Self: 'component; - - type Refs<'component> = (#(Comp~I::Ref<'component>,)*) - where Self: 'component; - - fn into_vec(self) -> Vec<Box<dyn Component>> - { - Vec::from_iter([#(Box::new(self.I) as Box<dyn Component>,)*]) - } - - fn metadata() -> Vec<Metadata> - { - vec![ - #( - Metadata { - id: Comp~I::id(), - is_optional: Comp~I::is_optional() - }, - )* - ] - } - - fn from_components_mut<'component>( - components: impl Iterator<Item = &'component EntityComponent>, - world: &'component World, - lock_component: fn( - entity_component: &EntityComponent, - ) -> WriteGuard<'_, Box<dyn Component>>, - ) -> Self::MutRefs<'component> - { - #( - let mut comp_~I: Option<WriteGuard<Box<dyn Component>>> = None; - )* - - for comp in components { - #( - if comp.id == Comp~I::Component::id() { - comp_~I = Some(lock_component(comp)); - continue; - } - )* - } - - (#( - Comp~I::RefMut::from_optional_mut_component(comp_~I, world), - )*) - } +impl Removals +{ + pub fn contains<ComponentT: Component>(&self) -> bool + { + self.contains_id(ComponentT::id()) + } - fn from_components<'component>( - components: impl Iterator<Item = &'component EntityComponent>, - world: &'component World, - lock_component: fn( - entity_component: &EntityComponent, - ) -> ReadGuard<'_, Box<dyn Component>>, - ) -> Self::Refs<'component> - { + pub fn contains_id(&self, component_id: Uid) -> bool + { + self.component_ids.contains(&component_id) + } - #( - let mut comp_~I: Option<ReadGuard<Box<dyn Component>>> = None; - )* - - for comp in components { - #( - if comp.id == Comp~I::Component::id() { - comp_~I = Some(lock_component(comp)); - continue; - } - )* - } - - (#( - Comp~I::Ref::from_optional_component(comp_~I, world), - )*) - } - } - }); - }; + pub(crate) fn add_ids(&mut self, ids: impl IntoIterator<Item = Uid>) + { + self.component_ids.extend(ids) + } } -seq!(C in 0..=64 { - inner!(C); -}); +impl FromIterator<Uid> for Removals +{ + 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 20627bf..0f6f641 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,21 +1,20 @@ 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>, } -unsafe impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> +impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent> where LocalComponent: Component, { - type Flags = (); type Input = LocalComponent; fn initialize<SystemImpl>( @@ -39,7 +38,7 @@ where } } -impl<'world, LocalComponent> Deref for Local<'world, LocalComponent> +impl<LocalComponent> Deref for Local<'_, LocalComponent> where LocalComponent: Component, { @@ -51,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 ffd682e..b27b552 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -1,621 +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::collections::{HashMap, HashSet}; -use std::slice::Iter as SliceIter; -use std::vec::IntoIter as OwnedVecIter; - -use crate::archetype::Id as ArchetypeId; -use crate::component::{ - Component, - IsOptional as ComponentIsOptional, - Metadata as ComponentMetadata, +use std::vec::IntoIter as VecIntoIter; + +use hashbrown::HashMap; + +use crate::component::storage::archetype::{ + Archetype, + Entity as ArchetypeEntity, + EntityComponent as ArchetypeEntityComponent, + Id as ArchetypeId, }; -use crate::type_name::TypeName; -use crate::uid::Uid; -use crate::util::Sortable; -use crate::EntityComponent; +use crate::component::storage::graph::{ + ArchetypeAddEdgeDfsIter, + ArchetypeAddEdgeDfsIterResult, + ArchetypeEdges, + Graph, +}; +use crate::uid::{Kind as UidKind, Uid}; +use crate::util::{BorrowedOrOwned, Either, StreamingIterator, VecExt}; -#[derive(Debug, Default)] -pub struct Storage +pub mod archetype; + +mod graph; + +#[derive(Debug)] +pub struct ArchetypeSearchTerms<'a> { - archetypes: Vec<Archetype>, - archetype_lookup: RefCell<HashMap<ArchetypeId, ArchetypeLookupEntry>>, - entity_archetype_lookup: HashMap<Uid, ArchetypeId>, + pub required_components: &'a [Uid], + pub excluded_components: &'a [Uid], } -impl Storage +impl ArchetypeSearchTerms<'_> { - pub fn find_entities<CompMetadataList>( - &self, - mut components_metadata: CompMetadataList, - ) -> ArchetypeRefIter<'_> - where - CompMetadataList: Sortable<Item = ComponentMetadata>, - CompMetadataList: AsRef<[ComponentMetadata]>, + fn excluded_contains(&self, comp_id: Uid) -> bool { - components_metadata.sort_by_key_b(|component_metadata| component_metadata.id); + let comp_id_kind = comp_id.kind(); - let archetype_id = ArchetypeId::from_components_metadata(&components_metadata); + debug_assert!( + comp_id_kind == UidKind::Component + || (comp_id_kind == UidKind::Pair + && comp_id.target_component() != Uid::wildcard()) + ); - // This looks stupid but the borrow checker complains otherwise - if self.archetype_lookup.borrow().contains_key(&archetype_id) { - return self.iter_archetypes_by_lookup(archetype_id); - } + let is_found = self.excluded_components.binary_search(&comp_id).is_ok(); - let comp_ids_set = create_non_opt_component_id_set(components_metadata.as_ref()); + 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() + }); + } - let matching_archetype_indices = self - .archetypes - .iter() - .enumerate() - .filter_map(|(index, archetype)| { - if archetype.component_ids_is_superset(&comp_ids_set) { - return Some(index); - } + is_found + } - None - }) - .collect(); - - self.archetype_lookup.borrow_mut().insert( - archetype_id, - ArchetypeLookupEntry { - component_ids: comp_ids_set, - archetype_indices: matching_archetype_indices, - }, - ); + fn contains_conflicting(&self) -> bool + { + self.excluded_components.iter().any(|excluded_comp_id| { + self.required_components + .binary_search(excluded_comp_id) + .is_ok() + }) + } - self.iter_archetypes_by_lookup(archetype_id) + fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool + { + self.required_components + .iter() + .all(|comp_id| archetype.contains_matching_component(*comp_id)) } +} - pub fn get_entity_archetype(&self, entity_uid: Uid) -> Option<&Archetype> +#[derive(Debug, Default)] +pub struct Storage +{ + graph: Graph, + entity_archetype_lookup: HashMap<Uid, ArchetypeId>, + imaginary_archetypes: RefCell<Vec<ImaginaryArchetype>>, +} + +impl Storage +{ + pub fn search_archetypes<'search_terms>( + &self, + search_terms: ArchetypeSearchTerms<'search_terms>, + ) -> ArchetypeRefIter<'_, 'search_terms> { - let archetype_id = self.entity_archetype_lookup.get(&entity_uid)?; + 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_index = - self.find_archetype_index_with_entity(*archetype_id, entity_uid)?; + 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, + }; + }; - self.archetypes.get(archetype_index) + ArchetypeRefIter { + storage: self, + pre_iter: Either::A([archetype_id].into_iter()), + dfs_iter: add_edge_recursive_iter, + search_terms, + } } - #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] - pub fn push_entity( - &mut self, - entity_uid: Uid, - mut components: Vec<Box<dyn Component>>, - ) -> Result<(ArchetypeId, Uid), Error> + pub fn get_archetype_by_id(&self, id: ArchetypeId) -> Option<&Archetype> { - if self.entity_archetype_lookup.contains_key(&entity_uid) { - return Err(Error::EntityAlreadyExists(entity_uid)); + Some(self.graph.get_node_by_id(id)?.archetype()) + } + + pub fn create_entity(&mut self, uid: Uid) -> Result<(), Error> + { + debug_assert_eq!(uid.kind(), UidKind::Entity); + + if self.entity_archetype_lookup.contains_key(&uid) { + return Err(Error::EntityAlreadyExists(uid)); } - components.sort_by_key(|component| component.self_id()); + let empty_archetype_id = ArchetypeId::new_empty(); - #[cfg(feature = "debug")] - tracing::debug!( - "Pushing entity with components: ({})", - components - .iter() - .map(|component| component.type_name()) - .collect::<Vec<_>>() - .join(", ") - ); + let archetype_node = self.graph.get_or_create_node(empty_archetype_id, &[]); - let archetype_id = ArchetypeId::from_components_metadata( - &components - .iter() - .map(|component| ComponentMetadata::get(&**component)) - .collect::<Vec<_>>(), - ); + archetype_node + .archetype_mut() + .push_entity(ArchetypeEntity::new(uid, [])); - let comp_ids_set = create_non_opt_component_id_set( - components - .iter() - .map(|component| ComponentMetadata::get(&**component)), - ); + self.entity_archetype_lookup.insert(uid, empty_archetype_id); - let archetype_index = - self.get_or_create_archetype(archetype_id, &comp_ids_set, &components); + Ok(()) + } - self.populate_matching_archetype_lookup_entries(&comp_ids_set, archetype_index); + 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 Err(Error::EntityDoesNotExist(entity_uid)); + }; - 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"); - archetype.push_entity(entity_uid, components); + let entity = archetype_node + .archetype_mut() + .remove_entity(entity_uid) + .expect("Entity should exist in archetype"); - archetype - .entity_lookup - .insert(entity_uid, archetype.entities.len() - 1); + self.entity_archetype_lookup.remove(&entity_uid); - self.entity_archetype_lookup - .insert(entity_uid, archetype_id); + Ok(entity) + } - Ok((archetype_id, entity_uid)) + 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) } - pub fn add_components_to_entity( + pub fn add_entity_component( &mut self, entity_uid: Uid, - components: Vec<Box<dyn Component>>, - ) -> Option<()> + (component_id, component_name, component): (Uid, &'static str, Box<dyn Any>), + ) -> Result<(), Error> { - let archetype_id = self.entity_archetype_lookup.get(&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, + }); + } - let archetype_index = - self.find_archetype_index_with_entity(*archetype_id, entity_uid)?; + 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 = self.archetypes.get_mut(archetype_index)?; + add_edge_id + } else { + let archetype_node = self + .graph + .get_node_by_id(archetype_id) + .expect("Archetype should exist"); - 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 (add_edge_id, add_edge_comp_ids) = + archetype_node.make_add_edge(component_id); - let entity = archetype.take_entity(entity_uid)?; + if !self.graph.contains_archetype(add_edge_id) { + self.graph.create_node(add_edge_id, &add_edge_comp_ids); + } - self.entity_archetype_lookup.remove(&entity_uid); + add_edge_id + }; + + 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, + ); - 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"); + add_edge_archetype.push_entity(entity); - Some(()) + self.entity_archetype_lookup + .insert(entity_uid, add_edge_archetype_id); + + Ok(()) } - pub fn remove_components_from_entity( + pub fn remove_entity_component( &mut self, entity_uid: Uid, - component_ids: impl IntoIterator<Item = Uid>, - ) -> Option<()> + component_id: Uid, + ) -> Result<(), Error> { - let archetype_id = self.entity_archetype_lookup.get(&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::ComponentNotFoundInEntity { + entity: entity_uid, + component: component_id, + }); + } - let archetype_index = - self.find_archetype_index_with_entity(*archetype_id, 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 = self.archetypes.get_mut(archetype_index)?; + remove_edge_id + }); - let entity = archetype.take_entity(entity_uid)?; + let archetype_node = self + .graph + .get_node_by_id_mut(archetype_id) + .expect("Archetype should exist"); - let component_ids_set = component_ids.into_iter().collect::<HashSet<_>>(); + let mut entity = archetype_node + .archetype_mut() + .remove_entity(entity_uid) + .expect("Entity should exist in archetype"); - self.entity_archetype_lookup.remove(&entity_uid); + entity.remove_component(component_id, archetype_node.archetype()); - 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.graph + .get_node_by_id_mut(remove_edge_id) + .expect("Remove edge archetype should exist") + .archetype_mut() + .push_entity(entity); - Some(()) + self.entity_archetype_lookup + .insert(entity_uid, remove_edge_id); + + 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 { + for imaginary_archetype in self.imaginary_archetypes.get_mut().drain(..) { + if self.graph.contains_archetype(imaginary_archetype.id) { continue; } - // There shouldn't be duplicate archetype indices in the lookup entry - if lookup_entry.archetype_indices.contains(&archetype_index) { - continue; - } - - if lookup_entry.component_ids.is_subset(comp_ids_set) { - lookup_entry.archetype_indices.push(archetype_index); - } + self.graph + .create_node(imaginary_archetype.id, &imaginary_archetype.component_ids); } } - fn get_or_create_archetype( - &mut self, - archetype_id: ArchetypeId, - comp_ids_set: &HashSet<Uid>, - 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(); + 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(); + }; + + let mut found = Vec::<ArchetypeId>::new(); + + 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 lookup_entry = archetype_lookup.entry(archetype_id).or_insert_with(|| { - ArchetypeLookupEntry { - component_ids: comp_ids_set.clone(), - archetype_indices: Vec::with_capacity(1), + if search_terms.excluded_contains(add_edge_component_id) { + search_iter.pop(); + continue; + } + + let node = self + .graph + .get_node_by_id(node_id) + .expect("Graph node found through DFS doesn't exist"); + + if node.archetype().component_cnt() < search_terms.required_components.len() { + continue; + } + + if !search_terms.archetype_contains_all_required(node.archetype()) { + continue; } - }); - if lookup_entry.archetype_indices.is_empty() { - self.archetypes.push(Archetype::new( - components.iter().map(|component| component.self_id()), - )); + found.push(node.archetype().id()); - lookup_entry - .archetype_indices - .push(self.archetypes.len() - 1); + search_iter.pop(); } - // SAFETY: Above, we push a archetype index if archetype_indices is empty so this - // cannot fail - unsafe { *lookup_entry.archetype_indices.first().unwrap_unchecked() } + found } +} - fn find_archetype_index_with_entity( +#[cfg(feature = "vizoxide")] +impl Storage +{ + pub fn create_vizoxide_archetype_graph( &self, - archetype_id: ArchetypeId, - entity_uid: Uid, - ) -> Option<usize> + graph_name: impl AsRef<str>, + params: VizoxideArchetypeGraphParams, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> { - let archetype_lookup = self.archetype_lookup.borrow_mut(); + 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); + } - let archetype_lookup_entry = archetype_lookup.get(&archetype_id)?; + 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()?; + } - // TODO: Also have a hashmap for precise archetype ID -> archetype index lookup. - // This way is slow - archetype_lookup_entry - .archetype_indices - .iter() - .find(|archetype_index| { - let Some(archetype) = self.archetypes.get(**archetype_index) else { - return false; - }; + 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()?; + } + } + } - archetype.has_entity(entity_uid) - }) - .copied() + drop(viz_node_lookup); + + Ok(viz_graph) } - fn iter_archetypes_by_lookup(&self, archetype_id: ArchetypeId) - -> ArchetypeRefIter<'_> + 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> { - let archetype_lookup = self.archetype_lookup.borrow(); - - // The archetype indices have to be cloned to prevent a panic when query - // iterations are nested. The panic happens because the archetype_lookup RefCell - // is borrowed and it tries to mutably borrow it - let archetype_indices = archetype_lookup - .get(&archetype_id) - .unwrap() - .archetype_indices - .clone(); - - ArchetypeRefIter { - indices: archetype_indices.into_iter(), - archetypes: &self.archetypes, + 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>, - 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(), - entities: Vec::new(), - } - } + type Item = &'component_storage Archetype; - pub fn component_ids_is_superset(&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 get_index_for_component(&self, component_id: Uid) -> Option<usize> - { - self.component_ids.get(&component_id).copied() - } - - fn push_entity( - &mut self, - entity_uid: Uid, - components: impl IntoIterator<Item = Box<dyn Component>>, - ) - { - self.entities.push(ArchetypeEntity { - uid: entity_uid, - components: components.into_iter().map(Into::into).collect(), - }); - } - - pub fn take_entity(&mut self, entity_uid: Uid) -> Option<ArchetypeEntity> - { - let entity_index = self.entity_lookup.remove(&entity_uid)?; - - let entity = self.entities.remove(entity_index); - for index in self.entity_lookup.values_mut() { - if *index > entity_index { - *index -= 1; + 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!(); + } } - } + }; - Some(entity) - } - - fn has_entity(&self, entity_uid: Uid) -> bool - { - self.entity_lookup.contains_key(&entity_uid) + Some( + self.storage + .get_archetype_by_id(archetype_id) + .expect("Archetype should exist"), + ) } } -#[derive(Debug)] -pub struct ArchetypeEntity +impl ArchetypeRefIter<'_, '_> { - uid: Uid, - components: Vec<EntityComponent>, -} - -impl ArchetypeEntity -{ - pub fn uid(&self) -> Uid + fn find_edges_of_imaginary_archetype( + &self, + imaginary_archetype_comps: &[Uid], + ) -> Vec<(Uid, ArchetypeEdges)> { - self.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 components(&self) -> &[EntityComponent] - { - &self.components - } + if found_archetype.component_cnt() < imaginary_archetype_comps.len() + 1 { + return None; + } - pub fn get_component(&self, index: usize) -> Option<&EntityComponent> - { - self.components.get(index) - } -} + 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"); -#[derive(Debug)] -pub struct ArchetypeRefIter<'component_storage> -{ - indices: OwnedVecIter<usize>, - archetypes: &'component_storage [Archetype], -} + let mut add_edge_comp_ids = imaginary_archetype_comps.to_vec(); -impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage> -{ - type Item = &'component_storage Archetype; + add_edge_comp_ids + .insert_at_partition_point_by_key(unique_comp_id, |id| *id); - fn next(&mut self) -> Option<Self::Item> - { - let archetype_index = self.indices.next()?; + let add_edge = ArchetypeId::new(&add_edge_comp_ids); - Some( - self.archetypes - .get(archetype_index) - .expect("Archetype index in archetype lookup entry was not found"), - ) + Some(( + unique_comp_id, + ArchetypeEdges { add: Some(add_edge), remove: None }, + )) + }) + .collect::<Vec<_>>() } } -#[derive(Debug)] -pub struct EntityIter<'archetype> +#[derive(Debug, thiserror::Error)] +pub enum Error { - iter: SliceIter<'archetype, ArchetypeEntity>, -} + #[error("Entity with ID {0:?} already exists")] + EntityAlreadyExists(Uid), -impl<'archetype> Iterator for EntityIter<'archetype> -{ - type Item = &'archetype ArchetypeEntity; + #[error("Entity with ID {0:?} does not exist")] + EntityDoesNotExist(Uid), - fn next(&mut self) -> Option<Self::Item> + #[error("Entity with ID {entity:?} already has component with ID {component:?}")] + ComponentAlreadyInEntity { - self.iter.next() - } + entity: Uid, component: Uid + }, + + #[error("Entity with ID {entity:?} does not have component with ID {component:?}")] + ComponentNotFoundInEntity + { + entity: Uid, component: Uid + }, } -fn create_non_opt_component_id_set<Item>( - component_metadata_iter: impl IntoIterator<Item = Item>, -) -> HashSet<Uid> -where - Item: Borrow<ComponentMetadata>, +#[derive(Debug)] +struct ImaginaryArchetype { - component_metadata_iter - .into_iter() - .filter_map(|item| { - let component_metadata = item.borrow(); - - if component_metadata.is_optional == ComponentIsOptional::Yes { - return None; - } - - Some(component_metadata.id) - }) - .collect::<HashSet<_>>() + 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, - IsOptional as ComponentIsOptional, - 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"); - - assert_eq!(archetype.component_ids.len(), 2); + let mut new_storage = Storage::default(); - // One entity - assert_eq!(archetype.entities.len(), 1); + let uid = Uid::new_unique(UidKind::Entity); - let entity_components = archetype - .entities - .first() - .expect("Expected a entity in archetype"); + new_storage.create_entity(uid).expect("Expected Ok"); - assert_eq!(entity_components.components.len(), 2); + 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!(component_storage.archetype_lookup.borrow().len(), 1); + assert_eq!(archetype_node.archetype().component_cnt(), 0); + assert_eq!(archetype_node.archetype().entity_cnt(), 1); - let mut components_metadata = [ - ComponentMetadata { - id: HealthPotion::id(), - is_optional: ComponentIsOptional::No, - }, - ComponentMetadata { - id: Hookshot::id(), - is_optional: ComponentIsOptional::No, - }, - ]; - - components_metadata.sort_by_key(|comp_metadata| comp_metadata.id); - - let archetype_lookup = component_storage.archetype_lookup.borrow(); - - let lookup_entry = archetype_lookup - .get(&ArchetypeId::from_components_metadata(&components_metadata)) - .expect("Expected entry in archetype lookup map"); - - let first_archetype_index = lookup_entry - .archetype_indices - .first() - .expect("Expected archetype lookup to contain a archetype reference"); - - assert_eq!(*first_archetype_index, 0); + 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.rs b/ecs/src/event.rs index 1a4edcc..9cea807 100644 --- a/ecs/src/event.rs +++ b/ecs/src/event.rs @@ -1,96 +1 @@ -use std::any::TypeId; -use std::fmt::Debug; -use std::hash::{DefaultHasher, Hash, Hasher}; - -use seq_macro::seq; - pub mod component; - -pub trait Event: Debug + 'static -{ - /// Returns the ID of this event. - #[must_use] - fn id() -> Id - where - Self: Sized, - { - Id::new::<Self, ()>(None) - } -} - -pub mod start; - -/// The ID of a [`Event`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Id -{ - inner: TypeId, - extra: Option<u64>, -} - -impl Id -{ - #[must_use] - pub fn new<EventT, Extra>(extra: Option<Extra>) -> Self - where - EventT: Event, - Extra: Hash, - { - Self { - inner: TypeId::of::<EventT>(), - extra: extra.map(|extra| { - let mut hasher = DefaultHasher::new(); - - extra.hash(&mut hasher); - - hasher.finish() - }), - } - } -} - -pub trait Ids -{ - type Iter<'a>: Iterator<Item = &'a Id> - where - Self: 'a; - - fn iter(&self) -> Self::Iter<'_>; -} - -/// A sequence of events. -pub trait Sequence -{ - type Ids: Ids; - - fn ids() -> Self::Ids; -} - -macro_rules! impl_sequence { - ($c: tt) => { - seq!(I in 0..=$c { - impl Ids for [Id; $c + 1] - { - type Iter<'a> = std::slice::Iter<'a, Id>; - - fn iter(&self) -> Self::Iter<'_> { - self.into_iter() - } - } - - impl<#(Event~I: Event,)*> Sequence for (#(Event~I,)*) { - type Ids = [Id; $c + 1]; - - fn ids() -> Self::Ids { - [#( - Event~I::id(), - )*] - } - } - }); - }; -} - -seq!(C in 0..=64 { - impl_sequence!(C); -}); diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs index 8b066a7..72a78a3 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -1,124 +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::Component; -use crate::uid::Uid; -use crate::event::{Event, Id}; -use crate::tuple::{ReduceElement as TupleReduceElement, With as TupleWith}; - -/// Event emitted when: -/// a) A entity with component `ComponentT` is spawned. -/// b) A component `ComponentT` is added to a entity. -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 } - } -} - -impl<ComponentT> Event for Added<ComponentT> -where - ComponentT: Component, -{ - fn id() -> Id - where - Self: Sized, - { - Id::new::<Added<ComponentForId>, _>(Some(ComponentT::id())) - } -} - -/// Event emitted when a `ComponentT` component is removed from a entity. -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 } - } -} - -impl<ComponentT> Event for Removed<ComponentT> -where - ComponentT: Component, -{ - fn id() -> Id - where - Self: Sized, - { - Id::new::<Removed<ComponentForId>, _>(Some(ComponentT::id())) - } -} - -#[must_use] -pub fn create_added_id(component_id: Uid) -> Id -{ - Id::new::<Added<ComponentForId>, _>(Some(component_id)) -} - -#[must_use] -pub fn create_removed_id(component_id: Uid) -> Id -{ - Id::new::<Removed<ComponentForId>, _>(Some(component_id)) -} - -pub struct TypeTransformComponentsToAddedEvents; - -impl<ComponentT: Component, Accumulator> - TupleReduceElement<Accumulator, TypeTransformComponentsToAddedEvents> for ComponentT -where - Accumulator: TupleWith<Added<Self>>, -{ - type Return = Accumulator::With; -} +use crate::Component; +/// 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)] -struct ComponentForId; +pub struct Added(Infallible); diff --git a/ecs/src/event/start.rs b/ecs/src/event/start.rs deleted file mode 100644 index 248dd50..0000000 --- a/ecs/src/event/start.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::event::Event; - -/// Start event. -#[derive(Debug, Clone, Copy)] -pub struct Start; - -impl Event for Start {} diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs index a945d89..42ebef9 100644 --- a/ecs/src/extension.rs +++ b/ecs/src/extension.rs @@ -1,9 +1,7 @@ use crate::component::Sequence as ComponentSequence; -use crate::event::component::TypeTransformComponentsToAddedEvents; -use crate::event::{Event, Sequence as EventSequence}; use crate::sole::Sole; use crate::system::System; -use crate::tuple::Reduce as TupleReduce; +use crate::uid::Uid; use crate::{SoleAlreadyExistsError, World}; /// A collection of systems, entities & soles that can be added to a [`World`]. @@ -27,21 +25,19 @@ impl<'world> Collector<'world> } /// Adds a system to the [`World`]. - pub fn add_system<'this, EventT, SystemImpl>( + pub fn add_system<'this, SystemImpl>( &'this mut self, - event: EventT, + phase_euid: Uid, system: impl System<'this, SystemImpl>, - ) where - EventT: Event, + ) { - self.world.register_system(event, system); + self.world.register_system(phase_euid, system); } /// Adds a entity to the [`World`]. pub fn add_entity<Comps>(&mut self, components: Comps) where - Comps: ComponentSequence + TupleReduce<TypeTransformComponentsToAddedEvents>, - Comps::Out: EventSequence, + Comps: ComponentSequence, { self.world.create_entity(components); } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 78e526f..07b1cba 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,65 +1,75 @@ #![deny(clippy::all, clippy::pedantic)] -use std::any::{type_name, TypeId}; +use std::any::{type_name, Any, TypeId}; use std::cell::RefCell; -use std::collections::HashMap; 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, Sequence as ComponentSequence}; -use crate::entity::CREATE_STATIC_ENTITIES; -use crate::event::component::{ - create_added_id as create_component_added_event_id, - create_removed_id as create_component_removed_event_id, - TypeTransformComponentsToAddedEvents, +use crate::component::{ + Component, + IntoParts, + Parts as ComponentParts, + Removals as ComponentRemovals, + Sequence as ComponentSequence, }; -use crate::event::start::Start as StartEvent; -use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; +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::query::options::Options as QueryOptions; +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::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, TypeErased as TypeErasedSystem}; -use crate::tuple::Reduce as TupleReduce; -use crate::type_name::TypeName; -use crate::uid::{Kind as UidKind, Uid}; +use crate::system::{System, SystemComponent}; +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 util; +mod lock; pub use ecs_macros::{Component, Sole}; pub use crate::query::Query; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct World { - systems: Vec<TypeErasedSystem>, data: WorldData, stop: AtomicBool, + is_first_tick: AtomicBool, } impl World @@ -67,21 +77,25 @@ impl World #[must_use] pub fn new() -> Self { - let mut world = Self::default(); + let mut world = Self { + data: WorldData::default(), + stop: AtomicBool::new(false), + is_first_tick: AtomicBool::new(false), + }; world.add_sole(Stats::default()).ok(); + for create_static_entity in CREATE_STATIC_ENTITIES { + 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 + TupleReduce<TypeTransformComponentsToAddedEvents>, - Comps::Out: EventSequence, + Comps: ComponentSequence, { let entity_uid = Uid::new_unique(UidKind::Entity); @@ -90,31 +104,27 @@ impl World entity_uid } - #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] + #[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 + TupleReduce<TypeTransformComponentsToAddedEvents>, - Comps::Out: EventSequence, + 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_vec()) - { - #[cfg(feature = "debug")] - 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; - }; + } - for component_added_event_id in <Comps::Out as EventSequence>::ids().iter() { - self.emit_event_by_id(*component_added_event_id); + let added_component_ids = Self::add_entity_components( + entity_uid, + components.into_parts_array(), + &mut self.data.component_storage, + ); + + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } @@ -129,28 +139,31 @@ impl World self.data.sole_storage.insert(sole) } - pub fn register_system<'this, EventT, SystemImpl>( + pub fn register_system<'this, SystemImpl>( &'this mut self, - event: EventT, + phase_euid: Uid, system: impl System<'this, SystemImpl>, - ) where - EventT: Event, + ) { - self.systems.push(system.into_type_erased()); - - self.data - .events - .entry(EventT::id()) - .or_default() - .push(self.systems.len() - 1); + self.create_entity(( + SystemComponent { system: system.into_type_erased() }, + Pair::new::<DependsOn>(phase_euid), + )); + } - drop(event); + pub fn register_observer_system<'this, SystemImpl>( + &'this mut self, + system: impl System<'this, SystemImpl>, + event: Pair<Uid, Uid>, + ) + { + 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); @@ -158,34 +171,177 @@ impl World extension.collect(extension_collector); } - /// Emits a event, running all systems listening to the event for each compatible - /// entity. - /// - /// # Panics - /// Will panic if a system has dissapeared. - pub fn emit<EventT>(&self, event: EventT) + pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> where - EventT: Event, + FieldTerms: QueryTermWithFieldTuple, + FieldlessTerms: QueryTermWithoutFieldTuple, { - self.emit_event_by_id(EventT::id()); - - drop(event); + Query::new(self) } - pub fn query<Comps, OptionsT>(&self) -> Query<Comps, OptionsT> - where - Comps: ComponentSequence, - OptionsT: QueryOptions, + pub fn flexible_query<const MAX_TERM_CNT: usize>( + &self, + terms: QueryTerms<MAX_TERM_CNT>, + ) -> FlexibleQuery<'_, MAX_TERM_CNT> { - Query::new(self) + FlexibleQuery::new(self, terms) } - /// Peforms the actions that have been queued up using [`Actions`]. - /// + /// Performs a single tick. /// # Panics - /// Will panic if a mutable internal lock cannot be acquired. - #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] - pub fn perform_queued_actions(&self) + /// Will panic if mutable internal lock cannot be acquired. + pub fn step(&mut self) -> StepResult + { + if self.stop.load(Ordering::Relaxed) { + return StepResult::Stop; + } + + if self + .is_first_tick + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + self.query_and_run_systems(*START_PHASE); + } + + 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; + } + + let mut stats_lock = self + .data + .sole_storage + .get::<Stats>() + .expect("No stats sole found") + .write_nonblock() + .expect("Failed to aquire read-write stats sole lock"); + + let stats = stats_lock + .downcast_mut::<Stats>() + .expect("Casting stats sole to Stats type failed"); + + stats.current_tick += 1; + + StepResult::Continue + } + + /// Starts a loop which calls [`Self::step`] until the world is stopped. + pub fn start_loop(&mut self) + { + while let StepResult::Continue = self.step() {} + } + + #[cfg(feature = "vizoxide")] + pub fn create_vizoxide_archetype_graph( + &self, + name: impl AsRef<str>, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> + { + use std::borrow::Cow; + + 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", + }, + ) + }, + }, + ) + } + + 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); + } + } + } + + fn perform_child_phases(&self, parent_phase_euid: Uid) + { + let phase_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + Phase::id(), + Pair::new::<ChildOf>(parent_phase_euid).id(), + ]) + .build(), + ); + + 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,), (Without<Pair<ChildOf, Wildcard>>,)>(); + + for (phase_entity_id, _) in phase_query.iter_with_euids() { + if phase_entity_id == *START_PHASE { + continue; + } + + self.query_and_run_systems(phase_entity_id); + self.perform_child_phases(phase_entity_id); + } + } + + #[tracing::instrument(skip_all)] + fn perform_queued_actions(&mut self) { let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() { @@ -202,82 +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) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - let component_ids = components - .iter() - .map(|component| component.self_id()) - .collect::<Vec<_>>(); + 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) { - #[cfg(feature = "debug")] - 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 component_id in component_ids { - self.emit_event_by_id(create_component_added_event_id( - component_id, - )); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } + Action::Despawn(entity_uid) => { + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, + entity_uid, + PendingRemoval::Entity, + ); + } Action::AddComponents(entity_uid, components) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - let component_ids = components - .iter() - .map(|component| component.self_id()) - .collect::<Vec<_>>(); - - component_storage_lock - .add_components_to_entity(entity_uid, components); - - drop(component_storage_lock); + 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 component_id in component_ids { - self.emit_event_by_id(create_component_added_event_id( - component_id, - )); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } - Action::RemoveComponents(entity_uid, components_metadata) => { - 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 component_metadata in components_metadata { - self.emit_event_by_id(create_component_removed_event_id( - component_metadata.id, - )); - } } Action::Stop => { self.stop.store(true, Ordering::Relaxed); @@ -286,60 +424,134 @@ impl World } } - /// A event loop which runs until a stop is issued with [`Flags::stop`]. Before the - /// loop begins, [`StartEvent`] is emitted. - /// - /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn event_loop<EventSeq: EventSequence>(&self) + fn perform_removals(&mut self, removals: Vec<(Uid, PendingRemoval)>) { - for create_static_entity in CREATE_STATIC_ENTITIES { - create_static_entity(self); + 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}"); + } + } + } } + } - self.emit(StartEvent); + #[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 Some(ent_handle) = Self::get_entity(component_storage, entity_uid) else { + tracing::warn!("Cannot schedule removal. Entity does not exist"); + return; + }; - let event_seq = EventSeq::ids(); + let component_ids = match removal { + PendingRemoval::Components(ref component_ids) => component_ids, + PendingRemoval::Entity => &ent_handle.component_ids().collect::<Vec<_>>(), + }; - loop { - for event_id in event_seq.iter() { - self.emit_event_by_id(*event_id); - } + let Some(mut component_removals) = ent_handle.get_mut::<ComponentRemovals>() + else { + Self::add_entity_components( + entity_uid, + [ComponentRemovals::from_iter(component_ids.iter().copied()) + .into_parts()], + component_storage, + ); + + pending_removals.push((entity_uid, removal)); + + return; + }; + + component_removals.add_ids(component_ids.iter().copied()); - self.perform_queued_actions(); + drop(component_removals); - if self.stop.load(Ordering::Relaxed) { - break; + 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; } - let mut stats_lock = self - .data - .sole_storage - .get::<Stats>() - .expect("No stats sole found") - .write_nonblock() - .expect("Failed to aquire read-write stats sole lock"); + added_component_ids.push(comp_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); - let stats = stats_lock - .downcast_mut::<Stats>() - .expect("Casting stats sole to Stats type failed"); + 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; + } - stats.current_tick += 1; + removed_component_ids.push(component_id); } + + removed_component_ids } - fn emit_event_by_id(&self, event_id: EventId) + fn emit_event_by_id<Event: Component>(&self, target: Uid) { - let Some(system_indices) = self.data.events.get(&event_id) else { + if target.kind() == UidKind::Pair { return; - }; + } - for system_index in system_indices { - let system = self.systems.get(*system_index).unwrap(); + let query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()]) + .build(), + ); - // SAFETY: The world lives long enough + for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { unsafe { - system.run(self); + system.system.run(self); } } } @@ -356,45 +568,95 @@ 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"), + )) } } -#[derive(Debug, Default)] -pub struct WorldData +impl Default for World { - events: HashMap<EventId, Vec<usize>>, - component_storage: Arc<Lock<ComponentStorage>>, - sole_storage: SoleStorage, - action_queue: Arc<ActionQueue>, + fn default() -> Self + { + Self::new() + } +} + +/// The result of calling [`World::step`]. +pub enum StepResult +{ + /// Another step can be made. + Continue, + + /// The world have been stopped so no step can be made again. + Stop, } #[derive(Debug)] -#[non_exhaustive] -pub struct EntityComponent +struct WorldData { - pub id: Uid, - pub name: &'static str, - pub component: Lock<Box<dyn Component>>, + component_storage: ComponentStorage, + sole_storage: SoleStorage, + action_queue: Rc<ActionQueue>, + pending_removals: Vec<(Uid, PendingRemoval)>, } -impl From<Box<dyn Component>> for EntityComponent +impl Default for WorldData { - fn from(component: Box<dyn Component>) -> Self + fn default() -> Self { Self { - id: component.self_id(), - name: component.type_name(), - component: Lock::new(component), + 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)] +pub struct EntityComponentRef<'a> +{ + component_id: Uid, + component: &'a ArchetypeEntityComponent, +} + +impl<'a> EntityComponentRef<'a> +{ + fn component(&self) -> &'a Lock<Box<dyn Any>> + { + 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 } + } +} + #[derive(Debug, Default, Clone, Copy)] enum ActiveActionQueue { @@ -403,7 +665,7 @@ enum ActiveActionQueue B, } -#[derive(Debug, Default)] +#[derive(Debug)] struct ActionQueue { queue_a: Lock<Vec<Action>>, @@ -430,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()), + } } } @@ -479,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, }), ); @@ -496,34 +762,16 @@ impl Drop for SoleStorage for sole in self.storage.values_mut() { if sole.drop_last { - #[cfg(feature = "debug")] - tracing::debug!( - "Sole {} pushed to dropping last queue", - sole.sole.read_nonblock().unwrap().type_name() - ); - soles_to_drop_last.push(sole); continue; } - #[cfg(feature = "debug")] - tracing::debug!( - "Dropping sole {}", - sole.sole.read_nonblock().unwrap().type_name() - ); - unsafe { ManuallyDrop::drop(sole); } } for sole in &mut soles_to_drop_last { - #[cfg(feature = "debug")] - tracing::debug!( - "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 fbc6842..0b36922 100644 --- a/ecs/src/lock.rs +++ b/ecs/src/lock.rs @@ -1,24 +1,29 @@ -use std::mem::transmute; +use std::mem::forget; use std::ops::{Deref, DerefMut}; -use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError}; -use crate::type_name::TypeName; +use parking_lot::{ + MappedRwLockReadGuard, + MappedRwLockWriteGuard, + RwLock, + RwLockReadGuard, + RwLockWriteGuard, +}; -#[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. @@ -27,15 +32,14 @@ where /// Returns `Err` if unavailable (A mutable handle is hold). pub fn read_nonblock(&self) -> Result<ReadGuard<Value>, Error> { - let guard = self.inner.try_read().or_else(|err| match err { - TryLockError::WouldBlock => Err(Error::Unavailable), - TryLockError::Poisoned(poison_err) => Ok(poison_err.into_inner()), - })?; + let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?; - #[cfg(feature = "debug")] - 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. @@ -44,62 +48,59 @@ where /// Returns `Err` if unavailable (A mutable or immutable handle is hold). pub fn write_nonblock(&self) -> Result<WriteGuard<Value>, Error> { - let guard = self.inner.try_write().or_else(|err| match err { - TryLockError::WouldBlock => Err(Error::Unavailable), - TryLockError::Poisoned(poison_err) => Ok(poison_err.into_inner()), - })?; + let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?; - #[cfg(feature = "debug")] 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() - .unwrap_or_else(PoisonError::into_inner) + Ok(WriteGuard { + inner: guard, + value_type_name: self.value_type_name, + }) } } #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Lock is unavailable")] - Unavailable, + #[error("Lock is unavailable for reading")] + ReadUnavailable, + + #[error("Lock is unavailable for writing")] + WriteUnavailable, } #[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, { - /// 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> + pub fn map<NewValue>( + self, + func: impl FnOnce(&Value) -> &NewValue, + ) -> MappedReadGuard<'guard, NewValue> { - unsafe { transmute(self) } + 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) }; + forget(self); + + MappedReadGuard { + inner: RwLockReadGuard::map(inner, func), + value_type_name, + } } } -impl<'guard, Value> Deref for ReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Deref for ReadGuard<'_, Value> { type Target = Value; @@ -109,28 +110,71 @@ where } } -impl<'guard, Value> Drop for ReadGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for ReadGuard<'_, Value> { fn drop(&mut self) { - #[cfg(feature = "debug")] - 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> +{ + inner: MappedRwLockReadGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<Value> Deref for MappedReadGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> Drop for MappedReadGuard<'_, Value> +{ + fn drop(&mut self) + { + 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> +{ + pub fn map<NewValue>( + self, + func: impl FnOnce(&mut Value) -> &mut NewValue, + ) -> MappedWriteGuard<'guard, NewValue> + { + 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) }; + forget(self); + + 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; @@ -140,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 { @@ -150,13 +192,49 @@ where } } -impl<'guard, Value> Drop for WriteGuard<'guard, Value> -where - Value: TypeName, +impl<Value> Drop for WriteGuard<'_, Value> { fn drop(&mut self) { - #[cfg(feature = "debug")] - 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> +{ + inner: MappedRwLockWriteGuard<'guard, Value>, + value_type_name: &'static str, +} + +impl<Value> Deref for MappedWriteGuard<'_, Value> +{ + type Target = Value; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<Value> DerefMut for MappedWriteGuard<'_, Value> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.inner + } +} + +impl<Value> Drop for MappedWriteGuard<'_, Value> +{ + fn drop(&mut self) + { + tracing::trace!( + "Dropped mapped mutable lock to value of type {}", + 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 new file mode 100644 index 0000000..9f47fb8 --- /dev/null +++ b/ecs/src/phase.rs @@ -0,0 +1,13 @@ +use ecs_macros::Component; + +use crate::pair::{ChildOf, Pair}; +use crate::static_entity; + +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct Phase; + +static_entity!(pub START, (Phase,)); + +static_entity!(pub PRE_UPDATE, (Phase,)); + +static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE))); diff --git a/ecs/src/query.rs b/ecs/src/query.rs index f3318bd..ccb7add 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -1,115 +1,91 @@ -use std::iter::{Filter, Flatten, Map}; +use std::any::type_name; use std::marker::PhantomData; -use crate::component::storage::{ - Archetype, - ArchetypeEntity, - ArchetypeRefIter, - EntityIter, - Storage as ComponentStorage, -}; +use seq_macro::seq; + use crate::component::{ Component, - Metadata as ComponentMetadata, - Sequence as ComponentSequence, -}; -use crate::lock::{ReadGuard, WriteGuard}; -use crate::query::options::Options; -use crate::system::{ - NoInitParamFlag as NoInitSystemParamFlag, - Param as SystemParam, - System, + Handle as ComponentHandle, + HandleMut as ComponentHandleMut, }; -use crate::uid::Uid; -use crate::{EntityComponent, World}; +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::{Kind as UidKind, Uid, With as WithUid}; +use crate::util::array_vec::ArrayVec; +use crate::util::Array; +use crate::World; -pub mod options; +pub mod flexible; +pub mod term; #[derive(Debug)] -pub struct Query<'world, Comps, OptionsT = ()> +pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where - Comps: ComponentSequence, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { world: &'world World, - component_storage: ReadGuard<'world, ComponentStorage>, - _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: ComponentSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { - /// Iterates over the entities matching this query. + /// Iterates over the entities matching this query, the iterator item being the entity + /// components. #[must_use] - pub fn iter_mut( - &'world self, - ) -> ComponentIterMut<'world, Comps, QueryEntityIter<'world>> + pub fn iter<'query>( + &'query self, + ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - #[cfg(feature = "debug")] - tracing::debug!("Searching for {}", std::any::type_name::<Comps>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - #[allow(clippy::map_flatten)] - ComponentIterMut { + Iter { world: self.world, - entities: self - .component_storage - .find_entities(Comps::metadata()) - .map(Archetype::entities as ComponentIterMapFn) - .flatten() - .filter(|entity| OptionsT::entity_filter(entity.components())), + iter: self.inner.iter(), comps_pd: PhantomData, } } - /// Iterates over the entities matching this query. + /// Iterates over the entities matching this query, the iterator item being the entity + /// [`Uid`] and the matching entity components. #[must_use] - pub fn iter(&'world self) -> ComponentIter<'world, Comps, QueryEntityIter<'world>> + pub fn iter_with_euids<'query>( + &'query self, + ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>> { - #[cfg(feature = "debug")] - tracing::debug!("Searching for {}", std::any::type_name::<Comps>()); + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); - #[allow(clippy::map_flatten)] - ComponentIter { + ComponentAndEuidIter { world: self.world, - entities: self - .component_storage - .find_entities(Comps::metadata()) - .map(Archetype::entities as ComponentIterMapFn) - .flatten() - .filter(|entity| OptionsT::entity_filter(entity.components())), + iter: self.inner.iter(), comps_pd: PhantomData, } } - /// Iterates over the entities matching this query and has the provided extra - /// component. + /// Iterates over the entities matching this query using the iterator returned by + /// `func`. + /// + /// This function exists so that a custom [`EntityHandle`] iterator can be given to + /// [`Iter`] without giving the user access to a reference to the [`World`]. #[must_use] - pub fn iter_with_extra_comps( - &'world self, - extra_components: impl IntoIterator<Item = ComponentMetadata>, - ) -> ComponentIter<'world, Comps, QueryEntityIter<'world>> + pub fn iter_with<'query, OutIter>( + &'query self, + func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter, + ) -> Iter<'query, 'world, FieldTerms, OutIter> + where + OutIter: Iterator<Item = EntityHandle<'query>>, { - #[cfg(feature = "debug")] - tracing::debug!( - "Searching for {} + extra components", - std::any::type_name::<Comps>() - ); - - #[allow(clippy::map_flatten)] - ComponentIter { + tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); + + Iter { world: self.world, - entities: self - .component_storage - .find_entities( - Comps::metadata() - .into_iter() - .chain(extra_components) - .collect::<Vec<_>>(), - ) - .map(Archetype::entities as ComponentIterMapFn) - .flatten() - .filter(|entity| OptionsT::entity_filter(entity.components())), + iter: func(self.inner.iter()), comps_pd: PhantomData, } } @@ -118,51 +94,45 @@ where #[must_use] pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid> { - Some( - self.component_storage - .find_entities(Comps::metadata()) - .flat_map(|archetype| archetype.entities()) - .filter(|entity| OptionsT::entity_filter(entity.components())) - .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, - component_storage: world - .data - .component_storage - .read_nonblock() - .expect("Failed to acquire read-only component storage lock"), + inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } } } -impl<'world, Comps, OptionsT> IntoIterator for &'world Query<'world, Comps, OptionsT> +impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator + for &'query Query<'world, FieldTerms, FieldlessTerms> where - Comps: ComponentSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { - type IntoIter = ComponentIterMut<'world, Comps, QueryEntityIter<'world>>; - type Item = Comps::MutRefs<'world>; + type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>; + type Item = FieldTerms::Fields<'query>; fn into_iter(self) -> Self::IntoIter { - self.iter_mut() + self.iter() } } -unsafe impl<'world, Comps, OptionsT> SystemParam<'world> - for Query<'world, Comps, OptionsT> +impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world> + for Query<'world, FieldTerms, FieldlessTerms> where - Comps: ComponentSequence, - OptionsT: Options, + FieldTerms: TermWithFieldTuple, + FieldlessTerms: TermWithoutFieldTuple, { - type Flags = NoInitSystemParamFlag; type Input = (); fn initialize<SystemImpl>( @@ -181,93 +151,429 @@ where } } -type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> EntityIter<'a>; +#[derive(Debug)] +pub struct Terms<const MAX_TERM_CNT: usize> +{ + required_components: ArrayVec<Uid, MAX_TERM_CNT>, + excluded_components: ArrayVec<Uid, MAX_TERM_CNT>, +} -type ComponentIterFilterFn = for<'a, 'b> fn(&'a &'b ArchetypeEntity) -> bool; +impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT> +{ + pub fn builder() -> TermsBuilder<MAX_TERM_CNT> + { + TermsBuilder::default() + } +} -type QueryEntityIter<'world> = Filter< - Flatten<Map<ArchetypeRefIter<'world>, ComponentIterMapFn>>, - ComponentIterFilterFn, ->; +#[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>, +} -pub struct ComponentIterMut<'world, Comps, EntityIter> -where - EntityIter: Iterator<Item = &'world ArchetypeEntity>, +#[allow(clippy::return_self_not_must_use)] +pub trait TermsBuilderInterface { - world: &'world World, - entities: EntityIter, - comps_pd: PhantomData<Comps>, + 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; } -impl<'world, Comps, EntityIter> Iterator for ComponentIterMut<'world, Comps, EntityIter> -where - Comps: ComponentSequence + 'world, - EntityIter: Iterator<Item = &'world ArchetypeEntity>, +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> { - type Item = Comps::MutRefs<'world>; + #[must_use] + pub fn build(self) -> Terms<MAX_TERM_CNT> + { + debug_assert!(self.required_components.is_sorted()); + debug_assert!(self.excluded_components.is_sorted()); - fn next(&mut self) -> Option<Self::Item> + 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>, + ) { - Some(Comps::from_components_mut( - self.entities.next()?.components().iter(), - self.world, - lock_component_rw, - )) + 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>() + ); + }) } } -fn lock_component_rw( - entity_component: &EntityComponent, -) -> WriteGuard<'_, Box<dyn Component>> +impl<ComponentT: Component> TermWithField for &mut ComponentT { - entity_component - .component - .write_nonblock() - .unwrap_or_else(|_| { + 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!( - "Failed to acquire read-write lock to component {}", - entity_component.name + "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<FieldTerms>, } -pub struct ComponentIter<'world, Comps, EntityIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> + Iter<'query, 'world, FieldTerms, EntityHandleIter> where - EntityIter: Iterator<Item = &'world ArchetypeEntity>, + FieldTerms: TermWithFieldTuple, + EntityHandleIter: Iterator<Item = EntityHandle<'query>>, + 'world: 'query, +{ + /// 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, FieldTerms, EntityHandleIter> Iterator + for Iter<'query, 'world, FieldTerms, EntityHandleIter> +where + FieldTerms: TermWithFieldTuple, + EntityHandleIter: Iterator<Item = EntityHandle<'query>>, + 'world: 'query, +{ + type Item = FieldTerms::Fields<'query>; + + fn next(&mut self) -> Option<Self::Item> + { + let entity_handle = self.iter.next()?; + + Some(FieldTerms::get_fields(&entity_handle, self.world)) + } +} + +pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> +where + FieldTerms: TermWithFieldTuple, + EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, - entities: EntityIter, - comps_pd: PhantomData<Comps>, + iter: EntityHandleIter, + comps_pd: PhantomData<FieldTerms>, } -impl<'world, Comps, EntityIter> Iterator for ComponentIter<'world, Comps, EntityIter> +impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator + for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter> where - Comps: ComponentSequence + 'world, - EntityIter: Iterator<Item = &'world ArchetypeEntity>, + FieldTerms: TermWithFieldTuple, + EntityHandleIter: Iterator<Item = EntityHandle<'query>>, + 'world: 'query, { - type Item = Comps::Refs<'world>; + type Item = (Uid, FieldTerms::Fields<'query>); fn next(&mut self) -> Option<Self::Item> { - Some(Comps::from_components( - self.entities.next()?.components().iter(), - self.world, - lock_component_ro, + let entity_handle = self.iter.next()?; + + Some(( + entity_handle.uid(), + FieldTerms::get_fields(&entity_handle, self.world), )) } } -fn lock_component_ro( - entity_component: &EntityComponent, -) -> ReadGuard<'_, Box<dyn Component>> +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 () { - entity_component - .component - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to acquire read-write lock to component {}", - entity_component.name - ); - }) + 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 new file mode 100644 index 0000000..add30b0 --- /dev/null +++ b/ecs/src/query/flexible.rs @@ -0,0 +1,84 @@ +//! Low-level querying. +use std::iter::{repeat_n, FlatMap, RepeatN, Zip}; + +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, const MAX_TERM_CNT: usize> +{ + world: &'world World, + terms: Terms<MAX_TERM_CNT>, +} + +impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> +{ + /// Iterates over the entities matching this query. + #[must_use] + pub fn iter(&self) -> Iter<'_> + { + Iter { + iter: self + .world + .data + .component_storage + .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, + ), + } + } + + pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self + { + Self { world, terms } + } +} + +impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT> +{ + type IntoIter = Iter<'query>; + type Item = EntityHandle<'query>; + + fn into_iter(self) -> Self::IntoIter + { + self.iter() + } +} + +pub struct Iter<'query> +{ + iter: QueryEntityIter<'query>, +} + +impl<'query> Iterator for Iter<'query> +{ + type Item = EntityHandle<'query>; + + fn next(&mut self) -> Option<Self::Item> + { + let (archetype, entity) = self.iter.next()?; + + Some(EntityHandle::new(archetype, entity)) + } +} + +type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>; + +type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>; + +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 bbbe0a8..0000000 --- a/ecs/src/query/options.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::collections::HashSet; -use std::marker::PhantomData; - -use crate::component::Component; -use crate::EntityComponent; - -/// Query options. -pub trait Options -{ - fn entity_filter<'component>( - components: impl IntoIterator<Item = &'component EntityComponent>, - ) -> bool; -} - -impl Options for () -{ - fn entity_filter<'component>( - _: impl IntoIterator<Item = &'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: impl IntoIterator<Item = &'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: impl IntoIterator<Item = &'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 0ebd9c2..0000000 --- a/ecs/src/relationship.rs +++ /dev/null @@ -1,418 +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, - FromOptional as FromOptionalComponent, - FromOptionalMut as FromOptionalMutComponent, -}; -use crate::lock::ReadGuard; -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> 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> 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(), - } - } - - /// 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) - } -} diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs index 084a06b..1cce419 100644 --- a/ecs/src/sole.rs +++ b/ecs/src/sole.rs @@ -5,12 +5,11 @@ use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Weak}; use crate::lock::{Lock, WriteGuard}; -use crate::system::{NoInitParamFlag, Param as SystemParam, System}; -use crate::type_name::TypeName; +use crate::system::{Param as SystemParam, System}; 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> @@ -88,11 +79,10 @@ where } } -unsafe impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT> +impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT> where SoleT: Sole, { - type Flags = NoInitParamFlag; type Input = (); fn initialize<SystemImpl>( @@ -115,7 +105,7 @@ where } } -impl<'world, SoleT> Deref for Single<'world, SoleT> +impl<SoleT> Deref for Single<'_, SoleT> where SoleT: Sole, { @@ -127,7 +117,7 @@ where } } -impl<'world, SoleT> DerefMut for Single<'world, SoleT> +impl<SoleT> DerefMut for Single<'_, SoleT> where SoleT: Sole, { @@ -174,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 046d25b..603c015 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,19 +1,12 @@ -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 std::panic::{RefUnwindSafe, UnwindSafe}; +use ecs_macros::Component; use seq_macro::seq; -use crate::component::{ - Component, - FromOptional as FromOptionalComponent, - FromOptionalMut as FromOptionalMutComponent, -}; -use crate::lock::{ReadGuard, WriteGuard}; -use crate::tuple::{ReduceElement as TupleReduceElement, With as TupleWith}; +use crate::component::{Component, HandleMut as ComponentHandleMut}; +use crate::tuple::{ReduceElement as TupleReduceElement, Tuple}; use crate::World; pub mod stateful; @@ -33,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, @@ -47,8 +40,8 @@ macro_rules! impl_system { impl<'world, Func, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*)> for Func where - Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, - #(TParam~I: Param<'world, Flags = NoInitParamFlag>,)* + Func: Fn(#(TParam~I,)*) + Copy + 'static, + #(TParam~I: Param<'world, Input = ()>,)* { type Input = Infallible; @@ -92,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"); } @@ -121,7 +114,7 @@ pub trait Into<Impl> pub struct TypeErased { - data: Box<dyn Any + RefUnwindSafe + UnwindSafe>, + data: Box<dyn Any>, run: Box<TypeErasedRunFn>, } @@ -149,13 +142,12 @@ impl Debug for TypeErased } /// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&dyn Any, &World) + RefUnwindSafe + UnwindSafe; +type TypeErasedRunFn = dyn Fn(&dyn Any, &World); /// A parameter to a [`System`]. -pub unsafe trait Param<'world> +pub trait Param<'world> { type Input; - type Flags; fn initialize<SystemImpl>( system: &mut impl System<'world, SystemImpl>, @@ -168,8 +160,6 @@ pub unsafe trait Param<'world> ) -> Self; } -pub struct NoInitParamFlag {} - /// A type which can be used as input to a [`System`]. pub trait Input: 'static {} @@ -179,9 +169,9 @@ pub struct ParamWithInputFilter; impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for InputT where - Accumulator: TupleWith<Self>, + Accumulator: Tuple, { - type Return = Accumulator::With; + type Return = Accumulator::WithElementAtEnd<Self>; } impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () @@ -189,128 +179,8 @@ impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () type Return = Accumulator; } -#[derive(Debug)] -pub struct ComponentRefMut<'a, ComponentT: Component> -{ - inner: WriteGuard<'a, Box<dyn Component>>, - _ph: PhantomData<ComponentT>, -} - -impl<'a, ComponentT: Component> ComponentRefMut<'a, ComponentT> -{ - pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Component>>) -> Self - { - Self { inner, _ph: PhantomData } - } -} - -impl<'component, ComponentT: Component> FromOptionalMutComponent<'component> - for ComponentRefMut<'component, ComponentT> +#[derive(Debug, Component)] +pub(crate) struct SystemComponent { - fn from_optional_mut_component( - inner: Option<WriteGuard<'component, Box<dyn Component>>>, - _world: &'component World, - ) -> Self - { - Self { - inner: inner.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - }), - _ph: PhantomData, - } - } -} - -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<'a, ComponentT: Component> Deref for ComponentRefMut<'a, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - self.inner.downcast_ref().unwrap() - } -} - -impl<'a, ComponentT: Component> DerefMut for ComponentRefMut<'a, ComponentT> -{ - fn deref_mut(&mut self) -> &mut Self::Target - { - self.inner.downcast_mut().unwrap() - } -} - -#[derive(Debug)] -pub struct ComponentRef<'a, ComponentT: Component> -{ - inner: ReadGuard<'a, Box<dyn Component>>, - _ph: PhantomData<ComponentT>, -} - -impl<'a, ComponentT: Component> ComponentRef<'a, ComponentT> -{ - pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Component>>) -> Self - { - Self { inner, _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 { - inner: inner.unwrap_or_else(|| { - panic!( - "Component {} was not found in entity", - type_name::<ComponentT>() - ); - }), - _ph: PhantomData, - } - } -} - -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<'a, ComponentT: Component> Deref for ComponentRef<'a, ComponentT> -{ - type Target = ComponentT; - - fn deref(&self) -> &Self::Target - { - self.inner.downcast_ref().unwrap() - } + pub(crate) system: TypeErased, } diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 536e6ed..54f6979 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,13 +1,12 @@ use std::any::{type_name, Any, TypeId}; -use std::collections::HashMap; 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, @@ -15,10 +14,9 @@ use crate::system::{ TypeErased, }; use crate::tuple::{ - IntoInOptions as TupleIntoInOptions, Reduce as TupleReduce, - TakeOptionElementResult as TupleTakeOptionElementResult, - WithOptionElements as TupleWithOptionElements, + Tuple, + WithAllElemLtStatic as TupleWithAllElemLtStatic, }; use crate::uid::Uid; use crate::World; @@ -27,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 { @@ -39,9 +37,10 @@ macro_rules! impl_system { Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, #(TParam~I: Param<'world>,)* #(TParam~I::Input: 'static,)* - (#(TParam~I::Input,)*): TupleReduce<ParamWithInputFilter>, - <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out: - TupleIntoInOptions + (#(TParam~I::Input,)*): TupleReduce< + ParamWithInputFilter, + Out: Tuple<InOptions: TupleWithAllElemLtStatic> + >, { type Input = <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out; @@ -50,35 +49,27 @@ macro_rules! impl_system { { let mut option_input = input.into_in_options(); + let mut index = 0; + #( if TypeId::of::<TParam~I::Input>() != TypeId::of::<()>() { - let input = match option_input.take::<TParam~I::Input>() { - TupleTakeOptionElementResult::Found(input) => input, - TupleTakeOptionElementResult::NotFound => { - panic!( - "Parameter input {} not found", - type_name::<TParam~I::Input>() - ); - } - TupleTakeOptionElementResult::AlreadyTaken => { - panic!( - concat!( - "Parameter {} is already initialized. ", - "System cannot contain multiple inputs with ", - "the same type", - ), - type_name::<TParam~I>() - ); - - } - }; + let input = option_input + .get_mut::<Option<TParam~I::Input>>(index) + .expect("Input element index out of range") + .take() + .expect("Input element is already taken"); TParam~I::initialize( &mut self, input ); + + #[allow(unused_assignments)] + { + index += 1; + } } )* @@ -118,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>( @@ -136,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/tuple.rs b/ecs/src/tuple.rs index 1434592..def25a0 100644 --- a/ecs/src/tuple.rs +++ b/ecs/src/tuple.rs @@ -1,36 +1,44 @@ use std::any::TypeId; -use std::mem::{transmute_copy, ManuallyDrop}; use paste::paste; use seq_macro::seq; use util_macros::sub; -/// Used to append a element to a tuple type. -pub trait With<OtherElem> +pub trait Tuple: sealed::Sealed { - type With; -} - -/// Used to remove the last element of a tuple type. -/// -/// For example: `(u32, String, PathBuf)` becomes `(u32, String)`. -pub trait WithoutLast -{ - type Return; -} - -/// Used to make all elements of a tuple type wrapped in [`Option`]. -pub trait IntoInOptions -{ - type InOptions: WithOptionElements; - + /// `Self` with the given type added as the last element. + /// + /// `(String, i32, u8)::WithElementAtEnd<Path> = (String, i32, u8, Path)` + /// + /// # Important note + /// If `Self` has 16 elements, this will be `()`. The reason for this is that the + /// `Tuple` trait is only implemented for tuples with up to and including 16 elements. + type WithElementAtEnd<NewElem>: Tuple; + + /// `Self` without the last element. + /// + /// `(u16, AtomicU8)::WithoutLastElement = (u16,)` + type WithoutLastElement: Tuple; + + /// The last element of `Self`. + type LastElement; + + /// Self with all elements wrapped in [`Option`]. + type InOptions: Tuple; + + /// Pops the last element from this tuple, returning the new tuple and the popped + /// element. + fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement); + + /// Converts this tuple so that all elements are wrapped in [`Option`]. fn into_in_options(self) -> Self::InOptions; } -/// A tuple with all elements wrapped in [`Option`]. -pub trait WithOptionElements +/// A tuple with element types that all have the lifetime `'static`. +pub trait WithAllElemLtStatic: Tuple + sealed::Sealed { - fn take<Element: 'static>(&mut self) -> TakeOptionElementResult<Element>; + /// Returns the element at the given index. + fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>; } /// Using the type system, reduces the elements of a tuple to a single one. Each element @@ -45,22 +53,6 @@ pub trait ReduceElement<Accumulator, Operation> type Return; } -/// The result of trying to [`take`] a element from a implementation of -/// [`WithOptionElements`]. -/// -/// [`take`]: WithOptionElements::take -pub enum TakeOptionElementResult<Element> -{ - /// The element was succesfully taken. - Found(Element), - - /// The element is not a element of the tuple. - NotFound, - - /// The element has already been taken - AlreadyTaken, -} - macro_rules! tuple_reduce_elem_tuple { (overflow) => { () @@ -95,13 +87,6 @@ macro_rules! elem_by_index { }; } -pub trait Pop: WithoutLast -{ - type LastElem; - - fn pop(self) -> (<Self as WithoutLast>::Return, Self::LastElem); -} - macro_rules! all_except_last { (start $($rest: tt)*) => { all_except_last!(@[] $($rest)*) @@ -131,19 +116,23 @@ macro_rules! all_except_last { macro_rules! impl_tuple_traits { ($cnt: tt) => { seq!(I in 0..$cnt { - impl<OtherElem, #(Elem~I,)*> With<OtherElem> for (#(Elem~I,)*) + impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*) { - type With = (#(Elem~I,)* OtherElem,); - } + type WithElementAtEnd<NewElem> = (#(Elem~I,)* NewElem,); - impl<#(Elem~I,)*> WithoutLast for (#(Elem~I,)*) - { - type Return = all_except_last!(start #(Elem~I)*); - } + type WithoutLastElement = all_except_last!(start #(Elem~I)*); - impl<#(Element~I: 'static,)*> IntoInOptions for (#(Element~I,)*) - { - type InOptions = (#(Option<Element~I>,)*); + type LastElement = sub!($cnt - 1, elem_type_by_index); + + type InOptions = (#(Option<Elem~I>,)*); + + fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement) + { + ( + all_except_last!(start #((self.I))*), + sub!($cnt - 1, elem_by_index, (self)) + ) + } fn into_in_options(self) -> Self::InOptions { @@ -152,32 +141,28 @@ macro_rules! impl_tuple_traits { } } - impl<#(Element~I: 'static,)*> WithOptionElements for (#(Option<Element~I>,)*) + impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*) { - fn take<Element: 'static>(&mut self) - -> TakeOptionElementResult<Element> + fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element> { - #( - if TypeId::of::<Element~I>() == TypeId::of::<Element>() { - let input = match self.I.take() { - Some(input) => ManuallyDrop::new(input), - None => { - return TakeOptionElementResult::AlreadyTaken; - } - }; - - return TakeOptionElementResult::Found( - // SAFETY: It can be transmuted safely since it is the - // same type and the type is 'static - unsafe { transmute_copy(&input) } - ); - } - )* - - TakeOptionElementResult::NotFound + match index { + #( + I => { + assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>()); + + // SAFETY: It is checked above that the type is correct + Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() }) + } + )* + _ => None + } } } + impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) + { + } + paste! { impl<Operation, #(Elem~I,)*> Reduce<Operation> for (#(Elem~I,)*) where @@ -190,19 +175,6 @@ macro_rules! impl_tuple_traits { type Out = sub!($cnt - 1, tuple_reduce_elem_tuple); } } - - impl<#(Elem~I,)*> Pop for (#(Elem~I,)*) - { - type LastElem = sub!($cnt - 1, elem_type_by_index); - - fn pop(self) -> (<Self as WithoutLast>::Return, Self::LastElem) - { - ( - all_except_last!(start #((self.I))*), - sub!($cnt - 1, elem_by_index, (self)) - ) - } - } }); }; } @@ -210,3 +182,57 @@ macro_rules! impl_tuple_traits { seq!(N in 0..16 { impl_tuple_traits!(N); }); + +seq!(I in 0..16 { + impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*) + { + type WithElementAtEnd<NewElem> = (); + + type WithoutLastElement = all_except_last!(start #(Elem~I)*); + + type LastElement = Elem15; + + type InOptions = (#(Option<Elem~I>,)*); + + fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement) + { + ( + all_except_last!(start #((self.I))*), + self.15 + ) + } + + fn into_in_options(self) -> Self::InOptions + { + #![allow(clippy::unused_unit)] + (#(Some(self.I),)*) + } + } + + impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*) + { + fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element> + { + match index { + #( + I => { + assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>()); + + // SAFETY: It is checked above that the type is correct + Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() }) + } + )* + _ => None + } + } + } + + impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) + { + } +}); + +mod sealed +{ + pub trait Sealed {} +} 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 0e5d88a..feed62c 100644 --- a/ecs/src/uid.rs +++ b/ecs/src/uid.rs @@ -1,21 +1,29 @@ +use std::fmt::{Debug, Display, Formatter}; use std::mem::transmute; use std::sync::atomic::{AtomicU32, Ordering}; -static NEXT: AtomicU32 = AtomicU32::new(1); +use crate::component::Component; +use crate::util::{gen_mask_64, BitMask, NumberExt}; -// Bit 0 and 1 for the kind -const KIND_BITS: u64 = 0x03; +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. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +/// A unique identifier. +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Uid { inner: u64, @@ -23,21 +31,213 @@ 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_part = NEXT.fetch_add(1, Ordering::Relaxed); + let id = NEXT.fetch_add(1, Ordering::Relaxed); + + Self { + inner: ID_BITS.field_prep(u64::from(id)) | KIND_BITS.field_prep(kind as u64), + } + } + #[must_use] + pub fn wildcard() -> Self + { Self { - inner: (u64::from(id_part) << 32) | kind as u64, + 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 + { + 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 & 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 + } +} + +impl Debug for Uid +{ + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result + { + formatter + .debug_struct("Uid") + .field("id", &self.id()) + .field("kind", &self.kind()) + .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 4480fc8..9ab4dc6 100644 --- a/ecs/src/util.rs +++ b/ecs/src/util.rs @@ -1,3 +1,186 @@ +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]> + + AsMut<[Item]> + + IntoIterator<Item = Item> + + Into<Vec<Item>> + + Sortable<Item = Item> + + sealed::Sealed +{ +} + +impl<Item, const CNT: usize> Array<Item> for [Item; CNT] {} + +impl<Item, const CNT: usize> sealed::Sealed for [Item; CNT] {} + pub trait Sortable { type Item; @@ -46,3 +229,105 @@ impl<Item> Sortable for Vec<Item> self.sort_by_key(func); } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BitMask<Value> +{ + mask: Value, +} + +impl BitMask<u64> +{ + #[must_use] + pub const fn new(mask: u64) -> Self + { + Self { mask } + } + + #[must_use] + pub const fn value(self) -> u64 + { + self.mask + } + + /// Prepares a bitfield value in the range of bits specified by this `BitMask`. + #[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) + } +} + +impl BitAnd<u64> for BitMask<u64> +{ + type Output = u64; + + fn bitand(self, rhs: u64) -> Self::Output + { + self.mask & rhs + } +} + +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; +} + +impl NumberExt for u64 +{ + fn field_get(self, field_mask: BitMask<Self>) -> Self + { + (field_mask & self) >> field_mask.value().trailing_zeros() + } +} + +macro_rules! gen_mask_64 { + ($low: literal..=$high: literal) => { + const { + if $high <= $low { + panic!("High bit index cannot be less than or equal to low bit index"); + } + + (((!0u64) - (1u64 << ($low)) + 1) + & (!0u64 >> (u64::BITS as u64 - 1 - ($high)))) + } + }; +} + +pub(crate) use gen_mask_64; + +mod sealed +{ + pub trait Sealed {} +} + +#[cfg(test)] +mod tests +{ + + use super::BitMask; + use crate::util::NumberExt; + + #[test] + fn field_get_works() + { + assert_eq!(0b11011u64.field_get(BitMask::new(0b11100)), 0b00110); + } + + #[test] + fn bitmask_field_prep_works() + { + 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(); + } + } + } +} |