#![deny(clippy::all, clippy::pedantic)] use std::any::{type_name, TypeId}; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::mem::ManuallyDrop; use std::ops::RangeBounds; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::vec::Drain; use crate::actions::Action; use crate::component::{Component, Sequence as ComponentSequence}; use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; use crate::lock::Lock; use crate::sole::Sole; use crate::system::{System, TypeErased as TypeErasedSystem}; use crate::type_name::TypeName; pub mod actions; pub mod component; pub mod event; pub mod lock; pub mod query; pub mod sole; pub mod system; pub mod tuple; pub mod type_name; pub use ecs_macros::{Component, Sole}; pub use crate::query::Query; #[derive(Debug, Default)] struct Entity { components: Vec>, } #[derive(Debug)] struct EntityComponent { id: TypeId, component: Lock>, drop_last: bool, } #[derive(Debug, Default)] pub struct World { systems: Vec, data: WorldData, stop: AtomicBool, } impl World { #[must_use] pub fn new() -> Self { Self::default() } /// 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) where Comps: ComponentSequence, { self.data .component_storage .write_nonblock() .expect("Failed to acquire read-write component storage lock") .entities .push(Entity { components: components .into_vec() .into_iter() .map(|component| { let drop_last = component.drop_last(); ManuallyDrop::new(EntityComponent { id: (*component).type_id(), component: Lock::new(component), drop_last, }) }) .collect(), }); } /// 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, EventT, SystemImpl>( &'this mut self, event: EventT, system: impl System<'this, SystemImpl>, ) where EventT: Event, { self.systems.push(system.into_type_erased()); self.data .events .entry(EventId::of::()) .or_default() .push(self.systems.len() - 1); drop(event); } /// Emits a event, running all systems listening to the event for each compatible /// entity. /// /// # Panics /// Will panic if a system has dissapeared. pub fn emit(&self, event: EventT) where EventT: Event, { self.emit_event_by_id(EventId::of::()); drop(event); } pub fn query(&self) -> Query where Comps: ComponentSequence, { Query::new(&self.data.component_storage) } /// Peforms the actions that have been queued up using [`Actions`]. /// /// # Panics /// Will panic if a mutable internal lock cannot be acquired. pub fn perform_queued_actions(&self) { for action in self .data .action_queue .write_nonblock() .expect("Failed to aquire read-write action queue lock") .drain(..) { match action { Action::Spawn(components) => { self.data .component_storage .write_nonblock() .expect("Failed to acquire read-write component storage lock") .entities .push(Entity { components: components .into_iter() .map(|component| { let drop_last = component.drop_last(); ManuallyDrop::new(EntityComponent { id: (*component).type_id(), component: Lock::new(component), drop_last, }) }) .collect(), }); } Action::Stop => { self.stop.store(true, Ordering::Relaxed); } } } } /// A event loop which runs until a stop is issued with [`Flags::stop`]. /// /// # Panics /// Will panic if a internal lock cannot be acquired. pub fn event_loop(&self) { let event_seq = EventSeq::ids(); loop { for event_id in event_seq.iter() { self.emit_event_by_id(*event_id); } self.perform_queued_actions(); if self.stop.load(Ordering::Relaxed) { break; } } } fn emit_event_by_id(&self, event_id: EventId) { let Some(system_indices) = self.data.events.get(&event_id) else { return; }; for system_index in system_indices { let system = self.systems.get(*system_index).unwrap(); // SAFETY: The world data lives long enough unsafe { system.run(&self.data); } } } } #[derive(Debug, Default)] pub struct WorldData { events: HashMap>, component_storage: Arc>, sole_storage: SoleStorage, action_queue: Arc>, } #[derive(Debug, Default)] struct ActionQueue { queue: Vec, } impl ActionQueue { fn push(&mut self, action: Action) { self.queue.push(action); } fn drain(&mut self, range: impl RangeBounds) -> Drain { self.queue.drain(range) } } impl TypeName for ActionQueue { fn type_name(&self) -> &'static str { type_name::() } } #[derive(Debug, Default)] pub struct ComponentStorage { entities: Vec, } impl ComponentStorage { fn find_entity_with_components( &self, start_index: usize, component_type_ids: &[TypeId], ) -> Option<(usize, &Entity)> { // TODO: This is a really dumb and slow way to do this. Refactor the world // to store components in archetypes self.entities.iter().enumerate().skip(start_index).find( move |(_index, entity)| { let entity_components = entity .components .iter() .map(|component| component.id) .collect::>(); if component_type_ids.iter().all(|component_type_id| { entity_components.contains(component_type_id) }) { return true; } false }, ) } } impl TypeName for ComponentStorage { fn type_name(&self) -> &'static str { type_name::() } } impl Drop for ComponentStorage { fn drop(&mut self) { let mut components_to_drop_last = Vec::new(); for entity in &mut self.entities { for component in &mut entity.components { if component.drop_last { #[cfg(feature = "debug")] tracing::debug!( "Component {} pushed to dropping last queue", component.component.read_nonblock().unwrap().type_name() ); components_to_drop_last.push(component); continue; } #[cfg(feature = "debug")] tracing::debug!( "Dropping component {}", component.component.read_nonblock().unwrap().type_name() ); unsafe { ManuallyDrop::drop(component); } } } for component in &mut components_to_drop_last { #[cfg(feature = "debug")] tracing::debug!( "Dropping component {} last", component.component.read_nonblock().unwrap().type_name() ); unsafe { ManuallyDrop::drop(component); } } } } #[derive(Debug, thiserror::Error)] #[error("Sole {0} already exists")] pub struct SoleAlreadyExistsError(pub &'static str); #[derive(Debug, Default)] struct SoleStorage { storage: HashMap>>, } impl SoleStorage { fn get(&self) -> Option<&Lock>> { self.storage.get(&TypeId::of::()) } 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::())); } self.storage.insert(sole_type_id, Lock::new(Box::new(sole))); Ok(()) } }