use crate::archetype::index::ArchetypeIndex;
use crate::archetype::{Archetype, ComponentInfo, EntityLocation};
use crate::component::Component;
use crate::entity::Entity;
use crate::storage::{StorageView, StorageViewMut};
use std::any::TypeId;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::RwLock;
pub mod hooks;
pub mod resources;
pub use self::hooks::*;
pub use self::resources::*;
pub use crate::entity::allocator::Entities;
pub struct World {
resources: HashMap<TypeId, RwLock<Box<dyn std::any::Any + Send + Sync>>>,
entity_locations: Vec<EntityLocation>,
pub(crate) archetype_index: ArchetypeIndex,
component_infos: HashMap<TypeId, ComponentInfo>,
pub(crate) component_hooks: HashMap<TypeId, ComponentHooks>,
despawn_hooks: Vec<DespawnHook>,
entities_to_despawn: Vec<Entity>,
is_despawning: bool,
pub tick: u32,
}
impl World {
pub fn new() -> Self {
let mut world = Self {
resources: HashMap::new(),
entity_locations: Vec::new(),
archetype_index: ArchetypeIndex::new(),
component_infos: HashMap::new(),
component_hooks: HashMap::new(),
despawn_hooks: Vec::new(),
entities_to_despawn: Vec::new(),
is_despawning: false,
tick: 1,
};
world.insert_resource(crate::commands::CommandQueue::new());
world.insert_resource(Entities::new());
world
}
pub fn increment_tick(&mut self) {
self.tick = self.tick.wrapping_add(1);
if self.tick == 0 {
self.tick = 1;
}
self.sort_archetype_hierarchy();
}
pub fn apply_commands(&mut self) {
let queue_opt = self
.get_resource::<crate::commands::CommandQueue>()
.map(|q| (*q).clone());
if let Some(queue) = queue_opt {
queue.apply(self);
}
}
}
impl Default for World {
fn default() -> Self {
Self::new()
}
}
impl World {
#[inline]
pub fn register_component_type<T: Component>(&mut self) {
let type_id = TypeId::of::<T>();
self.component_infos
.entry(type_id)
.or_insert_with(ComponentInfo::of::<T>);
}
#[inline]
pub fn is_component_registered<T: Component>(&self) -> bool {
self.component_infos.contains_key(&TypeId::of::<T>())
}
#[inline]
pub fn registered_component_count(&self) -> usize {
self.component_infos.len()
}
pub fn register_on_add<T: Component>(&mut self, hook: AddHook) {
self.component_hooks
.entry(TypeId::of::<T>())
.or_default()
.on_add
.push(hook);
}
pub fn register_on_remove<T: Component>(&mut self, hook: RemoveHook) {
self.component_hooks
.entry(TypeId::of::<T>())
.or_default()
.on_remove
.push(hook);
}
pub fn register_on_set<T: Component>(&mut self, hook: SetHook) {
self.component_hooks
.entry(TypeId::of::<T>())
.or_default()
.on_set
.push(hook);
}
pub fn spawn(&mut self) -> Entity {
let entity = {
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
entities.reserve_entity()
};
self.flush_spawn(entity);
entity
}
pub fn spawn_bundle<B: crate::component::Bundle>(&mut self, bundle: B) -> Entity {
let entity = self.spawn();
bundle.apply(self, entity);
entity
}
pub fn flush_spawn(&mut self, entity: Entity) {
self.archetype_index.on_spawn(entity.id());
let eid = entity.id();
let loc_idx = eid as usize;
let row = self.archetype_index.archetypes[0].len() as u32 - 1;
if loc_idx >= self.entity_locations.len() {
self.entity_locations
.resize(loc_idx + 1, EntityLocation::INVALID);
}
self.entity_locations[loc_idx] = EntityLocation {
archetype_id: 0,
row,
};
}
pub fn get_entity(&self, id: u32) -> Option<Entity> {
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
let state = entities.state.lock().expect("Entities mutex poisoned");
if (id as usize) < state.generations.len() && !state.free_set.contains(&id) {
return Some(Entity::new(id, state.generations[id as usize]));
}
None
}
pub fn clone_entity(&mut self, source_id: u32, count: usize) -> Option<Vec<Entity>> {
if count == 0 {
return Some(Vec::new());
}
let loc = self.entity_locations.get(source_id as usize).copied()?;
if !loc.is_valid() {
return None;
}
let arch_id = loc.archetype_id as usize;
let row = loc.row as usize;
let mut new_entities = Vec::with_capacity(count);
let mut new_eids = Vec::with_capacity(count);
{
let entities_res = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
for _ in 0..count {
let e = entities_res.reserve_entity();
new_eids.push(e.id());
new_entities.push(e);
}
}
let arch = &mut self.archetype_index.archetypes[arch_id];
let tick = self.tick;
let new_rows = unsafe { arch.batch_clone_row(row, count, &new_eids, tick) };
for (i, &id) in new_eids.iter().enumerate() {
let row = new_rows[i];
let idx = id as usize;
if idx >= self.entity_locations.len() {
self.entity_locations
.resize(idx + 1, EntityLocation::INVALID);
}
self.entity_locations[idx] = EntityLocation {
archetype_id: arch_id as u32,
row,
};
self.archetype_index.entity_archetype.insert(id, arch_id);
}
Some(new_entities)
}
pub fn register_despawn_hook(&mut self, hook: DespawnHook) {
self.despawn_hooks.push(hook);
}
pub fn despawn(&mut self, entity: Entity) {
self.entities_to_despawn.push(entity);
if self.is_despawning {
return;
}
self.is_despawning = true;
while let Some(e) = self.entities_to_despawn.pop() {
if !self.is_alive(e) {
continue;
}
let hooks = self.despawn_hooks.clone();
for hook in hooks {
hook(self, e);
}
let id = e.id();
let loc = self.entity_locations[id as usize];
if loc.is_valid() {
let comp_types = {
let arch = &self.archetype_index.archetypes[loc.archetype_id as usize];
arch.component_types()
};
for t in comp_types {
let c_hooks = self.component_hooks.get(&t).cloned();
if let Some(c_hooks) = c_hooks {
for hook in c_hooks.on_remove {
hook(self, e);
}
}
}
let loc = self.entity_locations[id as usize];
if loc.is_valid() {
if let Some(moved_eid) = self.archetype_index.archetypes
[loc.archetype_id as usize]
.swap_remove_entity(loc.row as usize)
{
self.entity_locations[moved_eid as usize].row = loc.row;
}
}
}
{
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
entities.free(e);
}
self.archetype_index.entity_archetype.remove(&id);
self.entity_locations[id as usize] = EntityLocation::INVALID;
}
self.is_despawning = false;
}
pub fn compact(&mut self) {
self.archetype_index
.gc_empty_archetypes(&mut self.entity_locations);
for arch in &mut self.archetype_index.archetypes {
arch.shrink_to_fit();
}
self.archetype_index.archetypes.shrink_to_fit();
self.entities_to_despawn.shrink_to_fit();
self.entity_locations.shrink_to_fit();
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
let mut state = entities.state.lock().expect("Entities mutex poisoned");
state.generations.shrink_to_fit();
state.free_ids.shrink_to_fit();
state.free_set.shrink_to_fit();
}
pub fn despawn_by_id(&mut self, id: u32) {
if let Some(entity) = self.get_entity(id) {
self.despawn(entity);
}
}
pub fn iter_alive_entities(&self) -> Vec<Entity> {
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
let state = entities.state.lock().expect("Entities mutex poisoned");
let mut alive = Vec::new();
for id in 0..state.next_entity_id {
if !state.free_set.contains(&id) {
alive.push(Entity::new(id, state.generations[id as usize]));
}
}
alive
}
#[inline]
pub fn is_alive(&self, entity: Entity) -> bool {
self.get_resource::<Entities>()
.expect("Entities resource not initialized")
.is_alive(entity)
}
pub fn add_component<T: Component>(&mut self, entity: Entity, component: T) {
if !self.is_alive(entity) {
return;
}
let eid = entity.id();
self.register_component_type::<T>();
let type_id = TypeId::of::<T>();
let target_arch_id =
match self
.archetype_index
.get_add_component_target(eid, type_id, &self.component_infos)
{
Some(id) => id,
None => return,
};
let old_loc = self.entity_locations[eid as usize];
if old_loc.archetype_id == target_arch_id as u32 {
{
let arch = &self.archetype_index.archetypes[target_arch_id];
let mut col = arch
.get_column_mut(type_id)
.expect("component column missing in current archetype");
unsafe {
let ptr = col.get_ptr(old_loc.row as usize) as *mut T;
*ptr = component;
col.ticks_ptr_mut()
.add(old_loc.row as usize)
.write(crate::archetype::ComponentTicks::new(self.tick));
}
}
let hooks = self.component_hooks.get(&type_id).cloned();
if let Some(hooks) = hooks {
for hook in hooks.on_set {
hook(self, entity);
}
}
return;
}
let (eid, old_arch_id, old_row) = (
entity.id(),
old_loc.archetype_id as usize,
old_loc.row as usize,
);
let (new_row, moved_eid) = unsafe {
let old_arch_ptr = &mut self.archetype_index.archetypes[old_arch_id] as *mut Archetype;
let target_arch_ptr =
&mut self.archetype_index.archetypes[target_arch_id] as *mut Archetype;
(&mut *old_arch_ptr).move_entity_to(old_row, &mut *target_arch_ptr)
};
if let Some(moved) = moved_eid {
self.entity_locations[moved as usize].row = old_row as u32;
}
{
let arch = &self.archetype_index.archetypes[target_arch_id];
let mut col = arch
.get_column_mut(type_id)
.expect("Mandatory component column missing");
unsafe {
let ptr = col.get_ptr(new_row as usize) as *mut T;
std::ptr::write(ptr, component);
col.ticks_ptr_mut()
.add(new_row as usize)
.write(crate::archetype::ComponentTicks::new(self.tick));
}
}
self.entity_locations[eid as usize] = EntityLocation {
archetype_id: target_arch_id as u32,
row: new_row,
};
self.archetype_index
.entity_archetype
.insert(eid, target_arch_id);
let hooks = self.component_hooks.get(&type_id).cloned();
if let Some(hooks) = hooks {
for hook in hooks.on_add {
hook(self, entity);
}
for hook in hooks.on_set {
hook(self, entity);
}
}
}
pub fn get_entity_component_types(&self, entity: Entity) -> Vec<TypeId> {
if !self.is_alive(entity) {
return Vec::new();
}
if let Some(&loc) = self.entity_locations.get(entity.id() as usize) {
if loc.is_valid() {
let arch = &self.archetype_index.archetypes[loc.archetype_id as usize];
return arch.component_types();
}
}
Vec::new()
}
pub fn get_component_ptr(&self, entity: Entity, type_id: TypeId) -> Option<*const u8> {
let loc = self.entity_locations.get(entity.id() as usize).copied()?;
if !loc.is_valid() {
return None;
}
let arch = &self.archetype_index.archetypes[loc.archetype_id as usize];
let col = arch.get_column(type_id)?;
Some(unsafe { col.get_ptr(loc.row as usize) })
}
pub fn remove_component<T: Component>(&mut self, entity: Entity) {
if !self.is_alive(entity) {
return;
}
let eid = entity.id();
let type_id = TypeId::of::<T>();
let old_loc = self.entity_locations[eid as usize];
let target_arch_id_opt =
self.archetype_index
.get_remove_component_target(eid, type_id, &self.component_infos);
let target_arch_id = match target_arch_id_opt {
Some(id) => id,
None => return, };
if old_loc.archetype_id == target_arch_id as u32 {
return; }
let (new_row, moved_eid) = unsafe {
let old_arch_ptr = &mut self.archetype_index.archetypes[old_loc.archetype_id as usize]
as *mut Archetype;
let target_arch_ptr =
&mut self.archetype_index.archetypes[target_arch_id] as *mut Archetype;
(&mut *old_arch_ptr).move_entity_to(old_loc.row as usize, &mut *target_arch_ptr)
};
if let Some(moved) = moved_eid {
self.entity_locations[moved as usize].row = old_loc.row;
}
self.entity_locations[eid as usize] = EntityLocation {
archetype_id: target_arch_id as u32,
row: new_row,
};
self.archetype_index
.entity_archetype
.insert(eid, target_arch_id);
let hooks = self.component_hooks.get(&type_id).cloned();
if let Some(hooks) = hooks {
for hook in hooks.on_remove {
hook(self, entity);
}
}
}
pub fn borrow<T: Component>(&self) -> StorageView<'_, T> {
let type_id = TypeId::of::<T>();
let mut matching = Vec::new();
let mut arch_id_to_idx = vec![None; self.archetype_index.archetypes.len()];
for arch in self.archetype_index.archetypes.iter() {
if let Some(col) = arch.get_column(type_id) {
arch_id_to_idx[arch.id as usize] = Some(matching.len());
matching.push((arch.entities(), col));
}
}
StorageView {
archetypes: matching,
arch_id_to_idx,
entity_locations: &self.entity_locations,
_marker: PhantomData,
}
}
pub fn borrow_mut<T: Component>(&self) -> StorageViewMut<'_, T> {
let type_id = TypeId::of::<T>();
let mut matching = Vec::new();
let mut arch_id_to_idx = vec![None; self.archetype_index.archetypes.len()];
for arch in self.archetype_index.archetypes.iter() {
if let Some(col) = arch.get_column_mut(type_id) {
arch_id_to_idx[arch.id as usize] = Some(matching.len());
matching.push((arch.entities(), col));
}
}
StorageViewMut {
archetypes: matching,
arch_id_to_idx,
entity_locations: &self.entity_locations,
_marker: PhantomData,
}
}
pub fn query<'w, Q: crate::query::WorldQuery>(&'w self) -> Option<crate::query::Query<'w, Q>> {
crate::query::Query::new(self)
}
pub fn query_cached<'w, Q: crate::query::WorldQuery>(
&'w mut self,
) -> Option<crate::query::Query<'w, Q>> {
crate::query::Query::new_cached(self)
}
pub fn query_entity_mut<'w, Q: crate::query::WorldQuery>(
&'w mut self,
entity_id: u32,
) -> Option<Q::Item<'w>> {
let loc = self.entity_location(entity_id);
if !loc.is_valid() {
return None;
}
let arch = &self.archetype_index.archetypes[loc.archetype_id as usize];
if !Q::matches_archetype(arch) {
return None;
}
unsafe {
let fetch = Q::fetch_raw(arch, self.tick)?;
if !Q::filter_row(fetch, loc.row as usize, self.tick) {
return None;
}
Some(Q::get_item(fetch, loc.row as usize))
}
}
pub fn query_entity<'w, Q: crate::query::WorldQuery>(
&'w self,
entity_id: u32,
) -> Option<Q::Item<'w>> {
let loc = self.entity_location(entity_id);
if !loc.is_valid() {
return None;
}
let arch = &self.archetype_index.archetypes[loc.archetype_id as usize];
if !Q::matches_archetype(arch) {
return None;
}
unsafe {
let fetch = Q::fetch_raw(arch, self.tick)?;
if !Q::filter_row(fetch, loc.row as usize, self.tick) {
return None;
}
Some(Q::get_item(fetch, loc.row as usize))
}
}
#[inline]
pub fn entity_location(&self, entity_id: u32) -> EntityLocation {
let loc_idx = entity_id as usize;
if loc_idx < self.entity_locations.len() {
self.entity_locations[loc_idx]
} else {
EntityLocation::INVALID
}
}
#[inline]
pub fn entity_count(&self) -> u32 {
let entities = self
.get_resource::<Entities>()
.expect("Entities resource not initialized");
let state = entities.state.lock().expect("Entities mutex poisoned");
state
.next_entity_id
.saturating_sub(state.free_ids.len() as u32)
}
pub fn insert_resource<T: Send + Sync + 'static>(&mut self, resource: T) {
let type_id = TypeId::of::<T>();
self.resources
.insert(type_id, RwLock::new(Box::new(resource)));
}
pub fn get_resource<T: 'static>(&self) -> Option<ResourceReadGuard<'_, T>> {
self.try_get_resource::<T>().ok()
}
pub fn get_resource_mut<T: 'static>(&self) -> Option<ResourceWriteGuard<'_, T>> {
self.try_get_resource_mut::<T>().ok()
}
pub fn try_get_resource<T: 'static>(
&self,
) -> Result<ResourceReadGuard<'_, T>, ResourceFetchError> {
let type_id = TypeId::of::<T>();
let storage = self
.resources
.get(&type_id)
.ok_or(ResourceFetchError::NotFound(type_id))?;
let guard = storage
.try_read()
.map_err(|_| ResourceFetchError::BorrowConflict(type_id))?;
Ok(ResourceReadGuard {
guard,
_marker: PhantomData,
})
}
pub fn try_get_resource_mut<T: 'static>(
&self,
) -> Result<ResourceWriteGuard<'_, T>, ResourceFetchError> {
let type_id = TypeId::of::<T>();
let storage = self
.resources
.get(&type_id)
.ok_or(ResourceFetchError::NotFound(type_id))?;
let guard = storage
.try_write()
.map_err(|_| ResourceFetchError::BorrowConflict(type_id))?;
Ok(ResourceWriteGuard {
guard,
_marker: PhantomData,
})
}
pub fn get_resource_mut_or_default<T: Default + Send + Sync + 'static>(
&mut self,
) -> ResourceWriteGuard<'_, T> {
let type_id = TypeId::of::<T>();
self.resources
.entry(type_id)
.or_insert_with(|| RwLock::new(Box::new(T::default())));
let storage = self
.resources
.get(&type_id)
.expect("resource just inserted");
let guard = storage.write().expect("resource write lock poisoned");
ResourceWriteGuard {
guard,
_marker: PhantomData,
}
}
pub fn remove_resource<T: 'static>(&mut self) -> Option<T> {
let type_id = TypeId::of::<T>();
let cell = self.resources.remove(&type_id)?;
let boxed_any = cell.into_inner().ok()?;
match boxed_any.downcast::<T>() {
Ok(boxed_t) => Some(*boxed_t),
Err(_) => None,
}
}
pub fn resource_scope<T: Send + Sync + 'static, U, F>(&mut self, f: F) -> Option<U>
where
F: FnOnce(&mut World, &mut T) -> U,
{
let resource = self.remove_resource::<T>()?;
struct Guard<'a, T: Send + Sync + 'static> {
world: *mut World,
resource: Option<T>,
_marker: std::marker::PhantomData<&'a mut World>,
}
impl<'a, T: Send + Sync + 'static> Drop for Guard<'a, T> {
fn drop(&mut self) {
if let Some(resource) = self.resource.take() {
unsafe { &mut *self.world }.insert_resource(resource);
}
}
}
let mut guard = Guard::<T> {
world: self as *mut World,
resource: Some(resource),
_marker: std::marker::PhantomData,
};
let result = f(self, guard.resource.as_mut().unwrap());
Some(result)
}
pub fn swap_archetype_rows(&mut self, arch_id: u32, row_a: usize, row_b: usize) {
if row_a == row_b {
return;
}
let arch = &self.archetype_index.archetypes[arch_id as usize];
if row_a >= arch.len() || row_b >= arch.len() {
return;
}
let entity_a = arch.entities()[row_a];
let entity_b = arch.entities()[row_b];
unsafe {
let mut_arch = &mut self.archetype_index.archetypes[arch_id as usize];
mut_arch.swap_rows(row_a, row_b);
}
self.entity_locations[entity_a as usize].row = row_b as u32;
self.entity_locations[entity_b as usize].row = row_a as u32;
}
pub fn sort_archetype_hierarchy(&mut self) {
let type_id = std::any::TypeId::of::<crate::component::Children>();
let mut arches_to_sort: Vec<usize> = Vec::new();
for (idx, arch) in self.archetype_index.archetypes.iter().enumerate() {
if arch.has_component(type_id) {
arches_to_sort.push(idx);
}
}
for arch_idx in arches_to_sort {
let arch_len = self.archetype_index.archetypes[arch_idx].len();
if arch_len <= 1 {
continue;
}
let mut visited = std::collections::HashSet::new();
for row in 0..arch_len {
let parent_entity_id = self.archetype_index.archetypes[arch_idx].entities()[row];
if visited.contains(&parent_entity_id) {
continue;
}
visited.insert(parent_entity_id);
let children_opt = {
let fetch = unsafe {
<&crate::component::Children as crate::query::FetchComponent>::fetch_raw(
&self.archetype_index.archetypes[arch_idx],
self.tick,
)
};
fetch.map(|f| unsafe {
<&crate::component::Children as crate::query::FetchComponent>::get_item(
f, row,
)
})
};
let children_list = match children_opt {
Some(c) => c.0.clone(),
None => continue,
};
let mut current_insert_row = row + 1;
for child_id in children_list {
let loc = self.entity_location(child_id);
if loc.is_valid() && loc.archetype_id == arch_idx as u32 {
let child_row = loc.row as usize;
if child_row > current_insert_row {
self.swap_archetype_rows(
arch_idx as u32,
current_insert_row,
child_row,
);
visited.insert(child_id);
current_insert_row += 1;
} else if child_row == current_insert_row {
visited.insert(child_id);
current_insert_row += 1;
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::component::Children;
#[derive(Clone, PartialEq, Debug)]
struct Transform(f32);
impl crate::component::Component for Transform {}
#[test]
fn test_sort_archetype_hierarchy() {
let mut world = World::new();
let e0 = world.spawn();
let e1 = world.spawn();
let e2 = world.spawn();
let e3 = world.spawn();
let e4 = world.spawn();
world.add_component(e0, Transform(0.0));
world.add_component(e1, Transform(1.0));
world.add_component(e2, Transform(2.0));
world.add_component(e3, Transform(3.0));
world.add_component(e4, Transform(4.0));
world.add_component(e0, Children(vec![e3.id(), e4.id()]));
world.add_component(e1, Children(vec![]));
world.add_component(e2, Children(vec![]));
world.add_component(e3, Children(vec![]));
world.add_component(e4, Children(vec![]));
world.sort_archetype_hierarchy();
let loc0 = world.entity_location(e0.id());
let loc3 = world.entity_location(e3.id());
let loc4 = world.entity_location(e4.id());
assert_eq!(
loc0.row + 1,
loc3.row,
"e3 (child), e0 (parent)'dan hemen sonra gelmeli"
);
assert_eq!(
loc0.row + 2,
loc4.row,
"e4 (child), e3'ten hemen sonra gelmeli"
);
let loc1 = world.entity_location(e1.id());
let loc2 = world.entity_location(e2.id());
assert!(
loc1.row > loc4.row || loc2.row > loc4.row,
"Bağımsız entityler sona itilmeli"
);
}
#[test]
fn test_sort_archetype_hierarchy_deep() {
let mut world = World::new();
let e0 = world.spawn();
let e1 = world.spawn();
let e2 = world.spawn();
let e3 = world.spawn();
world.add_component(e0, Transform(0.0));
world.add_component(e1, Transform(1.0));
world.add_component(e2, Transform(2.0));
world.add_component(e3, Transform(3.0));
world.add_component(e0, Children(vec![e1.id()]));
world.add_component(e1, Children(vec![e2.id()]));
world.add_component(e2, Children(vec![e3.id()]));
world.add_component(e3, Children(vec![]));
world.sort_archetype_hierarchy();
let l0 = world.entity_location(e0.id());
let l1 = world.entity_location(e1.id());
let l2 = world.entity_location(e2.id());
let l3 = world.entity_location(e3.id());
assert_eq!(l0.row + 1, l1.row);
assert_eq!(l1.row + 1, l2.row);
assert_eq!(l2.row + 1, l3.row);
}
}