summaryrefslogtreecommitdiff
path: root/ecs/src/lib.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-02-16 19:58:53 +0100
committerHampusM <hampus@hampusmat.com>2024-02-18 18:23:03 +0100
commitde5c3ff1320ea0f0452afde4c1f42676d9eeab52 (patch)
tree5394c42d76301f68526816552689a0beeb2ffdfb /ecs/src/lib.rs
parent12f73be1bf0a7dd7d67bab2eb44d0f0629d37028 (diff)
feat: add entity component system library
Diffstat (limited to 'ecs/src/lib.rs')
-rw-r--r--ecs/src/lib.rs194
1 files changed, 194 insertions, 0 deletions
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<Box<dyn Component>>,
+}
+
+#[derive(Debug)]
+pub struct World<Event>
+{
+ systems: Vec<Box<dyn AnySystem>>,
+ events: HashMap<Event, Vec<usize>>,
+ extra: WorldExtra,
+}
+
+#[derive(Debug)]
+struct WorldExtra
+{
+ entities: Vec<Entity>,
+}
+
+impl<Event> World<Event>
+{
+ #[must_use]
+ pub fn new() -> Self
+ {
+ Self {
+ systems: Vec::new(),
+ extra: WorldExtra { entities: Vec::new() },
+ events: HashMap::new(),
+ }
+ }
+
+ pub fn create_entity<Comps>(&mut self, components: Comps)
+ where
+ Comps: ComponentSequence,
+ {
+ self.extra
+ .entities
+ .push(Entity { components: components.into_vec() });
+ }
+
+ pub fn register_system<Comps>(&mut self, event: Event, system: System<Comps>)
+ 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<Comps>(&mut self) -> Query<Comps>
+ where
+ Comps: ComponentSequence,
+ {
+ Query::new(&mut self.extra)
+ }
+}
+
+impl<Event> Default for World<Event>
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
+}
+
+pub type System<Comps> = fn(Query<Comps>);
+
+trait AnySystem
+{
+ fn call(&self, world: &mut WorldExtra);
+}
+
+impl<Comps> AnySystem for System<Comps>
+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<Comps>,
+}
+
+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<Comps>
+ {
+ 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<TypeId>,
+ comps_pd: PhantomData<Comps>,
+}
+
+impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps>
+where
+ Comps: ComponentSequence + 'world,
+{
+ type Item = Comps::MutRefs<'world>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ // 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))
+ }
+}