diff options
| -rw-r--r-- | ecs/src/actions.rs | 4 | ||||
| -rw-r--r-- | ecs/src/component.rs | 37 | ||||
| -rw-r--r-- | ecs/src/component/storage/archetype.rs | 1 | ||||
| -rw-r--r-- | ecs/src/entity.rs | 5 | ||||
| -rw-r--r-- | ecs/src/event/component.rs | 6 | ||||
| -rw-r--r-- | ecs/src/lib.rs | 140 | 
6 files changed, 150 insertions, 43 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 6fb1230..5a8cd0b 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -2,6 +2,8 @@ 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::{ @@ -311,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 4496a2b..bab3d61 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -96,6 +96,11 @@ impl<'a> Handle<'a>          }      } +    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 } 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,  | 
