diff options
Diffstat (limited to 'ecs/src/lib.rs')
-rw-r--r-- | ecs/src/lib.rs | 575 |
1 files changed, 340 insertions, 235 deletions
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 1b9a31b..07b1cba 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,59 +1,64 @@ #![deny(clippy::all, clippy::pedantic)] -use std::any::{type_name, TypeId}; +use std::any::{type_name, Any, TypeId}; use std::cell::RefCell; use std::fmt::Debug; use std::mem::ManuallyDrop; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use hashbrown::HashMap; use crate::actions::Action; +use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; use crate::component::storage::Storage as ComponentStorage; use crate::component::{ Component, - IsOptional as ComponentIsOptional, - Metadata as ComponentMetadata, - RefSequence as ComponentRefSequence, + IntoParts, + Parts as ComponentParts, + Removals as ComponentRemovals, Sequence as ComponentSequence, }; -use crate::entity::CREATE_STATIC_ENTITIES; -use crate::event::component::Kind as ComponentEventKind; +use crate::entity::{Handle as EntityHandle, CREATE_STATIC_ENTITIES}; +use crate::event::component::Added as ComponentAddedEvent; use crate::extension::{Collector as ExtensionCollector, Extension}; -use crate::lock::{Lock, WriteGuard}; +use crate::lock::Lock; +use crate::pair::{ChildOf, DependsOn, Pair}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; -use crate::query::options::{Not, Options as QueryOptions, With}; -use crate::relationship::{ChildOf, DependsOn, Relationship}; +use crate::query::term::Without; +use crate::query::{ + Iter as QueryIter, + TermWithFieldTuple as QueryTermWithFieldTuple, + TermWithoutFieldTuple as QueryTermWithoutFieldTuple, + Terms as QueryTerms, + TermsBuilderInterface, +}; use crate::sole::Sole; use crate::stats::Stats; use crate::system::{System, SystemComponent}; -use crate::type_name::TypeName; -use crate::uid::{Kind as UidKind, Uid}; -use crate::util::Sortable; +use crate::uid::{Kind as UidKind, Uid, Wildcard}; pub mod actions; pub mod component; pub mod entity; pub mod event; pub mod extension; -pub mod lock; +pub mod pair; pub mod phase; pub mod query; -pub mod relationship; pub mod sole; pub mod stats; pub mod system; pub mod tuple; -pub mod type_name; pub mod uid; pub mod util; #[doc(hidden)] pub mod private; -mod archetype; +mod lock; pub use ecs_macros::{Component, Sole}; @@ -81,16 +86,13 @@ impl World world.add_sole(Stats::default()).ok(); for create_static_entity in CREATE_STATIC_ENTITIES { - create_static_entity(&world); + create_static_entity(&mut world); } world } /// Creates a new entity with the given components. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid where Comps: ComponentSequence, @@ -102,30 +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, { 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; - }; + } + + let added_component_ids = Self::add_entity_components( + entity_uid, + components.into_parts_array(), + &mut self.data.component_storage, + ); - for added_event_id in Comps::added_event_ids() { - self.emit_event_by_id(added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } @@ -148,27 +147,23 @@ impl World { self.create_entity(( SystemComponent { system: system.into_type_erased() }, - Relationship::<DependsOn, Phase>::new(phase_euid), + Pair::new::<DependsOn>(phase_euid), )); } - pub fn register_observer_system<'this, SystemImpl, Event>( + pub fn register_observer_system<'this, SystemImpl>( &'this mut self, system: impl System<'this, SystemImpl>, - event: Event, - ) where - Event: Component, + event: Pair<Uid, Uid>, + ) { - self.create_entity::<(SystemComponent, Event)>(( + self.create_entity(( SystemComponent { system: system.into_type_erased() }, event, )); } /// Adds a extensions. - /// - /// # Panics - /// Will panic if mutable internal lock cannot be acquired. pub fn add_extension(&mut self, extension: impl Extension) { let extension_collector = ExtensionCollector::new(self); @@ -176,29 +171,26 @@ impl World extension.collect(extension_collector); } - pub fn query<Comps, OptionsT>(&self) -> Query<Comps, OptionsT> + pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> where - Comps: ComponentRefSequence, - OptionsT: QueryOptions, + FieldTerms: QueryTermWithFieldTuple, + FieldlessTerms: QueryTermWithoutFieldTuple, { Query::new(self) } - pub fn flexible_query<CompMetadata>( + pub fn flexible_query<const MAX_TERM_CNT: usize>( &self, - comp_metadata: CompMetadata, - ) -> FlexibleQuery<CompMetadata> - where - CompMetadata: Sortable<Item = ComponentMetadata> + AsRef<[ComponentMetadata]>, + terms: QueryTerms<MAX_TERM_CNT>, + ) -> FlexibleQuery<'_, MAX_TERM_CNT> { - FlexibleQuery::new(self, comp_metadata) + FlexibleQuery::new(self, terms) } /// Performs a single tick. - /// /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn step(&self) -> StepResult + /// Will panic if mutable internal lock cannot be acquired. + pub fn step(&mut self) -> StepResult { if self.stop.load(Ordering::Relaxed) { return StepResult::Stop; @@ -214,8 +206,14 @@ impl World self.perform_phases(); + self.data.component_storage.create_imaginary_archetypes(); + + let prev_pending_removals = std::mem::take(&mut self.data.pending_removals); + self.perform_queued_actions(); + self.perform_removals(prev_pending_removals); + if self.stop.load(Ordering::Relaxed) { return StepResult::Stop; } @@ -238,26 +236,72 @@ impl World } /// Starts a loop which calls [`Self::step`] until the world is stopped. - /// - /// # Panics - /// Will panic if a internal lock cannot be acquired. - pub fn start_loop(&self) + pub fn start_loop(&mut self) { while let StepResult::Continue = self.step() {} } - fn query_and_run_systems(&self, phase_euid: Uid) + #[cfg(feature = "vizoxide")] + pub fn create_vizoxide_archetype_graph( + &self, + name: impl AsRef<str>, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> { - let system_comps_query = - self.query::<(&SystemComponent, &Relationship<DependsOn, Phase>), ()>(); + use std::borrow::Cow; - let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| { - phase_rel - .target_uids() - .any(|target_uid| target_uid == phase_euid) - }); + use crate::component::storage::{ + VizoxideArchetypeGraphEdgeKind, + VizoxideArchetypeGraphParams, + }; - for (system_component, _) in system_iter { + 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); @@ -267,48 +311,37 @@ impl World fn perform_child_phases(&self, parent_phase_euid: Uid) { - let phase_query = self.query::<(&Phase, &Relationship<ChildOf, Phase>), ()>(); - - for (index, (_, phase_rel)) in phase_query.iter().enumerate() { - if !phase_rel - .target_uids() - .any(|phase_euid| phase_euid == parent_phase_euid) - { - continue; - } - - let phase_euid = phase_query - .get_entity_uid(index) - .expect("Cannot get current query iteration entity UID"); - - self.query_and_run_systems(phase_euid); + let phase_query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([ + Phase::id(), + Pair::new::<ChildOf>(parent_phase_euid).id(), + ]) + .build(), + ); - self.perform_child_phases(phase_euid); + for child_phase_entity in &phase_query { + self.query_and_run_systems(child_phase_entity.uid()); + self.perform_child_phases(child_phase_entity.uid()); } } fn perform_phases(&self) { - let phase_query = - self.query::<(&Phase,), Not<With<Relationship<ChildOf, Phase>>>>(); - - for (index, (_,)) in phase_query.iter().enumerate() { - let child_phase_euid = phase_query - .get_entity_uid(index) - .expect("Cannot get current query iteration entity UID"); + let phase_query = self.query::<(&Phase,), (Without<Pair<ChildOf, Wildcard>>,)>(); - if child_phase_euid == *START_PHASE { + for (phase_entity_id, _) in phase_query.iter_with_euids() { + if phase_entity_id == *START_PHASE { continue; } - self.query_and_run_systems(child_phase_euid); - - self.perform_child_phases(child_phase_euid); + self.query_and_run_systems(phase_entity_id); + self.perform_child_phases(phase_entity_id); } } - #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] - fn perform_queued_actions(&self) + #[tracing::instrument(skip_all)] + fn perform_queued_actions(&mut self) { let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() { @@ -325,77 +358,64 @@ impl World let mut has_swapped_active_queue = false; + // TODO: Figure out a good way to handle situations where there are multiple + // AddComponents/RemoveComponents actions that affect the same entity. for action in active_action_queue.drain(..) { match action { - Action::Spawn(components, component_added_event_ids) => { - let mut component_storage_lock = self.lock_component_storage_rw(); + Action::Spawn(components) => { + let new_entity_uid = Uid::new_unique(UidKind::Entity); - #[allow(unused_variables)] - if let Err(err) = component_storage_lock - .push_entity(Uid::new_unique(UidKind::Entity), components) + if let Err(err) = + self.data.component_storage.create_entity(new_entity_uid) { - #[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 comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } Action::Despawn(entity_uid) => { - self.despawn_entity(entity_uid, &mut has_swapped_active_queue); + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, + entity_uid, + PendingRemoval::Entity, + ); } - Action::AddComponents( - entity_uid, - components, - component_added_event_ids, - ) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - component_storage_lock - .add_components_to_entity(entity_uid, components); - - drop(component_storage_lock); + Action::AddComponents(entity_uid, components) => { + let added_component_ids = Self::add_entity_components( + entity_uid, + components, + &mut self.data.component_storage, + ); if !has_swapped_active_queue { self.swap_event_queue(&mut has_swapped_active_queue); } - for comp_added_event_id in component_added_event_ids.ids { - self.emit_event_by_id(comp_added_event_id); + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); } } - Action::RemoveComponents( - entity_uid, - components_metadata, - component_removed_event_ids, - ) => { - let mut component_storage_lock = self.lock_component_storage_rw(); - - component_storage_lock.remove_components_from_entity( + Action::RemoveComponents(entity_uid, component_ids) => { + Self::schedule_removal( + &mut self.data.component_storage, + &mut self.data.pending_removals, entity_uid, - components_metadata - .iter() - .map(|component_metadata| component_metadata.id), + PendingRemoval::Components(component_ids), ); - - drop(component_storage_lock); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_removed_event_id in component_removed_event_ids.ids { - self.emit_event_by_id(comp_removed_event_id); - } } Action::Stop => { self.stop.store(true, Ordering::Relaxed); @@ -404,66 +424,132 @@ impl World } } - fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool) + fn perform_removals(&mut self, removals: Vec<(Uid, PendingRemoval)>) { - let mut component_storage_lock = self.lock_component_storage_rw(); + 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}"); + } + } + } + } + } - let Some(archetype) = component_storage_lock.get_entity_archetype(entity_uid) + #[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 { - #[cfg(feature = "debug")] - tracing::error!("No archetype for entity {entity_uid:?} was found"); + Self::add_entity_components( + entity_uid, + [ComponentRemovals::from_iter(component_ids.iter().copied()) + .into_parts()], + component_storage, + ); + + pending_removals.push((entity_uid, removal)); return; }; - let entity = archetype - .get_entity(entity_uid) - .expect("Entity archetype was found but the entity is not in the archetype"); - - let component_removed_event_uids = entity - .components() - .iter() - .map(|component| { - component - .component - .read_nonblock() - .unwrap_or_else(|_| { - panic!( - "Failed to acquire read-only {} component lock", - component.name - ) - }) - .get_event_uid(ComponentEventKind::Removed) - }) - .collect::<Vec<_>>(); - - component_storage_lock.remove_entity(entity_uid); - - drop(component_storage_lock); - - if !*has_swapped_active_queue { - self.swap_event_queue(has_swapped_active_queue); + component_removals.add_ids(component_ids.iter().copied()); + + drop(component_removals); + + pending_removals.push((entity_uid, removal)); + } + + fn add_entity_components( + entity_uid: Uid, + components: impl IntoIterator<Item = ComponentParts>, + component_storage: &mut ComponentStorage, + ) -> Vec<Uid> + { + let component_iter = components.into_iter(); + + let mut added_component_ids = + Vec::<Uid>::with_capacity(component_iter.size_hint().0); + + for component_parts in component_iter { + let comp_id = component_parts.id(); + + if let Err(err) = component_storage.add_entity_component( + entity_uid, + (comp_id, component_parts.name(), component_parts.into_data()), + ) { + tracing::error!("Failed to add component to entity: {err}"); + continue; + } + + added_component_ids.push(comp_id); } - for comp_removed_event_id in component_removed_event_uids { - self.emit_event_by_id(comp_removed_event_id); + added_component_ids + } + + fn remove_entity_components( + entity_uid: Uid, + component_ids: impl IntoIterator<Item = Uid>, + component_storage: &mut ComponentStorage, + ) -> Vec<Uid> + { + let component_id_iter = component_ids.into_iter(); + + let mut removed_component_ids = + Vec::<Uid>::with_capacity(component_id_iter.size_hint().0); + + for component_id in component_id_iter { + if let Err(err) = + component_storage.remove_entity_component(entity_uid, component_id) + { + tracing::error!("Failed to remove component to entity: {err}"); + continue; + } + + removed_component_ids.push(component_id); } + + removed_component_ids } - fn emit_event_by_id(&self, event_id: Uid) + fn emit_event_by_id<Event: Component>(&self, target: Uid) { - let query = self.flexible_query([ - ComponentMetadata::of::<SystemComponent>(), - ComponentMetadata { - id: event_id, - is_optional: ComponentIsOptional::No, - }, - ]); + if target.kind() == UidKind::Pair { + return; + } - for (system,) in query - .iter::<()>() - .into_component_iter::<(&SystemComponent,)>(self) - { + let query = self.flexible_query( + QueryTerms::<2>::builder() + .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()]) + .build(), + ); + + for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { unsafe { system.system.run(self); } @@ -482,12 +568,19 @@ impl World *has_swapped_active_queue = true; } - fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage> + fn get_entity( + component_storage: &mut ComponentStorage, + entity_uid: Uid, + ) -> Option<EntityHandle<'_>> { - self.data - .component_storage - .write_nonblock() - .expect("Failed to acquire read-write component storage lock") + let archetype = component_storage.get_entity_archetype(entity_uid)?; + + Some(EntityHandle::new( + archetype, + archetype + .get_entity_by_id(entity_uid) + .expect("Not possible"), + )) } } @@ -509,32 +602,58 @@ pub enum StepResult Stop, } -#[derive(Debug, Default)] -pub struct WorldData +#[derive(Debug)] +struct WorldData { - component_storage: Arc<Lock<ComponentStorage>>, + component_storage: ComponentStorage, sole_storage: SoleStorage, - action_queue: Arc<ActionQueue>, + action_queue: Rc<ActionQueue>, + pending_removals: Vec<(Uid, PendingRemoval)>, +} + +impl Default for WorldData +{ + fn default() -> Self + { + Self { + component_storage: ComponentStorage::default(), + sole_storage: SoleStorage::default(), + action_queue: Rc::new(ActionQueue::default()), + pending_removals: Vec::new(), + } + } } #[derive(Debug)] -#[non_exhaustive] -pub struct EntityComponent +enum PendingRemoval { - pub id: Uid, - pub name: &'static str, - pub component: Lock<Box<dyn Component>>, + Components(Vec<Uid>), + Entity, } -impl From<Box<dyn Component>> for EntityComponent +#[derive(Debug)] +pub struct EntityComponentRef<'a> { - fn from(component: Box<dyn Component>) -> Self + component_id: Uid, + component: &'a ArchetypeEntityComponent, +} + +impl<'a> EntityComponentRef<'a> +{ + fn component(&self) -> &'a Lock<Box<dyn Any>> { - Self { - id: component.self_id(), - name: component.type_name(), - component: Lock::new(component), - } + self.component.component() + } + + #[must_use] + pub fn id(&self) -> Uid + { + self.component_id + } + + fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self + { + Self { component_id, component: comp } } } @@ -546,7 +665,7 @@ enum ActiveActionQueue B, } -#[derive(Debug, Default)] +#[derive(Debug)] struct ActionQueue { queue_a: Lock<Vec<Action>>, @@ -573,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()), + } } } @@ -622,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, }), ); @@ -639,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); } |