use std::any::{Any, TypeId};
use std::collections::HashMap;
use crate::world::Entity;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArchetypeId(Vec<TypeId>);
impl ArchetypeId {
fn from_sorted(mut types: Vec<TypeId>) -> Self {
types.sort();
Self(types)
}
#[must_use]
#[inline]
pub fn component_count(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn contains(&self, type_id: &TypeId) -> bool {
self.0.contains(type_id)
}
}
struct Column {
data: Vec<Box<dyn Any + Send + Sync>>,
}
impl Column {
fn new() -> Self {
Self { data: Vec::new() }
}
fn push(&mut self, value: Box<dyn Any + Send + Sync>) {
self.data.push(value);
}
fn get<T: 'static>(&self, row: usize) -> Option<&T> {
self.data.get(row)?.downcast_ref::<T>()
}
fn get_mut<T: 'static>(&mut self, row: usize) -> Option<&mut T> {
self.data.get_mut(row)?.downcast_mut::<T>()
}
fn swap_remove(&mut self, row: usize) -> Box<dyn Any + Send + Sync> {
self.data.swap_remove(row)
}
}
struct Archetype {
id: ArchetypeId,
entities: Vec<Entity>,
columns: HashMap<TypeId, Column>,
}
impl Archetype {
fn new(id: ArchetypeId) -> Self {
let mut columns = HashMap::new();
for &tid in &id.0 {
columns.insert(tid, Column::new());
}
Self {
id,
entities: Vec::new(),
columns,
}
}
fn len(&self) -> usize {
self.entities.len()
}
fn remove(&mut self, row: usize) -> Entity {
let entity = self.entities.swap_remove(row);
for column in self.columns.values_mut() {
column.swap_remove(row);
}
entity
}
}
pub struct ArchetypeStore {
archetypes: Vec<Archetype>,
id_to_index: HashMap<ArchetypeId, usize>,
entity_locations: HashMap<Entity, (usize, usize)>,
}
impl Default for ArchetypeStore {
fn default() -> Self {
Self::new()
}
}
impl ArchetypeStore {
pub fn new() -> Self {
Self {
archetypes: Vec::new(),
id_to_index: HashMap::new(),
entity_locations: HashMap::new(),
}
}
pub fn insert(
&mut self,
entity: Entity,
components: Vec<(TypeId, Box<dyn Any + Send + Sync>)>,
) {
let type_ids: Vec<TypeId> = components.iter().map(|(tid, _)| *tid).collect();
let arch_id = ArchetypeId::from_sorted(type_ids);
let arch_idx = if let Some(&idx) = self.id_to_index.get(&arch_id) {
idx
} else {
let idx = self.archetypes.len();
self.archetypes.push(Archetype::new(arch_id.clone()));
self.id_to_index.insert(arch_id, idx);
idx
};
let archetype = &mut self.archetypes[arch_idx];
let row = archetype.len();
archetype.entities.push(entity);
for (tid, value) in components {
if let Some(column) = archetype.columns.get_mut(&tid) {
column.push(value);
}
}
self.entity_locations.insert(entity, (arch_idx, row));
}
pub fn remove(&mut self, entity: Entity) -> bool {
let Some((arch_idx, row)) = self.entity_locations.remove(&entity) else {
return false;
};
let archetype = &mut self.archetypes[arch_idx];
archetype.remove(row);
if row < archetype.len() {
let moved_entity = archetype.entities[row];
self.entity_locations.insert(moved_entity, (arch_idx, row));
}
true
}
pub fn get<T: 'static + Send + Sync>(&self, entity: Entity) -> Option<&T> {
let &(arch_idx, row) = self.entity_locations.get(&entity)?;
let archetype = &self.archetypes[arch_idx];
let column = archetype.columns.get(&TypeId::of::<T>())?;
column.get::<T>(row)
}
pub fn get_mut<T: 'static + Send + Sync>(&mut self, entity: Entity) -> Option<&mut T> {
let &(arch_idx, row) = self.entity_locations.get(&entity)?;
let archetype = &mut self.archetypes[arch_idx];
let column = archetype.columns.get_mut(&TypeId::of::<T>())?;
column.get_mut::<T>(row)
}
pub fn query<A: 'static + Send + Sync>(&self) -> Vec<(Entity, &A)> {
let tid = TypeId::of::<A>();
let mut results = Vec::new();
for archetype in &self.archetypes {
if !archetype.id.contains(&tid) {
continue;
}
let column = match archetype.columns.get(&tid) {
Some(c) => c,
None => continue,
};
for (row, entity) in archetype.entities.iter().enumerate() {
if let Some(component) = column.get::<A>(row) {
results.push((*entity, component));
}
}
}
results
}
pub fn query2<A: 'static + Send + Sync, B: 'static + Send + Sync>(
&self,
) -> Vec<(Entity, &A, &B)> {
let tid_a = TypeId::of::<A>();
let tid_b = TypeId::of::<B>();
let mut results = Vec::new();
for archetype in &self.archetypes {
if !archetype.id.contains(&tid_a) || !archetype.id.contains(&tid_b) {
continue;
}
let col_a = match archetype.columns.get(&tid_a) {
Some(c) => c,
None => continue,
};
let col_b = match archetype.columns.get(&tid_b) {
Some(c) => c,
None => continue,
};
for (row, entity) in archetype.entities.iter().enumerate() {
if let (Some(a), Some(b)) = (col_a.get::<A>(row), col_b.get::<B>(row)) {
results.push((*entity, a, b));
}
}
}
results
}
#[must_use]
pub fn contains(&self, entity: Entity) -> bool {
self.entity_locations.contains_key(&entity)
}
#[must_use]
pub fn entity_count(&self) -> usize {
self.entity_locations.len()
}
#[must_use]
pub fn archetype_count(&self) -> usize {
self.archetypes.len()
}
#[must_use]
pub fn archetype_sizes(&self) -> Vec<usize> {
self.archetypes.iter().map(|a| a.len()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct Position(f32, f32);
#[derive(Debug, PartialEq)]
struct Velocity(f32, f32);
#[derive(Debug, PartialEq)]
struct Health(u32);
fn pos_component(x: f32, y: f32) -> (TypeId, Box<dyn Any + Send + Sync>) {
(TypeId::of::<Position>(), Box::new(Position(x, y)))
}
fn vel_component(x: f32, y: f32) -> (TypeId, Box<dyn Any + Send + Sync>) {
(TypeId::of::<Velocity>(), Box::new(Velocity(x, y)))
}
fn health_component(hp: u32) -> (TypeId, Box<dyn Any + Send + Sync>) {
(TypeId::of::<Health>(), Box::new(Health(hp)))
}
#[test]
fn store_new() {
let store = ArchetypeStore::new();
assert_eq!(store.entity_count(), 0);
assert_eq!(store.archetype_count(), 0);
}
#[test]
fn insert_and_get() {
let mut store = ArchetypeStore::new();
let e = Entity::new(0, 0);
store.insert(e, vec![pos_component(1.0, 2.0), vel_component(3.0, 4.0)]);
let pos = store.get::<Position>(e).unwrap();
assert_eq!(*pos, Position(1.0, 2.0));
let vel = store.get::<Velocity>(e).unwrap();
assert_eq!(*vel, Velocity(3.0, 4.0));
}
#[test]
fn same_archetype_grouped() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
store.insert(e1, vec![pos_component(1.0, 0.0), vel_component(0.0, 1.0)]);
store.insert(e2, vec![pos_component(2.0, 0.0), vel_component(0.0, 2.0)]);
assert_eq!(store.archetype_count(), 1); assert_eq!(store.entity_count(), 2);
}
#[test]
fn different_archetypes() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
store.insert(e1, vec![pos_component(1.0, 0.0)]);
store.insert(e2, vec![pos_component(2.0, 0.0), vel_component(0.0, 1.0)]);
assert_eq!(store.archetype_count(), 2);
}
#[test]
fn get_mut() {
let mut store = ArchetypeStore::new();
let e = Entity::new(0, 0);
store.insert(e, vec![pos_component(1.0, 2.0)]);
store.get_mut::<Position>(e).unwrap().0 = 99.0;
assert_eq!(store.get::<Position>(e).unwrap().0, 99.0);
}
#[test]
fn remove_entity() {
let mut store = ArchetypeStore::new();
let e = Entity::new(0, 0);
store.insert(e, vec![pos_component(1.0, 2.0)]);
assert!(store.contains(e));
store.remove(e);
assert!(!store.contains(e));
assert_eq!(store.entity_count(), 0);
}
#[test]
fn remove_with_swap() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
let e3 = Entity::new(2, 0);
store.insert(e1, vec![pos_component(1.0, 0.0)]);
store.insert(e2, vec![pos_component(2.0, 0.0)]);
store.insert(e3, vec![pos_component(3.0, 0.0)]);
store.remove(e1); assert_eq!(store.entity_count(), 2);
assert!(store.contains(e2));
assert!(store.contains(e3));
assert_eq!(store.get::<Position>(e3).unwrap().0, 3.0);
}
#[test]
fn query_single() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
let e3 = Entity::new(2, 0);
store.insert(e1, vec![pos_component(1.0, 0.0)]);
store.insert(e2, vec![pos_component(2.0, 0.0), vel_component(0.0, 1.0)]);
store.insert(e3, vec![health_component(100)]);
let positions = store.query::<Position>();
assert_eq!(positions.len(), 2); }
#[test]
fn query2_components() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
store.insert(e1, vec![pos_component(1.0, 0.0)]);
store.insert(e2, vec![pos_component(2.0, 0.0), vel_component(0.0, 1.0)]);
let results = store.query2::<Position, Velocity>();
assert_eq!(results.len(), 1); assert_eq!(results[0].1, &Position(2.0, 0.0));
}
#[test]
fn get_missing_component() {
let mut store = ArchetypeStore::new();
let e = Entity::new(0, 0);
store.insert(e, vec![pos_component(1.0, 0.0)]);
assert!(store.get::<Velocity>(e).is_none());
}
#[test]
fn get_missing_entity() {
let store = ArchetypeStore::new();
let e = Entity::new(99, 0);
assert!(store.get::<Position>(e).is_none());
}
#[test]
fn remove_missing_entity() {
let mut store = ArchetypeStore::new();
assert!(!store.remove(Entity::new(99, 0)));
}
#[test]
fn archetype_sizes() {
let mut store = ArchetypeStore::new();
for i in 0..5 {
store.insert(
Entity::new(i, 0),
vec![pos_component(i as f32, 0.0), vel_component(0.0, 0.0)],
);
}
for i in 5..8 {
store.insert(Entity::new(i, 0), vec![pos_component(i as f32, 0.0)]);
}
let sizes = store.archetype_sizes();
assert_eq!(sizes.len(), 2);
assert!(sizes.contains(&5));
assert!(sizes.contains(&3));
}
#[test]
fn archetype_id_order_independent() {
let mut store = ArchetypeStore::new();
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
store.insert(e1, vec![pos_component(1.0, 0.0), vel_component(0.0, 1.0)]);
store.insert(e2, vec![vel_component(0.0, 2.0), pos_component(2.0, 0.0)]);
assert_eq!(store.archetype_count(), 1); }
#[test]
fn store_as_world_resource() {
let mut world = crate::World::new();
world.insert_resource(ArchetypeStore::new());
let store = world.get_resource::<ArchetypeStore>().unwrap();
assert_eq!(store.entity_count(), 0);
}
#[test]
fn large_archetype_iteration() {
let mut store = ArchetypeStore::new();
for i in 0..1000u32 {
store.insert(
Entity::new(i, 0),
vec![pos_component(i as f32, 0.0), vel_component(1.0, 1.0)],
);
}
let results = store.query::<Position>();
assert_eq!(results.len(), 1000);
}
}