From 43cbd47900d23801c584def1b7877fdea700c23a Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 7 Jun 2025 22:52:36 +0200 Subject: feat(ecs): make component removals queryable This replaces the old component removed events --- ecs/src/lib.rs | 140 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 35 deletions(-) (limited to 'ecs/src/lib.rs') 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::( - 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::(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::>(), + }; + + let Some(mut component_removals) = ent_handle.get_mut::() + 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, @@ -521,6 +567,21 @@ impl World *has_swapped_active_queue = true; } + + fn get_entity( + component_storage: &mut ComponentStorage, + entity_uid: Uid, + ) -> Option> + { + 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, + pending_removals: Vec<(Uid, PendingRemoval)>, } impl Default for WorldData @@ -557,10 +619,18 @@ 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), + Entity, +} + #[derive(Debug)] pub struct EntityComponentRef<'a> { -- cgit v1.2.3-18-g5258