use std::{any::TypeId, collections::HashMap};
use crate::{
CommandBuffer, Entity, Universe, World, WorldId,
components::{AnyComponent, Component},
query::Query,
};
use dirk_proc::system_trait;
pub trait System: 'static {
fn name() -> &'static str;
}
#[doc(hidden)]
pub use dirk_proc::System;
#[system_trait]
pub trait UniverseSystem: System {
fn world_created(&self, cmd: &mut CommandBuffer, universe: &Universe, world: &World);
fn world_destroyed(&self, cmd: &mut CommandBuffer, universe: &Universe, world: &World);
fn entity_spawned(&self, cmd: &mut CommandBuffer, universe: &Universe, entity: Entity);
fn entity_sent(
&self,
cmd: &mut CommandBuffer,
universe: &Universe,
entity: Entity,
old: WorldId,
new: WorldId,
);
fn entity_despawned(&self, cmd: &mut CommandBuffer, universe: &Universe, entity: Entity);
fn tick(&self, cmd: &mut CommandBuffer, universe: &Universe, delta_time: f64);
}
#[system_trait]
pub trait EntitySystem: System {
fn spawned(&self, cmd: &mut CommandBuffer, universe: &Universe, entity: Entity);
fn despawned(&self, cmd: &mut CommandBuffer, universe: &Universe, entity: Entity);
fn sent(
&self,
cmd: &mut CommandBuffer,
universe: &Universe,
entity: Entity,
from: WorldId,
to: WorldId,
);
fn query(&self) -> Query;
}
#[system_trait]
pub trait TickingSystem: System {
fn tick(
&self,
cmd: &mut CommandBuffer,
universe: &Universe,
delta_time: f64,
entities: &mut dyn Iterator<Item = Entity>,
);
fn query(&self) -> Query;
}
pub trait ComponentSystem: System {
type Component: Component;
fn added(&self, cmd: &mut CommandBuffer, entity: Entity, component: &Self::Component);
fn updated(
&self,
cmd: &mut CommandBuffer,
entity: Entity,
old: &Self::Component,
new: &Self::Component,
);
fn removed(&self, cmd: &mut CommandBuffer, entity: Entity, component: &Self::Component);
}
pub(crate) trait AnyComponentSystem {
fn component_type_id(&self) -> TypeId;
fn added(&self, cmd: &mut CommandBuffer, entity: Entity, component: &dyn AnyComponent);
fn updated(
&self,
cmd: &mut CommandBuffer,
entity: Entity,
old: &dyn AnyComponent,
new: &dyn AnyComponent,
);
fn removed(&self, cmd: &mut CommandBuffer, entity: Entity, component: &dyn AnyComponent);
}
impl<T: ComponentSystem> AnyComponentSystem for T {
fn component_type_id(&self) -> TypeId {
TypeId::of::<T::Component>()
}
fn added(&self, cmd: &mut CommandBuffer, entity: Entity, component: &dyn AnyComponent) {
if let Some(component) = component.as_any().downcast_ref::<T::Component>() {
T::added(self, cmd, entity, component);
}
}
fn updated(
&self,
cmd: &mut CommandBuffer,
entity: Entity,
old: &dyn AnyComponent,
new: &dyn AnyComponent,
) {
let Some(old) = old.as_any().downcast_ref::<T::Component>() else {
return;
};
let Some(new) = new.as_any().downcast_ref::<T::Component>() else {
return;
};
T::updated(self, cmd, entity, old, new);
}
fn removed(&self, cmd: &mut CommandBuffer, entity: Entity, component: &dyn AnyComponent) {
if let Some(component) = component.as_any().downcast_ref::<T::Component>() {
T::removed(self, cmd, entity, component);
}
}
}
#[derive(Default)]
pub(crate) struct ComponentSystemStorage {
systems: HashMap<TypeId, Vec<Box<dyn AnyComponentSystem>>>,
}
impl ComponentSystemStorage {
pub fn push<S: ComponentSystem>(&mut self, system: S) {
self.systems
.entry(system.component_type_id())
.or_default()
.push(Box::new(system));
}
pub fn push_any(&mut self, type_id: TypeId, system: Box<dyn AnyComponentSystem>) {
self.systems.entry(type_id).or_default().push(system);
}
pub fn iter(&self, type_id: TypeId) -> std::slice::Iter<'_, Box<dyn AnyComponentSystem>> {
#[allow(clippy::map_unwrap_or)]
self.systems
.get(&type_id)
.map(Vec::as_slice)
.unwrap_or(&[])
.iter()
}
}
impl IntoIterator for ComponentSystemStorage {
type Item = (TypeId, Vec<Box<dyn AnyComponentSystem>>);
type IntoIter = std::collections::hash_map::IntoIter<TypeId, Vec<Box<dyn AnyComponentSystem>>>;
fn into_iter(self) -> Self::IntoIter {
self.systems.into_iter()
}
}