use std::{any::TypeId, collections::HashMap, ops::Deref, ptr::NonNull}; use atomic_refcell::{AtomicRef, AtomicRefMut}; use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, Res, ResMut, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker, TrackedResource}; /// The id of the entity for the Archetype. /// /// The Archetype uses this as the index in the component columns #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ArchetypeEntityId(pub u64); impl Deref for ArchetypeEntityId { type Target = u64; fn deref(&self) -> &Self::Target { &self.0 } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Record { pub id: ArchetypeId, pub index: ArchetypeEntityId, } #[derive(Clone)] pub struct World { pub archetypes: HashMap, next_archetype_id: ArchetypeId, resources: HashMap, tracker: TickTracker, pub entities: Entities, } impl Default for World { fn default() -> Self { Self { archetypes: HashMap::new(), next_archetype_id: ArchetypeId(0), resources: HashMap::new(), tracker: TickTracker::new(), entities: Entities::default(), } } } impl World { pub fn new() -> Self { Self::default() } /// Reserves an entity in the world pub fn reserve_entity(&mut self) -> Entity { self.entities.reserve() } pub fn spawn(&mut self, bundle: B) -> Entity where B: Bundle { let new_entity = self.reserve_entity(); self.spawn_into(new_entity, bundle); new_entity } /// Spawn the components into a reserved entity. Only do this with entities that /// were reserved with [`World::reserve_entity`]. /// /// # Safety /// Do not use this method with an entity that is currently alive, it WILL cause undefined behavior. pub fn spawn_into(&mut self, entity: Entity, bundle: B) where B: Bundle { let tick = self.current_tick(); let bundle_types = bundle.type_ids(); // try to find an archetype let archetype = self.archetypes .values_mut() .find(|a| a.is_archetype_for(&bundle_types)); if let Some(archetype) = archetype { // make at just one check to ensure you're not spawning twice debug_assert!(!archetype.entity_ids.contains_key(&entity), "You attempted to spawn components into an entity that already exists!"); let arche_idx = archetype.add_entity(entity, bundle, &tick); // Create entity record and store it let record = Record { id: archetype.id(), index: arche_idx, }; self.entities.insert_entity_record(entity, record); } // create a new archetype if one isn't found else { // create archetype let new_arch_id = self.next_archetype_id.increment(); let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info()); let entity_arch_id = archetype.add_entity(entity, bundle, &tick); // store archetype self.archetypes.insert(new_arch_id, archetype); // Create entity record and store it let record = Record { id: new_arch_id, // this is the first entity in the archetype index: entity_arch_id, }; self.entities.insert_entity_record(entity, record); } } /// Despawn an entity from the World pub fn despawn(&mut self, entity: Entity) { let tick = self.current_tick(); if let Some(record) = self.entities.arch_index.get_mut(&entity.id) { let arch = self.archetypes.get_mut(&record.id).unwrap(); if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) { // replace the archetype index of the moved index with its new index. self.entities.arch_index.get_mut(&moved.id).unwrap().index = new_index; } } } /// Insert a component bundle into an existing entity. /// /// If the components are already existing on the entity, they will be updated, else the /// entity will be moved to a different Archetype that can store the entity. That may /// involve creating a new Archetype. pub fn insert(&mut self, entity: Entity, bundle: B) where B: Bundle { let tick = self.current_tick(); let record = self.entities.entity_record(entity); if record.is_none() { //let mut combined_column_infos: Vec = bundle.info().columns.iter().map(|c| c.info).collect(); let new_arch_id = self.next_archetype_id.increment(); let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info()); let mut dbun = DynamicBundle::new(); dbun.push_bundle(bundle); let entity_arch_id = archetype.add_entity(entity, dbun, &tick); self.archetypes.insert(new_arch_id, archetype); // Create entity record and store it let record = Record { id: new_arch_id, index: entity_arch_id, }; self.entities.insert_entity_record(entity, record); return; } let record = record.unwrap(); let current_arch = self.archetypes.get(&record.id).unwrap(); let current_arch_len = current_arch.len(); let mut contains_all = true; for id in bundle.type_ids() { contains_all = contains_all && current_arch.get_column(id).is_some(); } if contains_all { let current_arch = self.archetypes.get_mut(&record.id).unwrap(); let entry_idx = *current_arch.entity_indexes() .get(&entity).unwrap(); bundle.take(|ptr, id, _info| { let col = current_arch.get_column_mut(id).unwrap(); unsafe { col.set_at(entry_idx.0 as _, ptr, tick) }; }); return; } // contains the type ids for the old component columns + the ids for the new components let mut combined_column_types: Vec = current_arch.columns.iter().map(|c| c.info.type_id()).collect(); combined_column_types.extend(bundle.type_ids()); // contains the ComponentInfo for the old component columns + the info for the new components let mut combined_column_infos: Vec = current_arch.columns.iter().map(|c| c.info).collect(); combined_column_infos.extend(bundle.info()); // pointers only for the old columns let old_columns: Vec<(NonNull, ComponentInfo)> = current_arch.columns.iter() .map(|c| unsafe { (NonNull::new_unchecked(c.borrow_ptr().as_ptr()), c.info) }) .collect(); // try to find an archetype that this entity and its new components can fit into if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&combined_column_types)) { let mut dbun = DynamicBundle::new(); // move old entity components into new archetype columns for (col_ptr, col_info) in old_columns.into_iter() { unsafe { let ptr = NonNull::new_unchecked(col_ptr.as_ptr() .add(record.index.0 as usize * col_info.layout().size())); dbun.push_unknown(ptr, col_info); } } dbun.push_bundle(bundle); let res_index = arch.add_entity(entity, dbun, &tick); arch.ensure_synced(); let new_record = Record { id: arch.id(), index: res_index, }; self.entities.insert_entity_record(entity, new_record); } else { if current_arch_len == 1 { // if this entity is the only entity for this archetype, add more columns to it let current_arch = self.archetypes.get_mut(&record.id).unwrap(); current_arch.extend(&tick, vec![bundle]); return; } let new_arch_id = self.next_archetype_id.increment(); let mut archetype = Archetype::from_bundle_info(new_arch_id, combined_column_infos); let mut dbun = DynamicBundle::new(); for (column_ptr, column_info) in old_columns.into_iter() { unsafe { // ptr of component for the entity let comp_ptr = NonNull::new_unchecked(column_ptr.as_ptr() .add(record.index.0 as usize * column_info.layout().size())); dbun.push_unknown(comp_ptr, column_info); } } dbun.push_bundle(bundle); let entity_arch_id = archetype.add_entity(entity, dbun, &tick); self.archetypes.insert(new_arch_id, archetype); // Create entity record and store it let record = Record { id: new_arch_id, index: entity_arch_id, }; self.entities.insert_entity_record(entity, record); } let current_arch = self.archetypes.get_mut(&record.id).unwrap(); if let Some((en, enar)) = current_arch.remove_entity(entity, &tick) { let rec = Record { id: current_arch.id(), index: enar }; self.entities.insert_entity_record(en, rec); } current_arch.ensure_synced(); } /// A method used for debugging implementation details of the ECS. /// /// Here's an example of the output: /// ```nobuild /// Entities /// 1 in archetype 0 at 0 /// 0 in archetype 1 at 0 /// 2 in archetype 0 at 1 /// 3 in archetype 2 at 0 /// Arch 1 -- 1 entities /// Col 175564825027445222460146453544114453753 /// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0 /// Col 162279302565774655543278578489329315472 /// 0: 0 0 32 65 0 0 32 65 0 0 32 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0 /// Col 24291284537013640759061027938209843602 /// 0: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// Arch 2 -- 1 entities /// Col 175564825027445222460146453544114453753 /// 0: 0 0 0 0 0 0 0 0 0 0 0 0 237 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0 /// Col 162279302565774655543278578489329315472 /// 0: 0 0 76 66 0 0 170 66 0 0 136 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0 /// Col 142862377085187052737282554588643015580 /// 0: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// Arch 0 -- 2 entities /// Col 175564825027445222460146453544114453753 /// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0 /// 1: 0 0 0 0 0 0 0 0 0 0 0 0 237 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0 /// Col 162279302565774655543278578489329315472 /// 0: 0 0 112 65 0 0 112 65 0 0 112 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0 /// 1: 0 0 27 67 0 0 184 65 0 0 192 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0 /// Col 142862377085187052737282554588643015580 /// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// 1: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// Col 24291284537013640759061027938209843602 /// 0: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// 1: 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 /// Arch 3 -- 0 entities /// Col 175564825027445222460146453544114453753 /// Col 162279302565774655543278578489329315472 /// ``` /// /// This output prints all Entity ids, the archetype they're in, and the index that they're /// in inside of the archetype. Additionally, the archetypes are printing, including their /// columns type ids, and the contents of the type ids. This output can be used to debug /// the contents of entities inside the archetypes. /// /// Below is a template of the output: /// ```nobuild /// Entities /// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX% /// Arch ID -- %ARCHETYPE_LEN% entities /// %FOR EACH COL% /// Col COLUMN_COMPONENT_TYPE_ID /// %FOR EACH ENTITY% /// %ENTITY_INDEX%: %COMPONENT_BYTES% /// ``` /// If the template above doesn't help you in understanding the output, read the source code /// of the function. The source code is pretty simple. pub fn debug_print_world(&self) { println!("Entities"); for (en, rec) in &self.entities.arch_index { println!(" {} in archetype {} at {}", en.0, rec.id.0, rec.index.0); } for arch in self.archetypes.values() { println!("Arch {} -- {} entities", arch.id().0, arch.len()); for col in &arch.columns { // no clue if doing this is stable, but this is a debug function so :shrug: let tyid: u128 = unsafe { std::mem::transmute(col.info.type_id().as_rust()) }; println!(" Col {}", tyid); for en in 0..col.len { // get the ptr starting at the component let p = col.borrow_ptr(); let p = unsafe { p.as_ptr().add(en * col.info.layout().size()) }; print!(" {}: ", en); // print each byte of the component for i in 0..col.info.layout().size() { let d = unsafe { *p.add(i) }; print!("{} ", d); } println!(); } } } } pub fn entity_archetype(&self, entity: Entity) -> Option<&Archetype> { self.entities.entity_record(entity) .and_then(|record| self.archetypes.get(&record.id)) } pub fn entity_archetype_mut(&mut self, entity: Entity) -> Option<&mut Archetype> { self.entities.entity_record(entity) .and_then(|record| self.archetypes.get_mut(&record.id)) } /// View into the world for a set of entities that satisfy the queries. pub fn view(&self) -> ViewState { self.filtered_view::() } /// View into the world for a set of entities that satisfy the query and the filter. pub fn filtered_view(&self) -> ViewState { let archetypes = self.archetypes.values().collect(); ViewState::::new(self, Q::Query::new(), F::Filter::new(), archetypes) } /// View into the world for a set of entities that satisfy the queries. pub fn view_iter(&self) -> ViewIter { let archetypes = self.archetypes.values().collect(); let v = ViewState::new(self, Q::Query::new(), (), archetypes); v.into_iter() } /// View into the world for a set of entities that satisfy the queries. pub fn filtered_view_iter(&self) -> ViewIter { let archetypes = self.archetypes.values().collect(); let v = ViewState::new(self, Q::Query::new(), F::Filter::new(), archetypes); v.into_iter() } pub fn dynamic_view(&self) -> DynamicView { DynamicView::new(self) } pub fn view_one(&self, entity: Entity) -> ViewOne { ViewOne::new(self, entity.id, T::Query::new()) } /// Add a resource to the world. pub fn add_resource(&mut self, data: T) { self.resources.insert(TypeId::of::(), ResourceData::new(data, self.current_tick())); } /// Add the default value of a resource. /// /// > Note: This will replace existing values. pub fn add_resource_default(&mut self) { self.resources.insert(TypeId::of::(), ResourceData::new(T::default(), self.current_tick())); } /// Add the default value of a resource if it does not already exist. /// /// Returns a boolean indicating if the resource was added. pub fn add_resource_default_if_absent(&mut self) -> bool { let id = TypeId::of::(); if !self.resources.contains_key(&id) { self.resources.insert(id, ResourceData::new(T::default(), self.current_tick())); true } else { false } } /// Get a resource from the world, or insert it into the world with the provided /// `fn` and return it. pub fn get_resource_or_else(&mut self, f: F) -> ResMut where F: Fn() -> T + 'static { let tick = self.current_tick(); let res = self.resources.entry(TypeId::of::()) .or_insert_with(|| ResourceData::new(f(), tick)); ResMut { inner: res.data.borrow_mut(), world_tick: tick, _marker: std::marker::PhantomData::, } } /// Get a resource from the world, or insert its default value. pub fn get_resource_or_default(&mut self) -> ResMut { let tick = self.current_tick(); let res = self.resources.entry(TypeId::of::()) .or_insert_with(|| ResourceData::new(T::default(), tick)); ResMut { inner: res.data.borrow_mut(), world_tick: tick, _marker: std::marker::PhantomData::, } } /// Gets a resource from the World. pub fn get_resource(&self) -> Option> { self.get_tracked_resource::().map(|r| Res { inner: r, world_tick: self.current_tick(), _marker: std::marker::PhantomData::, }) } /// Get the tick of a resource. /// /// This tick represents the last time the resource was mutated. pub fn get_resource_tick(&self) -> Option { self.get_tracked_resource::().map(|r| r.tick) } /// Gets a reference to a change tracked resource. /// /// You will have to manually downcast the inner resource. Most people don't need this, see /// [`World::get_resource`]. pub fn get_tracked_resource(&self) -> Option>> { self.resources.get(&TypeId::of::()) .map(|r| r.data.borrow()) } /// Gets a mutable borrow to a change tracked resource. /// /// You will have to manually downcast the inner resource. Most people don't need this, see /// [`World::get_resource_mut`]. pub fn get_tracked_resource_mut(&self) -> Option>> { self.resources.get(&TypeId::of::()) .map(|r| r.data.borrow_mut()) } /// Returns a boolean indicating if the resource changed. /// /// This will return false if the resource doesn't exist. pub fn has_resource_changed(&self) -> bool { let tick = self.current_tick(); self.resources.get(&TypeId::of::()) .map(|r| r.changed(tick)) .unwrap_or(false) } /// Returns the [`Tick`] that the resource was last modified at. pub fn resource_tick(&self) -> Option { self.resources.get(&TypeId::of::()) .map(|r| r.data.borrow().tick) } /// Returns boolean indicating if the World contains a resource of type `T`. pub fn has_resource(&self) -> bool { self.resources.contains_key(&TypeId::of::()) } /// Gets a mutable borrow of a resource from the World. pub fn get_resource_mut(&self) -> Option> { self.get_tracked_resource_mut::().map(|r| ResMut { inner: r, world_tick: self.current_tick(), _marker: std::marker::PhantomData::, }) } /// Get the corresponding [`ResourceData`]. pub fn get_resource_data(&self) -> Option { self.resources.get(&TypeId::of::()) .map(|r| r.clone()) } /// Increments the world current tick for tracking changes to components and resources. /// /// # Note: /// For change tracking to work correctly, this must be ran each loop before you run world /// systems. pub fn tick(&self) -> Tick { self.tracker.tick() } /// Gets the current tick that the world is at. /// /// See [`World::tick`]. pub fn current_tick(&self) -> Tick { self.tracker.current() } pub fn tick_tracker(&self) -> &TickTracker { &self.tracker } /// Attempts to find a resource in the world and returns a NonNull pointer to it pub unsafe fn get_resource_ptr(&self) -> Option> { self.resources.get(&TypeId::of::()) .map(|d| unsafe { let data = d.data.borrow(); let ptr = NonNull::from(&data.res); NonNull::new_unchecked(ptr.as_ptr() as *mut T) }) } pub fn archetype_count(&self) -> usize { self.archetypes.len() } } // TODO: Ensure that all non-send resources are only accessible on the main thread. unsafe impl Send for World {} unsafe impl Sync for World {} #[cfg(test)] mod tests { use crate::{query::TickOf, tests::{Vec2, Vec3}, Entity}; use super::World; struct SimpleCounter(i32); #[test] fn spawning_entity() { let mut world = World::new(); let _e = world.spawn((Vec2 { x: 10.0, y: 15.0, }, )); } #[test] fn world_view_entities() { let mut world = World::new(); world.spawn((Vec2 { x: 10.0, y: 15.0, }, )); world.spawn((Vec2 { x: 152.0, y: 3585.0, }, )); world.spawn((Vec2 { x: 235.0, y: 734.0, }, )); let mut count = 0; for pos in world.view_iter::<&Vec2>() { println!("Found entity at {:?}", pos); count += 1; } assert!(count == 3); } #[test] fn despawn_entity() { let mut world = World::new(); world.spawn((Vec2::rand(),)); let middle_en = world.spawn((Vec2::rand(),)); let last_en = world.spawn((Vec2::rand(),)); world.despawn(middle_en); let record = world.entities.entity_record(last_en).unwrap(); assert_eq!(record.index.0, 1); } #[test] fn simple_resource() { let mut world = World::new(); { let counter = SimpleCounter(0); world.add_resource(counter); } let counter = world.get_resource::() .expect("Counter resource is missing"); assert_eq!(counter.0, 0); drop(counter); let mut counter = world.get_resource_mut::() .expect("Counter resource is missing"); counter.0 += 4582; drop(counter); assert!(world.get_resource::().is_none()); } #[test] fn resource_multi_borrow() { let mut world = World::new(); let counter = SimpleCounter(4582); world.add_resource(counter); // test multiple borrows at the same time let counter = world.get_resource::() .expect("Counter resource is missing"); assert_eq!(counter.0, 4582); let counter2 = world.get_resource::() .expect("Counter resource is missing"); assert_eq!(counter.0, 4582); assert_eq!(counter2.0, 4582); } #[test] fn resource_one_mutable_borrow() { let mut world = World::new(); { let counter = SimpleCounter(4582); world.add_resource(counter); } // test that its only possible to get a single mutable borrow let counter = world.get_resource_mut::() .expect("Counter resource is missing"); assert_eq!(counter.0, 4582); assert!(world.get_resource_mut::().is_none()); assert_eq!(counter.0, 4582); } #[test] fn insert_into_existing_archetype() { let mut world = World::new(); let e = world.spawn((Vec2::rand(),)); world.spawn((Vec2::rand(),Vec3::rand())); world.insert(e, (Vec3::rand(),)); assert!(world.view_one::<&Vec3>(e).get().is_some()) } #[test] fn insert_into_new_archetype() { let mut world = World::new(); let e = world.spawn((Vec2::rand(),)); world.insert(e, (Vec3::rand(),)); assert!(world.view_one::<&Vec3>(e).get().is_some()) } #[test] fn insert_multiple_times() { let v2s = &[Vec2::rand(), Vec2::rand(), Vec2::rand()]; let v3s = &[Vec3::rand(), Vec3::rand(), Vec3::rand()]; let mut world = World::new(); let e1 = world.spawn(v2s[0]); let e2 = world.spawn(v2s[1]); let e3 = world.spawn(v2s[2]); println!("Spawned entities"); let ev2 = world.view_one::<&Vec2>(e2).get() .expect("Failed to find Vec2 and Vec3 on inserted entity!"); assert_eq!(*ev2, v2s[1]); drop(ev2); let insert_and_assert = |world: &mut World, e: Entity, v2: Vec2, v3: Vec3| { println!("inserting entity"); world.insert(e, (v3,)); println!("inserted entity"); let (ev2, ev3) = world.view_one::<(&Vec2, &Vec3)>(e).get() .expect("Failed to find Vec2 and Vec3 on inserted entity!"); assert_eq!(*ev2, v2); assert_eq!(*ev3, v3); }; insert_and_assert(&mut world, e2, v2s[1], v3s[1]); println!("Entity 2 is good"); insert_and_assert(&mut world, e3, v2s[2], v3s[2]); println!("Entity 3 is good"); assert_eq!(world.archetypes.len(), 2); println!("No extra archetypes were created"); insert_and_assert(&mut world, e1, v2s[0], v3s[0]); println!("Entity 1 is good"); } #[test] fn view_one() { let v = Vec2::rand(); let mut world = World::new(); let e = world.spawn((v,)); let view = world.view_one::<&Vec2>(e); assert_eq!(*view.get().unwrap(), v); } #[test] fn view_change_tracking() { let mut world = World::new(); println!("spawning"); world.spawn((Vec2::new(10.0, 10.0),)); world.spawn((Vec2::new(5.0, 5.0),)); println!("spawned"); for mut v in world.view_iter::<&mut Vec2>() { v.y += 50.0; println!("Moved v to {:?}", v); } let world_tick = world.current_tick(); println!("The world tick is {}", *world_tick); for (v, tick) in world.view_iter::<(&Vec2, TickOf)>() { println!("Is at {:?}, it was changed at {}", v, *tick); assert!(v.y > 50.0); assert!(tick >= world_tick); } } /// Tests replacing components using World::insert #[test] fn entity_insert_replace() { let mut world = World::new(); let first = world.spawn((Vec2::new(10.0, 10.0),)); let second = world.spawn((Vec2::new(5.0, 5.0),)); world.insert(first, Vec2::new(50.0, 50.0)); let pos = world.view_one::<&mut Vec2>(first).get().unwrap(); assert_eq!(*pos, Vec2::new(50.0, 50.0)); drop(pos); let pos = world.view_one::<&mut Vec2>(second).get().unwrap(); assert_eq!(*pos, Vec2::new(5.0, 5.0)); } /// Tests resource change checks #[test] fn resource_changed() { let mut world = World::new(); world.add_resource(SimpleCounter(50)); assert!(world.has_resource_changed::()); world.spawn(Vec2::new(50.0, 50.0)); assert!(!world.has_resource_changed::()); let mut counter = world.get_resource_mut::() .expect("Counter resource is missing"); counter.0 += 100; assert!(world.has_resource_changed::()); } }