From de5c3ff1320ea0f0452afde4c1f42676d9eeab52 Mon Sep 17 00:00:00 2001 From: HampusM Date: Fri, 16 Feb 2024 19:58:53 +0100 Subject: feat: add entity component system library --- ecs/src/lib.rs | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 ecs/src/lib.rs (limited to 'ecs/src/lib.rs') diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs new file mode 100644 index 0000000..5b8942b --- /dev/null +++ b/ecs/src/lib.rs @@ -0,0 +1,194 @@ +#![deny(clippy::all, clippy::pedantic)] + +use std::any::TypeId; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::PhantomData; +use std::slice::IterMut as SliceIterMut; + +use crate::component::{Component, Sequence as ComponentSequence}; + +pub mod component; + +#[derive(Debug)] +struct Entity +{ + components: Vec>, +} + +#[derive(Debug)] +pub struct World +{ + systems: Vec>, + events: HashMap>, + extra: WorldExtra, +} + +#[derive(Debug)] +struct WorldExtra +{ + entities: Vec, +} + +impl World +{ + #[must_use] + pub fn new() -> Self + { + Self { + systems: Vec::new(), + extra: WorldExtra { entities: Vec::new() }, + events: HashMap::new(), + } + } + + pub fn create_entity(&mut self, components: Comps) + where + Comps: ComponentSequence, + { + self.extra + .entities + .push(Entity { components: components.into_vec() }); + } + + pub fn register_system(&mut self, event: Event, system: System) + where + Event: Hash + PartialEq + Eq, + Comps: ComponentSequence + 'static, + { + self.systems.push(Box::new(system)); + + 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.call(&mut self.extra); + } + } + + pub fn query(&mut self) -> Query + where + Comps: ComponentSequence, + { + Query::new(&mut self.extra) + } +} + +impl Default for World +{ + fn default() -> Self + { + Self::new() + } +} + +pub type System = fn(Query); + +trait AnySystem +{ + fn call(&self, world: &mut WorldExtra); +} + +impl AnySystem for System +where + Comps: ComponentSequence, +{ + fn call(&self, world: &mut WorldExtra) + { + self(Query::new(world)); + } +} + +impl Debug for dyn AnySystem +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + formatter.debug_struct("AnySystem").finish_non_exhaustive() + } +} + +#[derive(Debug)] +pub struct Query<'world, Comps> +{ + world: &'world mut WorldExtra, + comps_pd: PhantomData, +} + +impl<'world, Comps> Query<'world, Comps> +{ + fn new(world: &'world mut WorldExtra) -> Self + { + Self { world, comps_pd: PhantomData } + } +} + +impl<'world, Comps> Query<'world, Comps> +where + Comps: ComponentSequence, +{ + pub fn iter_mut(&mut self) -> QueryComponentIter + { + QueryComponentIter { + entity_iter: self.world.entities.iter_mut(), + component_type_ids: Comps::type_ids(), + comps_pd: PhantomData, + } + } +} + +pub struct QueryComponentIter<'world, Comps> +{ + entity_iter: SliceIterMut<'world, Entity>, + component_type_ids: Vec, + comps_pd: PhantomData, +} + +impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps> +where + Comps: ComponentSequence + 'world, +{ + type Item = Comps::MutRefs<'world>; + + fn next(&mut self) -> Option + { + // TODO: This is a really dumb and slow way to do this. Refactor the world + // to store components in archetypes + let entity = + self.entity_iter.find(|entity| { + let entity_components: HashSet<_> = entity + .components + .iter() + .map(|component| component.as_ref().type_id()) + .collect(); + + if self.component_type_ids.iter().all(|component_type_id| { + entity_components.contains(component_type_id) + }) { + return true; + } + + false + })?; + + Some(Comps::from_components(&mut entity.components)) + } +} -- cgit v1.2.3-18-g5258