diff options
| author | HampusM <hampus@hampusmat.com> | 2026-05-21 17:55:20 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-05-21 17:55:20 +0200 |
| commit | 8022e8998290b067b8aa0cb9cba8ba410826bdab (patch) | |
| tree | 7171e79ce530e03079046ee8fd12167160c45480 /ecs/src/lib.rs | |
| parent | 412cee02c252f91bcf0b70a3f5cc5fca6d2b4c62 (diff) | |
chore: rename ecs* crates to engine-ecs*
Diffstat (limited to 'ecs/src/lib.rs')
| -rw-r--r-- | ecs/src/lib.rs | 773 |
1 files changed, 0 insertions, 773 deletions
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs deleted file mode 100644 index 667aa0e..0000000 --- a/ecs/src/lib.rs +++ /dev/null @@ -1,773 +0,0 @@ -#![deny(clippy::all, clippy::pedantic)] - -use std::any::{Any, TypeId, type_name}; -use std::fmt::Debug; -use std::hint::cold_path; -use std::mem::ManuallyDrop; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; - -use hashbrown::HashMap; - -use crate::actions::Action; -use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent; -use crate::component::storage::{EntityAlreadyExistsError, Storage as ComponentStorage}; -use crate::component::{ - Component, - IntoParts as IntoComponentParts, - Parts as ComponentParts, - Sequence as ComponentSequence, -}; -use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle}; -use crate::error::{ - ErrorHandler, - Metadata as ErrorMetadata, - SourceKind as ErrorSourceKind, - err_handler_panic, -}; -use crate::event::component::Added; -use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter}; -use crate::extension::{Collector as ExtensionCollector, Extension}; -use crate::lock::Lock; -use crate::pair::{ChildOf, Pair, Wildcard}; -use crate::phase::{ - HasSystem as PhaseHasSystem, - POST_UPDATE as POST_UPDATE_PHASE, - PRE_UPDATE as PRE_UPDATE_PHASE, - Phase, - START as START_PHASE, - UPDATE as UPDATE_PHASE, -}; -use crate::query::flexible::Query as FlexibleQuery; -use crate::query::{ - MAX_TERM_CNT as QUERY_MAX_TERM_CNT, - TermWithFieldTuple as QueryTermWithFieldTuple, - TermWithoutFieldTuple as QueryTermWithoutFieldTuple, - Terms as QueryTerms, - TermsBuilderInterface, -}; -use crate::sole::{Single, Sole}; -use crate::stats::Stats; -use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent}; -use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent}; -use crate::uid::{Kind as UidKind, Uid}; - -pub mod actions; -pub mod component; -pub mod entity; -pub mod error; -pub mod event; -pub mod extension; -pub mod pair; -pub mod phase; -pub mod query; -pub mod sole; -pub mod stats; -pub mod system; -pub mod tuple; -pub mod uid; -pub mod util; - -mod lock; - -pub use ecs_macros::{Component, Sole}; - -pub use crate::query::Query; - -#[derive(Debug)] -pub struct World -{ - data: WorldData, - stop: AtomicBool, - is_first_tick: AtomicBool, - error_handler: ErrorHandler, -} - -impl World -{ - #[must_use] - pub fn new() -> Self - { - let mut world = Self { - data: WorldData::default(), - stop: AtomicBool::new(false), - is_first_tick: AtomicBool::new(false), - error_handler: err_handler_panic, - }; - - crate::phase::spawn_entities(&mut world); - - world.add_sole(Stats::default()).ok(); - - world - } - - pub fn set_err_handler(&mut self, err_handler: ErrorHandler) - { - self.error_handler = err_handler; - } - - /// Creates a entity with the given components. A new unique [`Uid`] will be generated - /// for this entity. - pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid - where - Comps: ComponentSequence, - { - let entity_uid = Uid::new_unique(UidKind::Entity); - - self.create_entity_with_uid(entity_uid, components); - - entity_uid - } - - /// Creates a entity with the given components. The entity will have the specified - /// [`Uid`]. - #[tracing::instrument(skip_all)] - pub fn create_entity_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps) - where - Comps: ComponentSequence, - { - self.create_ent(entity_uid, components.into_parts_array()); - } - - pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts) - { - Self::add_entity_components( - entity_id, - [component_parts], - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - - pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration) - { - entity_decl.create(self); - } - - /// Adds a globally shared singleton value. - /// - /// # Errors - /// Returns `Err` if this [`Sole`] has already been added. - pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - where - SoleT: Sole, - { - self.data.sole_storage.insert(sole) - } - - pub fn register_observer<'this, SystemImpl, ObserverT>( - &'this mut self, - observer: ObserverT, - ) where - ObserverT: Observer<'this, SystemImpl>, - { - let (wrapper_comp, mut system_callbacks) = observer.finish_observer(); - - let ent_id = Uid::new_unique(UidKind::Entity); - - self.create_ent( - ent_id, - [wrapper_comp.into_parts()].into_iter().chain( - ObserverT::observed_events() - .into_iter() - .map(IntoComponentParts::into_parts), - ), - ); - - system_callbacks.on_created(self, SystemMetadata { ent_id }); - } - - pub fn register_system<'this, SystemImpl>( - &'this mut self, - phase_euid: Uid, - system: impl System<'this, SystemImpl>, - ) - { - let (type_erased_system, mut system_callbacks) = system.finish(); - - let system_ent_id = - self.create_entity((SystemComponent { system: type_erased_system },)); - - system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); - - self.create_entity_with_uid( - phase_euid, - (Pair::builder() - .relation::<PhaseHasSystem>() - .target_id(system_ent_id) - .build(),), - ); - } - - /// Adds a extensions. - pub fn add_extension(&mut self, extension: impl Extension) - { - let extension_collector = ExtensionCollector::new(self); - - extension.collect(extension_collector); - } - - pub fn query<FieldTerms, FieldlessTerms>( - &self, - ) -> Query<'_, FieldTerms, FieldlessTerms> - where - FieldTerms: QueryTermWithFieldTuple, - FieldlessTerms: QueryTermWithoutFieldTuple, - { - Query::new(self) - } - - pub fn flexible_query<const MAX_TERM_CNT: usize>( - &self, - terms: QueryTerms<MAX_TERM_CNT>, - ) -> FlexibleQuery<'_, MAX_TERM_CNT> - { - FlexibleQuery::new(self, terms) - } - - pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> - { - let archetype = self - .data - .component_storage - .get_entity_archetype(entity_id)?; - - let Some(entity) = archetype.get_entity_by_id(entity_id) else { - unreachable!("Should exist since archetype was found by entity id"); - }; - - Some(EntityHandle::new(archetype, entity, self)) - } - - pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>> - { - Some(Single::new(self.data.sole_storage.get::<SoleT>()?)) - } - - pub fn event_submitter(&self) -> EventSubmitter<'_> - { - EventSubmitter::new(&self.data.new_events) - } - - /// Performs a single tick. - /// # Panics - /// 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() - { - let Some(start_phase_entity) = self.get_entity(*START_PHASE) else { - unreachable!(); - }; - - self.run_phase_systems(&start_phase_entity); - } - - self.perform_phases(); - - self.emit_new_events(); - - self.data.component_storage.create_imaginary_archetypes(); - - self.perform_queued_actions(); - - if self.stop.load(Ordering::Relaxed) { - return StepResult::Stop; - } - - let Some(mut stats) = self.get_sole::<Stats>() else { - unreachable!(); // Reason: is added in World::new - }; - - 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", - }, - ) - }, - }, - ) - } - - #[tracing::instrument(skip_all)] - fn create_ent( - &mut self, - entity_uid: Uid, - components: impl IntoIterator<Item = ComponentParts>, - ) - { - debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - - if let Err(EntityAlreadyExistsError) = - self.data.component_storage.create_entity(entity_uid) - { - // This is fine - } - - Self::add_entity_components( - entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - - fn run_phase_systems(&self, phase_entity: &EntityHandle<'_>) - { - // The phase's systems are retrieved this way so that the order they are - // run is the same order as they were registered, even if they have local - // components. - for system_entity in phase_entity - .get_wildcard_pair_matches::<PhaseHasSystem, Wildcard>() - .into_iter() - .filter_map(|phase_has_system| phase_has_system.get_target_ent()) - { - let Some(system) = system_entity.get::<SystemComponent>() else { - cold_path(); - continue; - }; - - // SAFETY: The world lives long enough - if let Err(err) = unsafe { - system - .system - .run(self, SystemMetadata { ent_id: system_entity.uid() }) - } { - cold_path(); - - (self.error_handler)( - err, - ErrorMetadata { - source_name: system.system.name(), - source_kind: ErrorSourceKind::System, - }, - ) - } - } - } - - fn perform_child_phases(&self, parent_phase_euid: Uid) - { - let phase_query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([ - Phase::id(), - Pair::builder() - .relation::<ChildOf>() - .target_id(parent_phase_euid) - .build() - .id(), - ]) - .build(), - ); - - for child_phase_entity in &phase_query { - self.run_phase_systems(&child_phase_entity); - self.perform_child_phases(child_phase_entity.uid()); - } - } - - fn perform_single_phase(&self, phase_entity_id: Uid) - { - let Some(phase_entity) = self.get_entity(phase_entity_id) else { - unreachable!(); - }; - - self.run_phase_systems(&phase_entity); - self.perform_child_phases(phase_entity_id); - } - - fn perform_phases(&self) - { - self.perform_single_phase(*PRE_UPDATE_PHASE); - self.perform_single_phase(*UPDATE_PHASE); - self.perform_single_phase(*POST_UPDATE_PHASE); - } - - fn emit_new_events(&self) - { - loop { - let new_events = { - let mut new_events_lock = self - .data - .new_events - .write_nonblock() - .expect("Failed to acquire read-write lock to new events"); - - if new_events_lock.is_empty() { - break; - } - - new_events_lock.take() - }; - - for (event_id, event_matches) in new_events { - self.emit_event_observers( - event_id, - &EmittedEvent { - event: event_id, - match_ids: &event_matches.match_ids, - }, - ); - } - } - } - - #[tracing::instrument(skip_all)] - fn perform_queued_actions(&mut self) - { - let mut action_queue_lock = self - .data - .action_queue - .queue - .write_nonblock() - .unwrap_or_else(|err| { - panic!("Failed to take read-write action queue lock: {err}",); - }); - - for action in action_queue_lock.drain(..) { - match action { - Action::Spawn(new_entity_uid, components) => { - if let Err(err) = - self.data.component_storage.create_entity(new_entity_uid) - { - tracing::warn!("Failed to create entity: {err}"); - continue; - } - - Self::add_entity_components( - new_entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - Action::Despawn(entity_uid) => { - if let Err(err) = - self.data.component_storage.remove_entity(entity_uid) - { - tracing::error!("Failed to despawn entity: {err}"); - } - } - Action::AddComponents(entity_uid, components) => { - Self::add_entity_components( - entity_uid, - components, - &mut self.data.component_storage, - &EventSubmitter::new(&self.data.new_events), - ); - } - Action::RemoveComponents(entity_uid, component_ids) => { - Self::remove_entity_components( - entity_uid, - component_ids, - &mut self.data.component_storage, - ); - } - Action::Stop => { - self.stop.store(true, Ordering::Relaxed); - } - } - } - } - - fn add_entity_components( - entity_uid: Uid, - components: impl IntoIterator<Item = ComponentParts>, - component_storage: &mut ComponentStorage, - event_submitter: &EventSubmitter<'_>, - ) - { - let component_iter = components.into_iter(); - - for component_parts in component_iter { - let comp_id = component_parts.id(); - - let comp_name = component_parts.name(); - - if let Err(err) = component_storage.add_entity_component( - entity_uid, - (comp_id, comp_name, component_parts.into_data()), - ) { - tracing::error!("Failed to add component {comp_name} to entity: {err}"); - continue; - } - - if comp_id.kind() == UidKind::Pair { - continue; - } - - event_submitter.submit_event( - &Pair::builder() - .relation::<Added>() - .target_id(comp_id) - .build(), - entity_uid, - ); - } - } - - fn remove_entity_components( - entity_uid: Uid, - component_ids: impl IntoIterator<Item = Uid>, - component_storage: &mut ComponentStorage, - ) - { - let component_id_iter = component_ids.into_iter(); - - 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}"); - } - } - } - - fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>) - { - assert_eq!(event_id.kind(), UidKind::Pair); - - let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query( - self.flexible_query( - QueryTerms::<QUERY_MAX_TERM_CNT>::builder() - .with_required([ObserverWrapperComponent::id(), event_id]) - .build(), - ), - ); - - for (observer_ent_id, (observer,)) in query.iter_with_euids() { - if let Err(err) = unsafe { - observer.run( - self, - SystemMetadata { ent_id: observer_ent_id }, - emitted_event.clone(), - ) - } { - cold_path(); - - (self.error_handler)( - err, - ErrorMetadata { - source_name: observer.name(), - source_kind: ErrorSourceKind::Observer, - }, - ) - } - } - } -} - -impl Default for World -{ - 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, Default)] -struct WorldData -{ - component_storage: ComponentStorage, - sole_storage: SoleStorage, - action_queue: Rc<ActionQueue>, - new_events: Lock<NewEvents>, -} - -#[derive(Debug, Clone)] -pub struct EntityComponentRef<'a> -{ - component_id: Uid, - component: &'a ArchetypeEntityComponent, - entity_id: Uid, -} - -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 - } - - #[must_use] - pub fn entity_id(&self) -> Uid - { - self.entity_id - } - - fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid) - -> Self - { - Self { - component_id, - component: comp, - entity_id, - } - } -} - -#[derive(Debug, Default)] -struct ActionQueue -{ - queue: Lock<Vec<Action>>, -} - -impl ActionQueue -{ - fn push(&self, action: Action) - { - self.queue - .write_nonblock() - .expect("Failed to aquire read-write lock to action queue") - .push(action); - } -} - -#[derive(Debug, thiserror::Error)] -#[error("Sole {0} already exists")] -pub struct SoleAlreadyExistsError(pub &'static str); - -#[derive(Debug)] -struct StoredSole -{ - sole: Arc<Lock<Box<dyn Sole>>>, - drop_last: bool, -} - -#[derive(Debug, Default)] -struct SoleStorage -{ - storage: HashMap<TypeId, ManuallyDrop<StoredSole>>, -} - -impl SoleStorage -{ - fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>> - { - self.storage - .get(&TypeId::of::<SoleT>()) - .map(|sole| &sole.sole) - } - - fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> - { - let sole_type_id = TypeId::of::<SoleT>(); - - if self.storage.contains_key(&sole_type_id) { - return Err(SoleAlreadyExistsError(type_name::<SoleT>())); - } - - let drop_last = sole.drop_last(); - - // TODO: Reconsider this maybe? - #[allow(clippy::arc_with_non_send_sync)] - self.storage.insert( - sole_type_id, - ManuallyDrop::new(StoredSole { - sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())), - drop_last, - }), - ); - - Ok(()) - } -} - -impl Drop for SoleStorage -{ - fn drop(&mut self) - { - let mut soles_to_drop_last = Vec::new(); - - for sole in self.storage.values_mut() { - if sole.drop_last { - soles_to_drop_last.push(sole); - continue; - } - - unsafe { - ManuallyDrop::drop(sole); - } - } - - for sole in &mut soles_to_drop_last { - unsafe { - ManuallyDrop::drop(sole); - } - } - } -} |
