use std::any::{Any, TypeId};
use std::cell::OnceCell;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{LazyLock, Mutex, OnceLock};
use crate::entity::property_store::PropertyStore;
use crate::entity::{Entity, EntityId, PopulationIterator};
use crate::HashMap;
static NEXT_ENTITY_INDEX: Mutex<usize> = Mutex::new(0);
#[allow(clippy::type_complexity)]
static ENTITY_METADATA_BUILDER: LazyLock<Mutex<HashMap<TypeId, (Vec<TypeId>, Vec<TypeId>)>>> =
LazyLock::new(|| Mutex::new(HashMap::default()));
#[allow(clippy::type_complexity)]
static ENTITY_METADATA: OnceLock<HashMap<TypeId, (Box<[TypeId]>, Box<[TypeId]>)>> = OnceLock::new();
#[allow(clippy::type_complexity)]
fn entity_metadata() -> &'static HashMap<TypeId, (Box<[TypeId]>, Box<[TypeId]>)> {
ENTITY_METADATA.get_or_init(|| {
let mut builder = ENTITY_METADATA_BUILDER.lock().unwrap();
let builder = std::mem::take(&mut *builder);
builder
.into_iter()
.map(|(entity_type_id, (props, reqs))| {
(
entity_type_id,
(props.into_boxed_slice(), reqs.into_boxed_slice()),
)
})
.collect()
})
}
pub fn register_property_with_entity(
entity_type_id: TypeId,
property_type_id: TypeId,
required: bool,
) {
let mut builder = ENTITY_METADATA_BUILDER.lock().unwrap();
if ENTITY_METADATA.get().is_some() {
panic!(
"`register_property_with_entity()` called after entity metadata was frozen; registration must occur during startup/ctors."
);
}
let (property_type_ids, required_property_type_ids) = builder
.entry(entity_type_id)
.or_insert_with(|| (Vec::new(), Vec::new()));
property_type_ids.push(property_type_id);
if required {
required_property_type_ids.push(property_type_id);
}
}
#[must_use]
pub fn get_entity_metadata_static(
entity_type_id: TypeId,
) -> (&'static [TypeId], &'static [TypeId]) {
match entity_metadata().get(&entity_type_id) {
Some((props, reqs)) => (props.as_ref(), reqs.as_ref()),
None => (&[], &[]),
}
}
pub fn add_to_entity_registry<R: Entity>() {
let _ = R::id();
}
pub fn get_registered_entity_count() -> usize {
*NEXT_ENTITY_INDEX.lock().unwrap()
}
pub fn initialize_entity_index(plugin_index: &AtomicUsize) -> usize {
let mut guard = NEXT_ENTITY_INDEX.lock().unwrap();
let candidate = *guard;
match plugin_index.compare_exchange(usize::MAX, candidate, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => {
*guard += 1;
candidate
}
Err(existing) => {
existing
}
}
}
pub struct EntityRecord {
pub(crate) entity_count: usize,
pub(crate) entity: OnceCell<Box<dyn Any>>,
pub(crate) property_store: OnceCell<Box<dyn Any>>,
}
impl EntityRecord {
pub(crate) fn new() -> Self {
Self {
entity_count: 0,
entity: OnceCell::new(),
property_store: OnceCell::new(),
}
}
}
pub struct EntityStore {
items: Vec<EntityRecord>,
}
impl Default for EntityStore {
fn default() -> Self {
EntityStore::new()
}
}
impl EntityStore {
pub fn new() -> Self {
let num_items = get_registered_entity_count();
Self {
items: (0..num_items).map(|_| EntityRecord::new()).collect(),
}
}
#[must_use]
pub fn get<E: Entity>(&self) -> &E {
let index = E::id();
self.items
.get(index)
.unwrap_or_else(|| panic!("No registered entity found with index = {index:?}. You must use the `define_entity!` macro to create an entity."))
.entity
.get_or_init(|| E::new_boxed())
.downcast_ref::<E>()
.expect("TypeID does not match registered entity type. You must use the `define_entity!` macro to create an entity.")
}
#[must_use]
pub fn get_mut<E: Entity>(&mut self) -> &mut E {
let index = E::id();
let record = self.items.get_mut(index).unwrap_or_else(|| {
panic!(
"No registered entity found with index = {index:?}. \
You must use the `define_entity!` macro to create an entity."
)
});
if record.entity.get().is_none() {
record.entity.set(E::new_boxed()).unwrap();
}
record.entity.get_mut().unwrap().downcast_mut::<E>().expect(
"TypeID does not match registered entity type. \
You must use the `define_entity!` macro to create an entity.",
)
}
pub(crate) fn new_entity_id<E: Entity>(&mut self) -> EntityId<E> {
let index = E::id();
let record = &mut self.items[index];
let id = record.entity_count;
record.entity_count += 1;
EntityId::new(id)
}
#[must_use]
pub fn get_entity_count<E: Entity>(&self) -> usize {
let index = E::id();
let record = &self.items[index];
record.entity_count
}
#[must_use]
pub fn get_entity_count_by_id(&self, id: usize) -> usize {
let record = &self.items[id];
record.entity_count
}
pub fn get_entity_iterator<E: Entity>(&self) -> PopulationIterator<E> {
let count = self.get_entity_count::<E>();
PopulationIterator::new(count)
}
pub fn get_property_store<E: Entity>(&self) -> &PropertyStore<E> {
let index = E::id();
let record = self.items
.get(index)
.unwrap_or_else(|| panic!("No registered entity found with index = {index:?}. You must use the `define_entity!` macro to create an entity."));
let property_store = record
.property_store
.get_or_init(|| Box::new(PropertyStore::<E>::new()));
property_store.downcast_ref::<PropertyStore<E>>()
.expect("TypeID does not match registered item type. You must use the `define_registered_item!` macro to create a registered item.")
}
pub fn get_property_store_mut<E: Entity>(&mut self) -> &mut PropertyStore<E> {
let index = E::id();
let record = self.items
.get_mut(index)
.unwrap_or_else(|| panic!("No registered entity found with index = {index:?}. You must use the `define_entity!` macro to create an entity."));
let _ = record
.property_store
.get_or_init(|| Box::new(PropertyStore::<E>::new()));
let property_store = record.property_store.get_mut().unwrap();
property_store.downcast_mut::<PropertyStore<E>>()
.expect("TypeID does not match registered item type. You must use the `define_registered_item!` macro to create a registered item.")
}
}
#[cfg(test)]
mod tests {
use std::any::Any;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Barrier};
use std::thread;
use crate::entity::entity_store::{
add_to_entity_registry, get_registered_entity_count, initialize_entity_index, EntityStore,
};
use crate::entity::Entity;
use crate::{impl_entity, with, Context, ContextEntitiesExt, HashMap};
#[derive(Debug, Clone, PartialEq)]
pub struct TestItem1 {
value: usize,
}
impl Default for TestItem1 {
fn default() -> Self {
Self { value: 42 }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestItem2 {
name: String,
}
impl Default for TestItem2 {
fn default() -> Self {
TestItem2 {
name: "test".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestItem3 {
data: Vec<u8>,
}
impl Default for TestItem3 {
fn default() -> Self {
TestItem3 {
data: vec![1, 2, 3],
}
}
}
impl_entity!(TestItem1);
impl_entity!(TestItem2);
impl_entity!(TestItem3);
#[test]
fn test_initialize_item_index_concurrent() {
let initial_registered_items_count = get_registered_entity_count();
const NUM_THREADS: usize = 100;
let index = Arc::new(AtomicUsize::new(usize::MAX));
let barrier = Arc::new(Barrier::new(NUM_THREADS));
let handles: Vec<_> = (0..NUM_THREADS)
.map(|_| {
let index_clone = Arc::clone(&index);
let barrier_clone = Arc::clone(&barrier);
thread::spawn(move || {
barrier_clone.wait();
initialize_entity_index(&index_clone)
})
})
.collect();
let results: Vec<usize> = handles.into_iter().map(|h| h.join().unwrap()).collect();
let first = results[0];
assert_ne!(first, usize::MAX);
assert!(results.iter().all(|&r| r == first));
assert_eq!(first, initial_registered_items_count);
let initial_registered_items_count = get_registered_entity_count();
const NUM_ENTITIES: usize = 5;
let entities: Vec<_> = (0..NUM_ENTITIES)
.map(|_| Arc::new(AtomicUsize::new(usize::MAX)))
.collect();
let mut handles = vec![];
for entity in entities.iter() {
let entity_clone = Arc::clone(entity);
let handle = thread::spawn(move || initialize_entity_index(&entity_clone));
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
results.push(handle.join().unwrap());
}
results.sort();
for (i, &result) in results.iter().enumerate() {
assert_eq!(
result,
i + initial_registered_items_count,
"Entity should have index {}, got {}",
i,
result
);
}
let initial_registered_items_count = get_registered_entity_count();
let entity1 = Arc::new(AtomicUsize::new(usize::MAX));
let entity2 = Arc::new(AtomicUsize::new(usize::MAX));
let entity3 = Arc::new(AtomicUsize::new(usize::MAX));
let mut handles = vec![];
for _ in 0..5 {
let e1 = Arc::clone(&entity1);
handles.push(thread::spawn(move || initialize_entity_index(&e1)));
let e2 = Arc::clone(&entity2);
handles.push(thread::spawn(move || initialize_entity_index(&e2)));
let e3 = Arc::clone(&entity3);
handles.push(thread::spawn(move || initialize_entity_index(&e3)));
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
let mut counts = HashMap::default();
for &result in &results {
*counts.entry(result).or_insert(0) += 1;
}
assert_eq!(counts.len(), 3, "Should have 3 unique indices");
for (&idx, &count) in &counts {
assert_eq!(
count, 5,
"Index {} should appear 5 times, appeared {} times",
idx, count
);
}
assert_eq!(
get_registered_entity_count() - initial_registered_items_count,
3
);
let indices: Vec<_> = vec![
entity1.load(Ordering::Acquire),
entity2.load(Ordering::Acquire),
entity3.load(Ordering::Acquire),
];
let mut sorted_indices = indices.clone();
sorted_indices.sort_unstable();
let expected_indices = vec![
initial_registered_items_count,
1 + initial_registered_items_count,
2 + initial_registered_items_count,
];
assert_eq!(sorted_indices, expected_indices);
}
#[test]
fn test_add_to_registry_idempotent() {
let index1 = TestItem1::id();
let index2 = TestItem2::id();
let index3 = TestItem3::id();
assert_ne!(index1, usize::MAX);
assert_ne!(index2, usize::MAX);
assert_ne!(index3, usize::MAX);
assert_ne!(index1, index2);
assert_ne!(index2, index3);
assert_ne!(index1, index3);
add_to_entity_registry::<TestItem1>();
add_to_entity_registry::<TestItem1>();
add_to_entity_registry::<TestItem1>();
let index_from_registry_1 = TestItem1::id();
let index_from_registry_2 = TestItem2::id();
let index_from_registry_3 = TestItem3::id();
assert_eq!(index1, index_from_registry_1);
assert_eq!(index2, index_from_registry_2);
assert_eq!(index3, index_from_registry_3);
}
#[test]
fn test_registered_items_get() {
{
let mut items = EntityStore::new();
let item1 = items.get_mut::<TestItem1>();
assert_eq!(item1.value, 42);
assert_eq!(TestItem1::name(), "TestItem1");
let item2 = items.get_mut::<TestItem2>();
assert_eq!(item2.name, "test");
let item3 = items.get_mut::<TestItem3>();
assert_eq!(item3.data, vec![1, 2, 3]);
}
{
let items = EntityStore::new();
let item1 = items.get::<TestItem1>();
assert_eq!(item1.value, 42);
assert_eq!(TestItem1::name(), "TestItem1");
let item2 = items.get::<TestItem2>();
assert_eq!(item2.name, "test");
let item3 = items.get::<TestItem3>();
assert_eq!(item3.data, vec![1, 2, 3]);
}
}
#[test]
fn test_registered_items_get_cached() {
{
let items = EntityStore::new();
let item1_ref1 = items.get::<TestItem1>();
let item1_ref2 = items.get::<TestItem1>();
assert!(std::ptr::eq(item1_ref1, item1_ref2));
}
{
let mut items = EntityStore::new();
let item1_ptr1: *mut TestItem1 = items.get_mut::<TestItem1>();
let item1_ptr2: *mut TestItem1 = items.get_mut::<TestItem1>();
assert!(std::ptr::eq(item1_ptr1, item1_ptr2));
}
}
#[test]
fn test_registered_items_get_mut() {
let mut items = EntityStore::new();
let item = items.get_mut::<TestItem1>();
assert_eq!(item.value, 42);
item.value = 100;
let item = items.get::<TestItem1>();
assert_eq!(item.value, 100);
}
#[test]
fn test_registered_items_multiple_items_mutated() {
let mut items = EntityStore::new();
let item1 = items.get_mut::<TestItem1>();
assert_eq!(item1.value, 42);
item1.value = 10;
let item2 = items.get_mut::<TestItem2>();
assert_eq!(item2.name, "test");
item2.name = "modified".to_string();
let item3 = items.get_mut::<TestItem3>();
assert_eq!(item3.data, vec![1, 2, 3]);
item3.data = vec![9, 8, 7];
assert_eq!(items.get::<TestItem1>().value, 10);
assert_eq!(items.get::<TestItem2>().name, "modified");
assert_eq!(items.get::<TestItem3>().data, vec![9, 8, 7]);
}
#[test]
#[should_panic(expected = "No registered entity found with index")]
fn test_registered_items_invalid_index() {
#[derive(Debug, Default)]
struct UnregisteredEntity;
impl Entity for UnregisteredEntity {
fn name() -> &'static str
where
Self: Sized,
{
"UnregisteredItem"
}
fn id() -> usize
where
Self: Sized,
{
87000 }
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
let items = EntityStore::new();
let _ = items.get::<UnregisteredEntity>();
}
#[test]
fn test_registered_item_trait_name() {
assert_eq!(TestItem1::name(), "TestItem1");
assert_eq!(TestItem2::name(), "TestItem2");
assert_eq!(TestItem3::name(), "TestItem3");
}
#[test]
fn test_registered_item_new_boxed() {
let boxed1 = TestItem1::new_boxed();
assert_eq!(boxed1.value, 42);
let boxed2 = TestItem2::new_boxed();
assert_eq!(boxed2.name, "test");
let boxed3 = TestItem3::new_boxed();
assert_eq!(boxed3.data, vec![1, 2, 3]);
}
#[test]
fn test_box_dyn_registered_item_type_alias() {
let item = TestItem1::new_boxed();
assert_eq!(
(item as Box<dyn Any>)
.downcast_ref::<TestItem1>()
.unwrap()
.value,
42
);
}
#[test]
fn test_entity_iterator() {
let mut context = Context::new();
for _ in 0..5 {
context
.add_entity::<TestItem1, _>(with!(TestItem1))
.unwrap();
}
for _ in 0..3 {
context
.add_entity::<TestItem2, _>(with!(TestItem2))
.unwrap();
}
assert_eq!(context.get_entity_count::<TestItem1>(), 5);
assert_eq!(context.get_entity_count::<TestItem2>(), 3);
assert_eq!(context.get_entity_count::<TestItem3>(), 0);
let iter1 = context.get_entity_iterator::<TestItem1>();
let results1: Vec<_> = iter1.collect();
assert_eq!(results1.len(), 5);
for (i, id) in results1.into_iter().enumerate() {
assert_eq!(id.0, i);
}
let iter2 = context.get_entity_iterator::<TestItem2>();
assert_eq!(iter2.count(), 3);
let mut iter3 = context.get_entity_iterator::<TestItem3>();
assert!(iter3.next().is_none());
let snapshot_iter = context.get_entity_iterator::<TestItem1>();
context
.add_entity::<TestItem1, _>(with!(TestItem1))
.unwrap();
assert_eq!(context.get_entity_count::<TestItem1>(), 6);
assert_eq!(snapshot_iter.count(), 5); assert_eq!(context.get_entity_iterator::<TestItem1>().count(), 6); }
}