use parking_lot::RwLock;
use refslots::{Handle, Registry};
use slotmap::SecondaryMap;
use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::Deref,
sync::Arc,
};
slotmap::new_key_type! {
pub struct EntityId;
}
pub struct Entity<T> {
any: AnyEntity,
phantom: PhantomData<fn(T) -> T>,
}
impl<T: 'static> Clone for Entity<T> {
fn clone(&self) -> Self {
Self { any: self.any.clone(), phantom: PhantomData }
}
}
impl<T: 'static> Deref for Entity<T> {
type Target = AnyEntity;
fn deref(&self) -> &Self::Target {
&self.any
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AnyEntity(Handle<EntityId, TypeId>);
impl AnyEntity {
pub fn downcast<T: 'static>(self) -> Result<Entity<T>, Self> {
if *self.0.meta() != TypeId::of::<T>() {
return Err(self);
}
Ok(Entity { any: self, phantom: PhantomData })
}
pub fn id(&self) -> EntityId {
self.0.key()
}
}
#[derive(Default)]
pub struct EntityMap {
entities: SecondaryMap<EntityId, Box<dyn Any>>,
registry: Arc<RwLock<Registry<EntityId>>>,
}
impl EntityMap {
pub fn insert<T: 'static>(&mut self, value: T) -> Entity<T> {
let handle = Handle::insert(&self.registry, TypeId::of::<T>());
self.entities.insert(handle.key(), Box::new(value));
Entity { any: AnyEntity(handle), phantom: PhantomData }
}
pub fn get<T: 'static>(&self, entity: &Entity<T>) -> &T {
self.assert_valid_context(entity);
let Some(value) = self.entities.get(entity.id()) else {
panic!("entity: {:?} inaccessible", entity.id());
};
value.downcast_ref::<T>().unwrap()
}
pub fn take_released(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
let mut registry = self.registry.write();
let released = registry.take_released().filter_map(|id| {
Some((id, self.entities.remove(id)?))
});
released.collect()
}
fn assert_valid_context(&self, entity: &AnyEntity) {
assert!(entity.0.check_context(&Arc::downgrade(&self.registry)))
}
}
fn main() {
let mut map = EntityMap::default();
let mut entities = vec![map.insert(42_u8).any, map.insert("foo").any];
entities.push(map.insert(("bar", entities[1].clone())).any);
for any in entities.drain(..) {
if let Ok(entity) = any.clone().downcast::<u8>() {
println!("{:?} = {:?} :: u8", entity.id(), map.get(&entity));
}
if let Ok(entity) = any.clone().downcast::<&str>() {
println!("{:?} = {:?} :: str", entity.id(), map.get(&entity));
}
if let Ok(entity) = any.clone().downcast::<(&str, AnyEntity)>() {
let (value, inner) = map.get(&entity);
println!("{:?} = {:?} :: (str, AnyEntity)", entity.id(), (value, inner.id()));
}
}
loop {
let released = map.take_released();
if released.is_empty() {
break;
}
println!("dropped {} released slots", released.len());
}
}