#![deny(clippy::all, clippy::pedantic)] use std::any::{type_name, TypeId}; use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; use std::mem::ManuallyDrop; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use crate::actions::Action; use crate::component::storage::Storage as ComponentStorage; use crate::component::{ Component, IsOptional as ComponentIsOptional, Metadata as ComponentMetadata, Sequence as ComponentSequence, }; use crate::entity::CREATE_STATIC_ENTITIES; use crate::event::component::Kind as ComponentEventKind; use crate::extension::{Collector as ExtensionCollector, Extension}; use crate::lock::{Lock, WriteGuard}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::options::{Not, Options as QueryOptions, With}; use crate::relationship::{ChildOf, DependsOn, Relationship}; 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}; pub mod actions; pub mod component; pub mod entity; pub mod event; pub mod extension; pub mod lock; 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; #[doc(hidden)] pub mod private; mod archetype; mod util; pub use ecs_macros::{Component, Sole}; pub use crate::query::Query; #[derive(Debug, Default)] pub struct World { data: WorldData, stop: AtomicBool, is_first_tick: AtomicBool, } impl World { #[must_use] pub fn new() -> Self { let mut world = Self::default(); world.add_sole(Stats::default()).ok(); for create_static_entity in CREATE_STATIC_ENTITIES { create_static_entity(&world); } world } /// Creates a new entity with the given components. /// /// # Panics /// Will panic if mutable internal lock cannot be acquired. pub fn create_entity(&mut self, components: Comps) -> Uid where Comps: ComponentSequence, { let entity_uid = Uid::new_unique(UidKind::Entity); self.create_entity_with_uid(components, entity_uid); entity_uid } #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] #[doc(hidden)] pub fn create_entity_with_uid(&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}"); return; }; for added_event_id in Comps::added_event_ids() { self.emit_event_by_id(added_event_id); } } /// Adds a globally shared singleton value. /// /// # Errors /// Returns `Err` if this [`Sole`] has already been added. pub fn add_sole(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> where SoleT: Sole, { self.data.sole_storage.insert(sole) } pub fn register_system<'this, SystemImpl>( &'this mut self, phase_euid: Uid, system: impl System<'this, SystemImpl>, ) { self.create_entity(( SystemComponent { system: system.into_type_erased() }, Relationship::::new(phase_euid), )); } pub fn register_observer_system<'this, SystemImpl, Event>( &'this mut self, system: impl System<'this, SystemImpl>, event: Event, ) where Event: Component, { self.create_entity::<(SystemComponent, Event)>(( 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); extension.collect(extension_collector); } pub fn query(&self) -> Query where Comps: ComponentSequence, OptionsT: QueryOptions, { Query::new(self) } /// Performs a single tick. /// /// # Panics /// Will panic if a internal lock cannot be acquired. pub fn step(&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() { self.query_and_run_systems(*START_PHASE); } self.perform_phases(); self.perform_queued_actions(); if self.stop.load(Ordering::Relaxed) { return StepResult::Stop; } let mut stats_lock = self .data .sole_storage .get::() .expect("No stats sole found") .write_nonblock() .expect("Failed to aquire read-write stats sole lock"); let stats = stats_lock .downcast_mut::() .expect("Casting stats sole to Stats type failed"); stats.current_tick += 1; StepResult::Continue } /// 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) { while let StepResult::Continue = self.step() {} } fn query_and_run_systems(&self, phase_euid: Uid) { let system_comps_query = self.query::<(SystemComponent, Relationship), ()>(); let system_iter = system_comps_query.iter().filter(|(_, phase_rel)| { phase_rel .target_uids() .any(|target_uid| target_uid == phase_euid) }); for (system_component, _) in system_iter { // SAFETY: The world lives long enough unsafe { system_component.system.run(self); } } } fn perform_child_phases(&self, parent_phase_euid: Uid) { let phase_query = self.query::<(Phase, Relationship), ()>(); 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); self.perform_child_phases(phase_euid); } } fn perform_phases(&self) { let phase_query = self.query::<(Phase,), Not>>>(); 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"); if child_phase_euid == *START_PHASE { continue; } self.query_and_run_systems(child_phase_euid); self.perform_child_phases(child_phase_euid); } } #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] fn perform_queued_actions(&self) { let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() { ActiveActionQueue::A => &self.data.action_queue.queue_a, ActiveActionQueue::B => &self.data.action_queue.queue_b, } .write_nonblock() .unwrap_or_else(|err| { panic!( "Failed to take read-write action queue lock {:?}: {err}", self.data.action_queue.active_queue ); }); let mut has_swapped_active_queue = false; 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(); #[allow(unused_variables)] if let Err(err) = component_storage_lock .push_entity(Uid::new_unique(UidKind::Entity), components) { #[cfg(feature = "debug")] tracing::error!("Failed to create entity: {err}"); continue; } drop(component_storage_lock); 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); } } Action::Despawn(entity_uid) => { self.despawn_entity(entity_uid, &mut has_swapped_active_queue); } 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); 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); } } 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( entity_uid, components_metadata .iter() .map(|component_metadata| component_metadata.id), ); 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); } } } } fn despawn_entity(&self, entity_uid: Uid, has_swapped_active_queue: &mut bool) { let mut component_storage_lock = self.lock_component_storage_rw(); let Some(archetype) = component_storage_lock.get_entity_archetype(entity_uid) else { #[cfg(feature = "debug")] tracing::error!("No archetype for entity {entity_uid:?} was found"); 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::>(); component_storage_lock.remove_entity(entity_uid); drop(component_storage_lock); if !*has_swapped_active_queue { self.swap_event_queue(has_swapped_active_queue); } for comp_removed_event_id in component_removed_event_uids { self.emit_event_by_id(comp_removed_event_id); } } fn emit_event_by_id(&self, event_id: Uid) { for (system,) in self .query::<(SystemComponent,), ()>() .iter_with_extra_comps([ComponentMetadata { id: event_id, is_optional: ComponentIsOptional::No, }]) { // SAFETY: The world lives long enough unsafe { system.system.run(self); } } } fn swap_event_queue(&self, has_swapped_active_queue: &mut bool) { let mut active_queue = self.data.action_queue.active_queue.borrow_mut(); *active_queue = match *active_queue { ActiveActionQueue::A => ActiveActionQueue::B, ActiveActionQueue::B => ActiveActionQueue::A, }; *has_swapped_active_queue = true; } fn lock_component_storage_rw(&self) -> WriteGuard<'_, ComponentStorage> { self.data .component_storage .write_nonblock() .expect("Failed to acquire read-write component storage lock") } } /// 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)] pub struct WorldData { component_storage: Arc>, sole_storage: SoleStorage, action_queue: Arc, } #[derive(Debug)] #[non_exhaustive] pub struct EntityComponent { pub id: Uid, pub name: &'static str, pub component: Lock>, } impl From> for EntityComponent { fn from(component: Box) -> Self { Self { id: component.self_id(), name: component.type_name(), component: Lock::new(component), } } } #[derive(Debug, Default, Clone, Copy)] enum ActiveActionQueue { #[default] A, B, } #[derive(Debug, Default)] struct ActionQueue { queue_a: Lock>, queue_b: Lock>, active_queue: RefCell, } impl ActionQueue { fn push(&self, action: Action) { match *self.active_queue.borrow() { ActiveActionQueue::A => self .queue_a .write_nonblock() .expect("Failed to aquire read-write action queue A lock") .push(action), ActiveActionQueue::B => self .queue_b .write_nonblock() .expect("Failed to aquire read-write action queue A lock") .push(action), } } } impl TypeName for ActionQueue { fn type_name(&self) -> &'static str { type_name::() } } #[derive(Debug, thiserror::Error)] #[error("Sole {0} already exists")] pub struct SoleAlreadyExistsError(pub &'static str); #[derive(Debug)] struct StoredSole { sole: Arc>>, drop_last: bool, } #[derive(Debug, Default)] struct SoleStorage { storage: HashMap>, } impl SoleStorage { fn get(&self) -> Option<&Arc>>> { self.storage .get(&TypeId::of::()) .map(|sole| &sole.sole) } fn insert(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError> { let sole_type_id = TypeId::of::(); if self.storage.contains_key(&sole_type_id) { return Err(SoleAlreadyExistsError(type_name::())); } 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))), 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 { #[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); } } } }