use std::cmp::{Eq, PartialEq};
use std::collections::VecDeque;
use std::hash::Hash;
const ENTITY_INDEX_BITS: u32 = 24;
const ENTITY_INDEX_MASK: u32 = (1<<ENTITY_INDEX_BITS)-1;
const ENTITY_GENERATION_BITS: u32 = 8;
const ENTITY_GENERATION_MASK: u32 = (1<<ENTITY_GENERATION_BITS)-1;
const ENTITY_MAX: u32 = std::u32::MAX>>8;
const MINIMUM_FREE_INDICES: usize = 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Entity(u32);
impl Default for Entity {
fn default() -> Self {
Entity::null()
}
}
impl std::fmt::Display for Entity {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.index_unchecked())
}
}
impl Entity {
pub fn null() -> Entity {
Entity(std::u32::MAX)
}
pub fn root() -> Entity {
Entity(0)
}
pub(crate) fn new(index: u32, generation: u32) -> Entity {
Entity(index | generation << ENTITY_INDEX_BITS)
}
pub fn is_null(&self) -> bool {
self.0 == std::u32::MAX
}
pub fn index(&self) -> Option<usize> {
if self.0 < std::u32::MAX {
Some((self.0 & ENTITY_INDEX_MASK) as usize)
} else {
None
}
}
pub fn generation(&self) -> Option<u8> {
if self.0 < std::u32::MAX {
Some(((self.0 >> ENTITY_INDEX_BITS) & ENTITY_GENERATION_MASK) as u8)
} else {
None
}
}
pub(crate) fn index_unchecked(&self) -> usize {
(self.0 & ENTITY_INDEX_MASK) as usize
}
}
pub(crate) struct EntityManager {
count: u32,
generation: Vec<u8>,
free_list: VecDeque<u32>,
}
impl EntityManager {
pub fn new() -> EntityManager {
EntityManager {
count: 0,
generation: Vec::new(),
free_list: VecDeque::with_capacity(MINIMUM_FREE_INDICES),
}
}
pub(crate) fn create_entity(&mut self) -> Option<Entity> {
let index = if self.free_list.len() > MINIMUM_FREE_INDICES {
self.free_list.pop_front()
} else {
self.generation.push(0);
let idx = (self.generation.len() - 1) as u32;
assert!((idx as u32) < ENTITY_MAX, "Entity index exceeds maximum allowed value");
Some(idx)
};
index.map(|idx| Entity::new(idx, self.generation[idx as usize] as u32))
}
pub fn is_alive(&self, entity: Entity) -> bool {
self.generation[entity.index_unchecked()] == entity.generation().unwrap()
}
pub fn destroy_entity(&mut self, entity: Entity) {
let index = entity.index_unchecked() as u32;
assert!(self.generation[index as usize] <= std::u8::MAX, "Entity generation exceeds maximum allowed value");
self.generation[index as usize] += 1;
self.free_list.push_back(index);
}
}
pub trait AsEntity {
fn entity(&self) -> Entity;
}
impl AsEntity for Entity {
fn entity(&self) -> Entity {
*self
}
}
impl AsEntity for (Entity, Entity) {
fn entity(&self) -> Entity {
self.0
}
}
impl AsEntity for (Entity, Entity, Entity) {
fn entity(&self) -> Entity {
self.0
}
}
impl AsEntity for (Entity, Entity, Entity, Entity) {
fn entity(&self) -> Entity {
self.0
}
}
impl AsEntity for (Entity, Entity, Entity, Entity, Entity) {
fn entity(&self) -> Entity {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create() {
let entity = Entity::new(42, 69);
assert_eq!(entity.index(), Some(42));
assert_eq!(entity.generation(), Some(69));
}
}