mod container;
use std::{
any::TypeId,
vec::Vec,
collections::HashMap,
marker::PhantomData
};
use container::Container;
use crate::{
count,
ecs::{ Component, Entity },
recursive,
};
pub struct World {
content: Vec<Container>,
index: HashMap<Entity, usize>,
counter: u64,
}
impl World {
pub fn new() -> Self {
Self {
content: Vec::new(),
index: HashMap::new(),
counter: 0,
}
}
pub fn spawn<T, I>(&mut self, iter: I)
where
T: Archetype + Pattern,
I: IntoIterator<Item = T>
{
let (index, container) = if let Some((index, container)) = self.content
.iter_mut()
.enumerate()
.find(|(_, s)| T::matches(s) && s.len() == T::len() + 1) {
(index, container)
} else {
let container = Container::new::<T>();
let index = self.content.len();
self.content.push(container);
(index, self.content.last_mut().unwrap())
};
for entity in iter {
let entity_id = self.counter;
entity.store(container, entity_id);
self.index.insert(Entity::from(entity_id), index);
self.counter += 1;
}
}
pub fn query<'w, Q>(&'w self) -> impl Iterator<Item = <<Q as Query>::Iter as Iterator>::Item> + 'w
where
Q: Query<'w>,
{
let iter = self.content.iter()
.filter(|&container| Q::matches(container))
.map(|container| Q::select(container))
.flatten();
Matches {
iter,
}
}
pub fn exile(&mut self, entity: Entity) {
let index = if let Some(index) = self.index.get(&entity) {
*index
} else {
return;
};
if index >= self.content.len() {
return;
}
let entity_index = if let Some(entities) = self.content[index].get::<Entity>() {
if let Some((index, _)) = entities.iter()
.enumerate()
.find(|(_index, e)| **e == entity) {
index
} else {
return;
}
} else {
return;
};
self.content[index].remove(entity_index);
self.index.remove(&entity);
}
pub fn counter(&self) -> u64 {
self.counter
}
}
unsafe impl Send for World {}
unsafe impl Sync for World {}
impl Default for World {
fn default() -> Self {
Self::new()
}
}
pub trait Archetype {
fn store(self, container: &mut Container, entity_id: u64);
fn map(container: &mut Container);
}
pub trait Pattern {
fn len() -> usize;
fn matches(container: &Container) -> bool;
}
pub trait Query<'w> {
type Iter: Iterator + 'w;
fn select(container: &'w Container) -> Self::Iter;
fn matches(container: &'w Container) -> bool;
}
pub struct Matches<I> {
iter: I,
}
impl<'w, I> Iterator for Matches<I>
where
I: Iterator,
{
type Item = I::Item;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
pub struct Zipper<'w, T> {
tuple: T,
_phantom: PhantomData<&'w ()>,
}
pub trait Selector<'w> {
type Iter: Iterator;
type Component: Component;
fn borrow(container: &'w Container) -> Self::Iter;
fn matches(container: &'w Container) -> bool {
container.has(TypeId::of::<Self::Component>())
}
}
impl<'w, C> Selector<'w> for &'_ C
where
C: Component,
{
type Iter = std::slice::Iter<'w, C>;
type Component = C;
fn borrow(container: &'w Container) -> Self::Iter {
container.get::<C>().unwrap().iter()
}
}
impl<'w, C> Selector<'w> for &'_ mut C
where
C: Component,
{
type Iter = std::slice::IterMut<'w, C>;
type Component = C;
fn borrow(container: &'w Container) -> Self::Iter {
container.get_mut::<C>().unwrap().iter_mut()
}
}
#[macro_export]
macro_rules! impl_tuples {
($($i: ident),*) => {
impl<$($i),*> Archetype for ($($i,)*)
where
$(
$i: Component,
)*
{
#[allow(non_snake_case)]
fn store(self, container: &mut Container, entity_id: u64) {
let ($($i,)*) = self;
container.push::<Entity>(Entity::from(entity_id));
$(
container.push::<$i>($i);
)*
}
fn map(container: &mut Container) {
container.init::<Entity>();
$(
container.init::<$i>();
)*
}
}
impl<$($i),*> Pattern for ($($i,)*)
where
$($i: Component,)*
{
fn len() -> usize {
count!($($i,)*)
}
fn matches(container: &Container) -> bool {
$(container.has(TypeId::of::<$i>()))&&*
}
}
impl<'w, $($i),*> Query<'w> for ($($i,)*)
where
$($i: Selector<'w> + 'w,)*
{
type Iter = Zipper<'w, ($($i::Iter,)*)>;
fn select(container: &'w Container) -> Self::Iter {
Zipper {
tuple: ($({$i::borrow(container)},)*),
_phantom: PhantomData,
}
}
fn matches(container: &'w Container) -> bool
{
$(
$i::matches(container)
)&&*
}
}
#[allow(non_snake_case)]
impl<'w, $($i),*> Iterator for Zipper<'w, ($($i,)*)>
where
$($i: Iterator + 'w,)*
{
type Item = ($($i::Item,)*);
fn next(&mut self) -> Option<Self::Item> {
let ($(ref mut $i,)*) = self.tuple;
$(
let $i = match $i.next() {
None => return None,
Some(item) => item,
};
)*
Some(($($i,)*))
}
}
}
}
recursive!(impl_tuples, A, B, C, D, E, F, G, H);
#[cfg(test)]
mod tests {
use crate::ecs::Entity;
use super::World;
struct Armor(u32);
struct Health(u32);
struct Speed(u32);
struct Damage(u32);
struct Weight(u32);
fn spawn() -> World {
let mut world = World::new();
world.spawn(Some((Armor(100), Health(100), Damage(300))));
world.spawn(Some((Health(80), Speed(10))));
world.spawn(Some((Speed(50), Damage(45))));
world.spawn(Some((Damage(600), Armor(10))));
let bulk = (0..9).map(|_| (Speed(35), Weight(5000)));
world.spawn(bulk);
world
}
#[test]
fn spawn_and_query() {
let world = spawn();
let mut iter = world.query::<(&Armor, &Damage)>();
let item = iter.next();
assert_eq!(item.is_some(), true);
if let Some ((armor, damage)) = item {
assert_eq!(armor.0, 100); assert_eq!(damage.0, 300); }
let item = iter.next();
assert_eq!(item.is_some(), true);
let item = item.unwrap();
assert_eq!(item.0.0, 10); assert_eq!(item.1.0, 600);
let item = iter.next();
assert_eq!(item.is_some(), false);
}
#[test]
fn spawn_and_modify() {
let world = spawn();
{
let iter = world.query::<(&mut Speed,)>();
for (speed,) in iter {
speed.0 = 123;
}
}
{
let iter = world.query::<(&Speed,)>();
for (speed,) in iter {
assert_eq!(speed.0, 123);
}
}
}
#[test]
fn spawn_and_exile() {
let mut world = spawn();
{
let iter = world.query::<(&Entity, &mut Armor,)>();
let mut entity_to_delete = None;
let mut entities_before = 0;
for (entity, armor,) in iter {
if armor.0 == 100 {
entity_to_delete = Some(*entity);
}
entities_before += 1;
}
assert_eq!(entity_to_delete.is_some(), true);
world.exile(entity_to_delete.unwrap());
let iter = world.query::<(&Entity, &mut Armor,)>();
let mut entities_after = 0;
for (entity, _armor,) in iter {
assert_ne!(*entity, entity_to_delete.unwrap());
entities_after += 1;
}
assert_eq!(entities_before - 1, entities_after);
}
}
}