#![deny(clippy::all, clippy::pedantic)] use std::any::{Any, TypeId}; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use std::slice::{Iter as SliceIter, IterMut as SliceIterMut}; use crate::component::{Component, Sequence as ComponentSequence}; use crate::system::{ NoInitParamFlag as NoInitSystemParamFlag, Param as SystemParam, System, TypeErased as TypeErasedSystem, }; use crate::tuple::FilterExclude as TupleFilterExclude; pub mod component; pub mod system; pub mod tuple; pub use ecs_macros::Component; #[derive(Debug)] struct Entity { components: Vec>, } #[derive(Debug)] pub struct World { systems: Vec, events: HashMap>, component_storage: ComponentStorage, } impl World { #[must_use] pub fn new() -> Self { Self { systems: Vec::new(), component_storage: ComponentStorage { entities: Vec::new() }, events: HashMap::new(), } } pub fn create_entity(&mut self, components: Comps) where Comps: ComponentSequence, { self.component_storage .entities .push(Entity { components: components.into_vec() }); } pub fn register_system(&mut self, event: Event, system: TSystem) where Event: Hash + PartialEq + Eq, TSystem: System, { self.systems.push(system.into_type_erased()); self.events .entry(event) .or_default() .push(self.systems.len() - 1); } /// 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(&mut self, event: &Event) where Event: Hash + PartialEq + Eq, { let Some(system_indices) = self.events.get(event).cloned() else { return; }; for system_index in system_indices { let system = self.systems.get_mut(system_index).unwrap(); system.run(&mut self.component_storage); } } pub fn query(&mut self) -> Query where Comps: ComponentSequence, { Query::new(&mut self.component_storage) } } impl Default for World { fn default() -> Self { Self::new() } } #[derive(Debug)] pub struct Query<'world, Comps> where Comps: ComponentSequence, { component_storage: &'world mut ComponentStorage, comps_pd: PhantomData, } impl<'world, Comps> Query<'world, Comps> where Comps: ComponentSequence, { fn new(component_storage: &'world mut ComponentStorage) -> Self { Self { component_storage, comps_pd: PhantomData, } } } impl<'world, Comps> Query<'world, Comps> where Comps: ComponentSequence, { pub fn iter(&self) -> QueryComponentIter { QueryComponentIter { entity_iter: self.component_storage.entities.iter(), component_type_ids: Comps::type_ids(), comps_pd: PhantomData, } } pub fn iter_mut(&mut self) -> QueryComponentMutIter { QueryComponentMutIter { entity_iter: self.component_storage.entities.iter_mut(), component_type_ids: Comps::type_ids(), comps_pd: PhantomData, } } } impl<'world, Comps> IntoIterator for &'world Query<'world, Comps> where Comps: ComponentSequence, { type IntoIter = QueryComponentIter<'world, Comps>; type Item = Comps::Refs<'world>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'world, Comps> IntoIterator for &'world mut Query<'world, Comps> where Comps: ComponentSequence, { type IntoIter = QueryComponentMutIter<'world, Comps>; type Item = Comps::MutRefs<'world>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } unsafe impl<'world, Comps> SystemParam<'world> for Query<'world, Comps> where Comps: ComponentSequence, { type Flags = NoInitSystemParamFlag; type Input = TupleFilterExclude; fn initialize(_system: &mut impl System, _input: Self::Input) { } fn new( _system: &'world mut impl System, component_storage: &'world mut ComponentStorage, ) -> Self { Self::new(component_storage) } fn is_compatible>() -> bool { let other_comparable = Other::get_comparable(); let Some(other_query_component_ids) = other_comparable.downcast_ref::() else { return true; }; !other_query_component_ids.contains_component_in::() } fn get_comparable() -> Box { Box::new(QueryComponentIds { component_type_ids: Comps::type_ids(), }) } } #[derive(Debug)] struct QueryComponentIds { component_type_ids: Vec, } impl QueryComponentIds { fn contains_component_in(&self) -> bool where OtherComps: ComponentSequence, { let other_component_type_ids = OtherComps::type_ids() .into_iter() .collect::>(); // TODO: Make this a bit smarter. Queries with a same component can be compatible // if one of the queries have a component the other one does not have self.component_type_ids .iter() .any(|component_type_id| other_component_type_ids.contains(component_type_id)) } } pub struct QueryComponentIter<'world, Comps> { entity_iter: SliceIter<'world, Entity>, component_type_ids: Vec, comps_pd: PhantomData, } impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps> where Comps: ComponentSequence + 'world, { type Item = Comps::Refs<'world>; fn next(&mut self) -> Option { let matching_entity = find_entity_with_components::<&Entity>( &mut self.entity_iter, &self.component_type_ids, )?; Some(Comps::from_components(&matching_entity.components)) } } pub struct QueryComponentMutIter<'world, Comps> { entity_iter: SliceIterMut<'world, Entity>, component_type_ids: Vec, comps_pd: PhantomData, } impl<'world, Comps> Iterator for QueryComponentMutIter<'world, Comps> where Comps: ComponentSequence + 'world, { type Item = Comps::MutRefs<'world>; fn next(&mut self) -> Option { let matching_entity = find_entity_with_components::<&mut Entity>( &mut self.entity_iter, &self.component_type_ids, )?; Some(Comps::from_components_mut(&mut matching_entity.components)) } } trait EntityRef { fn components(&self) -> &[Box]; } impl EntityRef for &Entity { fn components(&self) -> &[Box] { &self.components } } impl EntityRef for &mut Entity { fn components(&self) -> &[Box] { &self.components } } fn find_entity_with_components( entity_iter: &mut impl Iterator, component_type_ids: &[TypeId], ) -> Option where EntityRefT: EntityRef, { // TODO: This is a really dumb and slow way to do this. Refactor the world // to store components in archetypes entity_iter.find(|entity| { let entity_components: HashSet<_> = entity .components() .iter() .map(|component| component.as_ref().type_id()) .collect(); if component_type_ids .iter() .all(|component_type_id| entity_components.contains(component_type_id)) { return true; } false }) } #[derive(Debug)] pub struct ComponentStorage { entities: Vec, }