diff options
Diffstat (limited to 'ecs')
-rw-r--r-- | ecs/src/actions.rs | 4 | ||||
-rw-r--r-- | ecs/src/component.rs | 170 | ||||
-rw-r--r-- | ecs/src/component/storage/archetype.rs | 1 | ||||
-rw-r--r-- | ecs/src/entity.rs | 35 | ||||
-rw-r--r-- | ecs/src/event/component.rs | 6 | ||||
-rw-r--r-- | ecs/src/lib.rs | 140 | ||||
-rw-r--r-- | ecs/src/pair.rs | 2 | ||||
-rw-r--r-- | ecs/src/query.rs | 49 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 4 | ||||
-rw-r--r-- | ecs/src/query/term.rs | 23 | ||||
-rw-r--r-- | ecs/src/util/array_vec.rs | 14 |
11 files changed, 257 insertions, 191 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index e0efeda..3dd8755 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -23,7 +23,7 @@ impl<'world> Actions<'world> .push(Action::Spawn(components.into_parts_array().into())); } - /// Queues up despawning a entity at the end of the current tick. + /// Queues up despawning a entity at the end of the **next** tick. pub fn despawn(&mut self, entity_uid: Uid) { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); @@ -48,7 +48,7 @@ impl<'world> Actions<'world> )); } - /// Queues up removing component(s) from a entity at the end of the current tick. + /// Queues up removing component(s) from a entity at the end of the **next** tick. pub fn remove_components( &mut self, entity_uid: Uid, diff --git a/ecs/src/component.rs b/ecs/src/component.rs index a0ed752..5a8cd0b 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,8 +1,9 @@ use std::any::{type_name, Any}; -use std::error::Error; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; +use ecs_macros::Component; +use hashbrown::HashSet; use seq_macro::seq; use crate::lock::{ @@ -15,7 +16,7 @@ use crate::lock::{ use crate::system::Input as SystemInput; use crate::uid::Uid; use crate::util::Array; -use crate::{EntityComponentRef, World}; +use crate::EntityComponentRef; pub mod local; @@ -23,14 +24,6 @@ pub(crate) mod storage; pub trait Component: SystemInput + Any { - type HandleMut<'component>: HandleFromEntityComponentRef<'component> - where - Self: Sized; - - type Handle<'component>: HandleFromEntityComponentRef<'component> - where - Self: Sized; - /// Returns the ID of this component. fn id() -> Uid where @@ -77,46 +70,31 @@ pub trait Sequence fn into_parts_array(self) -> Self::PartsArray; } -/// [`Component`] metadata. -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Metadata -{ - pub id: Uid, -} - -impl Metadata +#[derive(Debug)] +pub struct Handle<'a, ComponentData: 'static> { - #[must_use] - pub fn of<ComponentT: Component>() -> Self - { - Self { id: ComponentT::id() } - } + inner: MappedReadGuard<'a, ComponentData>, } -pub trait HandleFromEntityComponentRef<'comp>: Sized +impl<'comp, ComponentData: 'static> Handle<'comp, ComponentData> { - type Error: Error; - /// Creates a new handle instance from a [`EntityComponentRef`]. /// /// # Errors - /// See the implementation's [`Self::Error`] type. - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - world: &'comp World, - ) -> Result<Self, Self::Error>; -} - -#[derive(Debug)] -pub struct Handle<'a, ComponentData: 'static> -{ - inner: MappedReadGuard<'a, ComponentData>, -} + /// Will return `Err` if acquiring the component's lock fails. + pub fn from_entity_component_ref( + entity_component_ref: EntityComponentRef<'comp>, + ) -> Result<Self, HandleError> + { + Ok(Self::new( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + )) + } -impl<'a, ComponentData: 'static> Handle<'a, ComponentData> -{ - pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Any>>) -> Self + pub(crate) fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Self { Self { inner: inner.map(|component| { @@ -133,28 +111,6 @@ impl<'a, ComponentData: 'static> Handle<'a, ComponentData> } } -impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp> - for Handle<'comp, ComponentData> -{ - type Error = HandleError; - - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - _world: &'comp World, - ) -> Result<Self, Self::Error> - { - let entity_comp = - entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?; - - Ok(Self::new( - entity_comp - .component() - .read_nonblock() - .map_err(AcquireComponentLockFailed)?, - )) - } -} - impl<ComponentData: 'static> Deref for Handle<'_, ComponentData> { type Target = ComponentData; @@ -171,9 +127,25 @@ pub struct HandleMut<'a, ComponentData: 'static> inner: MappedWriteGuard<'a, ComponentData>, } -impl<'a, ComponentData: 'static> HandleMut<'a, ComponentData> +impl<'comp, ComponentData: 'static> HandleMut<'comp, ComponentData> { - pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Any>>) -> Self + /// 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> + { + Ok(Self::new( + entity_component_ref + .component() + .write_nonblock() + .map_err(AcquireLockError)?, + )) + } + + pub(crate) fn new(inner: WriteGuard<'comp, Box<dyn Any>>) -> Self { Self { inner: inner.map(|component| { @@ -190,28 +162,6 @@ impl<'a, ComponentData: 'static> HandleMut<'a, ComponentData> } } -impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp> - for HandleMut<'comp, ComponentData> -{ - type Error = HandleError; - - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - _world: &'comp World, - ) -> Result<Self, Self::Error> - { - let entity_comp = - entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?; - - Ok(Self::new( - entity_comp - .component() - .write_nonblock() - .map_err(AcquireComponentLockFailed)?, - )) - } -} - impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData> { type Target = ComponentData; @@ -234,15 +184,12 @@ impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData> pub enum HandleError { #[error(transparent)] - AcquireComponentLockFailed(#[from] AcquireComponentLockFailed), - - #[error("Component does not exist")] - ComponentDoesNotExist, + AcquireLockFailed(#[from] AcquireLockError), } #[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct AcquireComponentLockFailed(LockError); +#[error("Failed to acquire component lock")] +pub struct AcquireLockError(#[source] LockError); macro_rules! inner { ($c: tt) => { @@ -366,3 +313,38 @@ impl Default for PartsBuilder Self { name: "(unspecified)" } } } + +/// Pending component removals for a entity. +#[derive(Debug, Clone, Component)] +pub struct Removals +{ + component_ids: HashSet<Uid>, +} + +impl Removals +{ + pub fn contains<ComponentT: Component>(&self) -> bool + { + self.contains_id(ComponentT::id()) + } + + pub fn contains_id(&self, component_id: Uid) -> bool + { + self.component_ids.contains(&component_id) + } + + pub(crate) fn add_ids(&mut self, ids: impl IntoIterator<Item = Uid>) + { + self.component_ids.extend(ids) + } +} + +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/storage/archetype.rs b/ecs/src/component/storage/archetype.rs index a88e0e8..bb29701 100644 --- a/ecs/src/component/storage/archetype.rs +++ b/ecs/src/component/storage/archetype.rs @@ -332,6 +332,7 @@ impl EntityComponent } } + #[allow(dead_code)] pub fn id(&self) -> Uid { self.id diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 562f7ea..bab3d61 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -10,7 +10,6 @@ use crate::component::storage::archetype::{ use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::uid::{Kind as UidKind, Uid}; @@ -20,7 +19,6 @@ use crate::{EntityComponentRef, World}; #[derive(Debug)] pub struct Handle<'a> { - world: &'a World, archetype: &'a Archetype, entity: &'a ArchetypeEntity, } @@ -50,13 +48,12 @@ impl<'a> Handle<'a> let component = self.get_matching_components(ComponentT::id()).next()?; Some( - ComponentHandle::from_entity_component_ref(Some(component), self.world) - .unwrap_or_else(|err| { - panic!( - "Taking component {} lock failed: {err}", - type_name::<ComponentT>() - ); - }), + ComponentHandle::from_entity_component_ref(component).unwrap_or_else(|err| { + panic!( + "Taking component {} lock failed: {err}", + type_name::<ComponentT>() + ); + }), ) } @@ -77,13 +74,14 @@ impl<'a> Handle<'a> let component = self.get_matching_components(ComponentT::id()).next()?; Some( - ComponentHandleMut::from_entity_component_ref(Some(component), self.world) - .unwrap_or_else(|err| { + ComponentHandleMut::from_entity_component_ref(component).unwrap_or_else( + |err| { panic!( "Taking component {} lock failed: {err}", type_name::<ComponentT>() ); - }), + }, + ), ) } @@ -98,13 +96,14 @@ impl<'a> Handle<'a> } } - pub(crate) fn new( - world: &'a World, - archetype: &'a Archetype, - entity: &'a ArchetypeEntity, - ) -> Self + 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 { world, archetype, entity } + Self { archetype, entity } } } diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs index ef09480..72a78a3 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -10,9 +10,3 @@ use crate::Component; /// b) The target component is added to a entity. #[derive(Debug, Component)] pub struct Added(Infallible); - -/// Pair relation for events emitted when: -/// a) The target component is removed from a entity. -/// b) A entity with the target component is despawned. -#[derive(Debug, Component)] -pub struct Removed(Infallible); diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 53abc6b..07b1cba 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -15,14 +15,13 @@ use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComp use crate::component::storage::Storage as ComponentStorage; use crate::component::{ Component, + IntoParts, Parts as ComponentParts, + Removals as ComponentRemovals, Sequence as ComponentSequence, }; -use crate::entity::CREATE_STATIC_ENTITIES; -use crate::event::component::{ - Added as ComponentAddedEvent, - Removed as ComponentRemovedEvent, -}; +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; use crate::pair::{ChildOf, DependsOn, Pair}; @@ -209,8 +208,12 @@ impl World 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; } @@ -355,6 +358,8 @@ 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) => { @@ -382,24 +387,12 @@ impl World } } Action::Despawn(entity_uid) => { - let removed_entity = - match self.data.component_storage.remove_entity(entity_uid) { - Ok(components) => components, - Err(err) => { - tracing::error!("Failed to despawn entity: {err}"); - return; - } - }; - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for removed_ent_comp in removed_entity.components() { - self.emit_event_by_id::<ComponentRemovedEvent>( - removed_ent_comp.id(), - ); - } + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, + entity_uid, + PendingRemoval::Entity, + ); } Action::AddComponents(entity_uid, components) => { let added_component_ids = Self::add_entity_components( @@ -417,19 +410,12 @@ impl World } } Action::RemoveComponents(entity_uid, component_ids) => { - let removed_component_ids = Self::remove_entity_components( - entity_uid, - component_ids, + Self::schedule_removal( &mut self.data.component_storage, + &mut self.data.pending_removals, + entity_uid, + PendingRemoval::Components(component_ids), ); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_id in removed_component_ids { - self.emit_event_by_id::<ComponentRemovedEvent>(comp_id); - } } Action::Stop => { self.stop.store(true, Ordering::Relaxed); @@ -438,6 +424,66 @@ impl World } } + fn perform_removals(&mut self, removals: Vec<(Uid, PendingRemoval)>) + { + for (entity_id, removal) in removals { + match removal { + PendingRemoval::Components(component_ids) => { + Self::remove_entity_components( + entity_id, + component_ids.into_iter().chain([ComponentRemovals::id()]), + &mut self.data.component_storage, + ); + } + PendingRemoval::Entity => { + if let Err(err) = self.data.component_storage.remove_entity(entity_id) + { + tracing::error!("Failed to remove entity {entity_id}: {err}"); + } + } + } + } + } + + #[tracing::instrument(skip(component_storage, pending_removals))] + fn schedule_removal( + component_storage: &mut ComponentStorage, + pending_removals: &mut Vec<(Uid, PendingRemoval)>, + entity_uid: Uid, + removal: PendingRemoval, + ) + { + let Some(ent_handle) = Self::get_entity(component_storage, entity_uid) else { + tracing::warn!("Cannot schedule removal. Entity does not exist"); + return; + }; + + let component_ids = match removal { + PendingRemoval::Components(ref component_ids) => component_ids, + PendingRemoval::Entity => &ent_handle.component_ids().collect::<Vec<_>>(), + }; + + let Some(mut component_removals) = ent_handle.get_mut::<ComponentRemovals>() + else { + 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()); + + drop(component_removals); + + pending_removals.push((entity_uid, removal)); + } + fn add_entity_components( entity_uid: Uid, components: impl IntoIterator<Item = ComponentParts>, @@ -521,6 +567,21 @@ impl World *has_swapped_active_queue = true; } + + fn get_entity( + component_storage: &mut ComponentStorage, + entity_uid: Uid, + ) -> Option<EntityHandle<'_>> + { + let archetype = component_storage.get_entity_archetype(entity_uid)?; + + Some(EntityHandle::new( + archetype, + archetype + .get_entity_by_id(entity_uid) + .expect("Not possible"), + )) + } } impl Default for World @@ -542,11 +603,12 @@ pub enum StepResult } #[derive(Debug)] -pub struct WorldData +struct WorldData { component_storage: ComponentStorage, sole_storage: SoleStorage, action_queue: Rc<ActionQueue>, + pending_removals: Vec<(Uid, PendingRemoval)>, } impl Default for WorldData @@ -557,11 +619,19 @@ impl Default for WorldData 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, diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs index 2055d5e..4ff4995 100644 --- a/ecs/src/pair.rs +++ b/ecs/src/pair.rs @@ -140,7 +140,7 @@ impl Handle<'_> unreachable!(); }; - Some(EntityHandle::new(self.world, archetype, archetype_entity)) + Some(EntityHandle::new(archetype, archetype_entity)) } } diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 7e10c5b..ccb7add 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -6,7 +6,6 @@ use seq_macro::seq; use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::entity::Handle as EntityHandle; @@ -342,18 +341,26 @@ impl<ComponentT: Component> TermWithField for &ComponentT fn get_field<'world>( entity_handle: &EntityHandle<'world>, - world: &'world World, + _world: &'world World, ) -> Self::Field<'world> { assert_eq!(ComponentT::id().kind(), UidKind::Component); - Self::Field::from_entity_component_ref( - entity_handle - .get_matching_components(ComponentT::id()) - .next(), - world, - ) - .unwrap_or_else(|err| { + 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>() @@ -375,18 +382,26 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT fn get_field<'world>( entity_handle: &EntityHandle<'world>, - world: &'world World, + _world: &'world World, ) -> Self::Field<'world> { assert_eq!(ComponentT::id().kind(), UidKind::Component); - Self::Field::from_entity_component_ref( - entity_handle - .get_matching_components(ComponentT::id()) - .next(), - world, - ) - .unwrap_or_else(|err| { + 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>() diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index 6d65ee0..add30b0 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -22,7 +22,6 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> pub fn iter(&self) -> Iter<'_> { Iter { - world: self.world, iter: self .world .data @@ -59,7 +58,6 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T pub struct Iter<'query> { - world: &'query World, iter: QueryEntityIter<'query>, } @@ -71,7 +69,7 @@ impl<'query> Iterator for Iter<'query> { let (archetype, entity) = self.iter.next()?; - Some(EntityHandle::new(self.world, archetype, entity)) + Some(EntityHandle::new(archetype, entity)) } } diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs index 2e1ecca..9c772da 100644 --- a/ecs/src/query/term.rs +++ b/ecs/src/query/term.rs @@ -4,7 +4,6 @@ use std::marker::PhantomData; use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::query::{ @@ -65,17 +64,14 @@ impl<ComponentT: Component> TermWithField for Option<&ComponentT> fn get_field<'world>( entity_handle: &crate::entity::Handle<'world>, - world: &'world crate::World, + _world: &'world crate::World, ) -> Self::Field<'world> { Some( ComponentHandle::<'world, ComponentT>::from_entity_component_ref( - Some( - entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ), - world, + entity_handle + .get_matching_components(ComponentT::id()) + .next()?, ) .unwrap_or_else(|err| { panic!( @@ -99,17 +95,14 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT> fn get_field<'world>( entity_handle: &crate::entity::Handle<'world>, - world: &'world crate::World, + _world: &'world crate::World, ) -> Self::Field<'world> { Some( ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref( - Some( - entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ), - world, + entity_handle + .get_matching_components(ComponentT::id()) + .next()?, ) .unwrap_or_else(|err| { panic!( diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs index 13a0349..a37b1f9 100644 --- a/ecs/src/util/array_vec.rs +++ b/ecs/src/util/array_vec.rs @@ -115,3 +115,17 @@ impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY> } } } + +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(); + } + } + } +} |