use core::{any::Any, cell::Cell};
use std::collections::{BTreeMap, HashMap};
use crate::{component::*, entity::*, error::NotFound};
#[derive(Default)]
pub struct PhantomContext;
pub type Priority = i32;
pub trait System<E, Ctx>: Any
where
E: EntityStore,
{
fn run(&self, _ecm: &mut EntityComponentManager<E>) {}
fn run_with_context(&self, _ecm: &mut EntityComponentManager<E>, _ctx: &mut Ctx) {}
}
pub struct EntitySystem<E, Ctx> {
pub system: Box<dyn System<E, Ctx>>,
priority: Priority,
}
impl<E, Ctx> EntitySystem<E, Ctx> {
pub fn new(system: Box<dyn System<E, Ctx>>) -> Self {
EntitySystem {
system,
priority: 0,
}
}
}
pub struct SystemStoreBuilder<'a, E, Ctx>
where
E: EntityStore,
{
pub entity_system_id: u32,
pub system_store: &'a mut SystemStore<E, Ctx>,
pub priority: Cell<i32>,
}
impl<'a, E, Ctx> SystemStoreBuilder<'a, E, Ctx>
where
E: EntityStore,
{
pub fn with_priority(self, priority: Priority) -> Self {
self.priority.set(priority);
self
}
pub fn build(self) -> u32 {
self.system_store
.register_priority(self.priority.get(), self.entity_system_id);
self.entity_system_id
}
}
#[derive(Default)]
pub struct SystemStore<E, Ctx>
where
E: EntityStore,
{
entity_systems: HashMap<u32, EntitySystem<E, Ctx>>,
init_system: Option<EntitySystem<E, Ctx>>,
cleanup_system: Option<EntitySystem<E, Ctx>>,
pub priorities: BTreeMap<i32, Vec<u32>>,
}
impl<E, Ctx> SystemStore<E, Ctx>
where
E: EntityStore,
{
pub fn new() -> Self {
SystemStore {
entity_systems: HashMap::new(),
init_system: None,
cleanup_system: None,
priorities: BTreeMap::new(),
}
}
pub fn register_init_system(&mut self, init_system: impl System<E, Ctx>) {
self.init_system = Some(EntitySystem::new(Box::new(init_system)));
}
pub fn register_cleanup_system(&mut self, cleanup_system: impl System<E, Ctx>) {
self.cleanup_system = Some(EntitySystem::new(Box::new(cleanup_system)));
}
pub fn register_system(&mut self, system: impl System<E, Ctx>, system_id: u32) {
self.entity_systems
.insert(system_id, EntitySystem::new(Box::new(system)));
}
pub fn remove_system(&mut self, system_id: u32) {
{
let system_to_remove = self.entity_systems.get(&system_id).unwrap();
self.priorities.remove(&system_to_remove.priority);
}
self.entity_systems.remove(&system_id);
}
pub fn register_priority(&mut self, priority: Priority, system_id: u32) {
self.entity_systems.get_mut(&system_id).unwrap().priority = priority;
self.priorities
.entry(priority)
.or_insert_with(Vec::new)
.push(system_id);
}
pub fn borrow_entity_system(
&self,
entity_system_id: u32,
) -> Result<&EntitySystem<E, Ctx>, NotFound> {
self.entity_systems
.get(&entity_system_id)
.map_or_else(|| Err(NotFound::EntitySystem(entity_system_id)), Ok)
}
pub fn borrow_init_system(&self) -> &Option<EntitySystem<E, Ctx>> {
&self.init_system
}
pub fn borrow_cleanup_system(&self) -> &Option<EntitySystem<E, Ctx>> {
&self.cleanup_system
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entity::VecEntityStore;
struct TestSystem;
impl System<VecEntityStore, PhantomContext> for TestSystem {}
#[test]
fn test_register_system() {
let mut esm = SystemStore::new();
esm.register_system(TestSystem, 0);
assert!(esm.entity_systems.contains_key(&0));
}
#[test]
fn test_register_init_system() {
let mut esm = SystemStore::new();
assert!(esm.init_system.is_none());
esm.register_init_system(TestSystem);
assert!(esm.init_system.is_some());
}
#[test]
fn test_register_cleanup_system() {
let mut esm = SystemStore::new();
assert!(esm.cleanup_system.is_none());
esm.register_cleanup_system(TestSystem);
assert!(esm.cleanup_system.is_some());
}
#[test]
fn test_remove_system() {
let mut esm = SystemStore::new();
esm.register_system(TestSystem, 0);
esm.remove_system(0);
assert!(!esm.entity_systems.contains_key(&0));
assert!(!esm.priorities.contains_key(&0));
}
#[test]
fn test_register_priority() {
let mut esm = SystemStore::new();
esm.register_system(TestSystem, 0);
esm.register_priority(5, 0);
assert_eq!(esm.entity_systems.get(&0).unwrap().priority, 5);
assert!(esm.priorities.contains_key(&5));
}
#[test]
fn test_borrow_init_entity_system() {
let mut esm = SystemStore::new();
esm.register_init_system(TestSystem);
assert!(esm.borrow_init_system().is_some());
}
#[test]
fn test_borrow_cleanup_entity_system() {
let mut esm = SystemStore::new();
esm.register_cleanup_system(TestSystem);
assert!(esm.borrow_cleanup_system().is_some());
}
#[test]
fn test_borrow_entity_system() {
let mut esm = SystemStore::new();
esm.register_system(TestSystem, 0);
assert!(esm.borrow_entity_system(0).is_ok());
}
#[test]
fn test_build() {
let mut esm = SystemStore::new();
esm.register_system(TestSystem, 0);
{
let esb = SystemStoreBuilder {
entity_system_id: 0,
system_store: &mut esm,
priority: Cell::new(0),
};
assert_eq!(esb.build(), 0);
}
}
}