#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
use std::any::type_name;
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
use std::collections::BTreeMap;
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
use std::panic::Location;
use std::{
any::Any,
cell::{Cell, Ref, RefCell, RefMut},
num::NonZeroU64,
};
use crate::{
query::Query,
scheduler::{Scheduler, SysId},
sparse_set::SparseSets,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Entity(NonZeroU64);
impl Entity {
pub fn id(&self) -> u64 {
self.0.get()
}
}
impl From<u64> for Entity {
fn from(value: u64) -> Self {
Self(NonZeroU64::new(value).unwrap())
}
}
struct EntityCounter(Cell<NonZeroU64>);
impl EntityCounter {
fn inc(&self) -> Entity {
let entity = self.0.get();
self.0.set(entity.checked_add(1).unwrap());
Entity(entity)
}
}
impl Default for EntityCounter {
fn default() -> Self {
Self(Cell::new(NonZeroU64::MIN))
}
}
#[derive(Default)]
pub struct World {
entities: EntityCounter,
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
dead_entity_locations: RefCell<BTreeMap<Entity, &'static Location<'static>>>,
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
dead_entity_components: RefCell<BTreeMap<Entity, String>>,
despawning: RefCell<Vec<Entity>>,
pub(crate) sparse_sets: SparseSets,
scheduler: Scheduler<'static, ()>,
}
impl World {
#[track_caller]
fn insert<C: Any>(&self, entity: Entity, component: C) {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
if let Some(components) = self.dead_entity_components.borrow().get(&entity) {
let loc = self.dead_entity_locations.borrow()[&entity];
panic!(
"Attaching `{}` to despawned entity (despawned at {loc}).Components at despawn time: {components}",
type_name::<C>(),
);
}
if let Some(mut set) = self.sparse_sets.get_mut::<C>() {
set.insert(entity, component);
} else {
self.sparse_sets.insert(entity, component);
}
}
#[track_caller]
pub fn spawn<C: AttachComponents>(&self, components: C) -> Entity {
components.attach_to(self, self.entities.inc())
}
#[track_caller]
pub fn despawn(&self, entity: Entity) {
self.despawning.borrow_mut().push(entity);
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
self.dead_entity_locations
.borrow_mut()
.insert(entity, Location::caller());
}
#[track_caller]
pub fn flush_despawned(&self) {
for entity in self.despawning.borrow_mut().drain(..) {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
let components = self.debug_components(entity);
self.detach_all(entity);
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
self.dead_entity_components
.borrow_mut()
.insert(entity, components);
}
}
pub(crate) fn is_despawning(&self, entity: Entity) -> bool {
self.despawning.borrow().contains(&entity)
}
#[track_caller]
pub fn attach<C: AttachComponents>(&self, entity: Entity, components: C) {
components.attach_to(self, entity);
}
#[track_caller]
pub fn detach<C: 'static>(&self, entity: Entity) -> Option<C> {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
if let Some(components) = self.dead_entity_components.borrow().get(&entity) {
let loc = self.dead_entity_locations.borrow()[&entity];
panic!(
"Detaching `{}` from despawned entity (despawned at {loc})\nComponents at despawn time: {components}",
type_name::<C>(),
);
}
let mut set = self.sparse_sets.get_mut::<C>()?;
set.remove(entity)
}
#[track_caller]
pub fn detach_all(&self, entity: Entity) {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
if let Some(components) = self.dead_entity_components.borrow().get(&entity) {
let loc = self.dead_entity_locations.borrow()[&entity];
panic!(
"Detaching all components from despawned entity (despawned at {loc})\nComponents at despawn time: {components}"
);
}
self.sparse_sets.remove(entity)
}
#[track_caller]
pub fn debug_components(&self, entity: Entity) -> String {
self.sparse_sets.debug(entity)
}
pub fn detach_any<C: 'static>(&self) {
if let Some(mut set) = self.sparse_sets.get_mut::<C>() {
set.clear();
}
}
pub fn is_attached<C: 'static>(&self, entity: Entity) -> bool {
self.sparse_sets
.get::<C>()
.is_some_and(|set| set.get(entity).is_some())
}
#[track_caller]
pub fn get<C: 'static>(&self, entity: Entity) -> Option<Ref<C>> {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
if let Some(components) = self.dead_entity_components.borrow().get(&entity) {
let loc = self.dead_entity_locations.borrow()[&entity];
panic!(
"Getting `{}` from despawned entity (despawned at {loc})\nComponents at despawn time: {components}",
type_name::<C>(),
);
}
let set = self.sparse_sets.get::<C>()?;
Ref::filter_map(set, |set| set.get(entity)).ok()
}
#[track_caller]
pub fn get_mut<C: 'static>(&self, entity: Entity) -> Option<RefMut<C>> {
#[cfg(any(debug_assertions, feature = "track_dead_entities"))]
if let Some(components) = self.dead_entity_components.borrow().get(&entity) {
let loc = self.dead_entity_locations.borrow()[&entity];
panic!(
"Getting `{}` from despawned entity (despawned at {loc})\nComponents at despawn time: {components}",
type_name::<C>(),
);
}
let set = self.sparse_sets.get_mut::<C>()?;
RefMut::filter_map(set, |set| set.get_mut(entity)).ok()
}
#[track_caller]
pub fn query<Q: Query<T>, T>(&self, f: Q) {
Q::get_components(self, f)
}
pub fn query_retain<C: 'static>(&self, mut f: impl for<'a> FnMut(Entity, &'a mut C) -> bool) {
let Some(mut set) = self.sparse_sets.get_mut::<C>() else {
return;
};
let set = &mut *set;
let mut idx = 0;
while let Some(entity) = set.ids.get(idx).copied() {
let retain = f(entity, &mut set.dense[idx]);
if retain {
idx += 1;
} else {
set.remove(entity);
}
}
}
pub fn add_system(&self, mut system: impl FnMut(&World) + 'static) -> SysId {
self.scheduler.register(move |world, _| system(world))
}
pub fn remove_system(&self, system: SysId) {
self.scheduler.deregister(system);
}
pub fn run_systems(&self) {
self.scheduler.run(self, &mut ());
}
}
pub trait AttachComponents {
fn attach_to(self, world: &World, entity: Entity) -> Entity;
}
macro_rules! impl_attach_components {
($($T:ident),+) => {
impl<$($T: Any),+> AttachComponents for ($($T,)+) {
#[track_caller]
fn attach_to(self, world: &World, entity: Entity) -> Entity {
#[allow(non_snake_case)]
let ($($T,)+) = self;
$(world.insert(entity, $T);)+
entity
}
}
};
}
impl_attach_components!(A);
impl_attach_components!(A, B);
impl_attach_components!(A, B, C);
impl_attach_components!(A, B, C, D);
impl_attach_components!(A, B, C, D, E);
impl_attach_components!(A, B, C, D, E, F);
impl_attach_components!(A, B, C, D, E, F, G);
impl_attach_components!(A, B, C, D, E, F, G, H);
impl_attach_components!(A, B, C, D, E, F, G, H, I);
impl_attach_components!(A, B, C, D, E, F, G, H, I, J);
impl_attach_components!(A, B, C, D, E, F, G, H, I, J, K);
impl_attach_components!(A, B, C, D, E, F, G, H, I, J, K, L);