use crate::{ archetype::{Archetype, ArchetypeId, Archetypes}, bundle::{Bundle, BundleInfo}, component::{Component, ComponentFlags, ComponentId, Components, StorageType}, entity::{Entity, EntityLocation}, storage::{SparseSet, Storages}, world::{Mut, World}, }; use std::any::TypeId; pub struct EntityRef<'w> { world: &'w World, entity: Entity, location: EntityLocation, } impl<'w> EntityRef<'w> { #[inline] pub(crate) fn new(world: &'w World, entity: Entity, location: EntityLocation) -> Self { Self { world, entity, location, } } #[inline] pub fn id(&self) -> Entity { self.entity } #[inline] pub fn location(&self) -> EntityLocation { self.location } #[inline] pub fn archetype(&self) -> &Archetype { // SAFE: EntityRefs always point to valid entities. Valid entities always have valid archetypes unsafe { self.world .archetypes .get_unchecked(self.location.archetype_id) } } #[inline] pub fn world(&mut self) -> &World { self.world } #[inline] pub fn contains(&self) -> bool { self.contains_type_id(TypeId::of::()) } #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { // SAFE: entity location is valid unsafe { contains_component_with_id(self.world, component_id, self.location) } } #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { // SAFE: entity location is valid unsafe { contains_component_with_type(self.world, type_id, self.location) } } #[inline] pub fn get(&self) -> Option<&'w T> { // SAFE: entity location is valid and returned component is of type T unsafe { get_component_with_type(self.world, TypeId::of::(), self.entity, self.location) .map(|value| &*value.cast::()) } } /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component #[inline] pub unsafe fn get_unchecked_mut(&self) -> Option> { get_component_and_flags_with_type(self.world, TypeId::of::(), self.entity, self.location) .map(|(value, flags)| Mut { value: &mut *value.cast::(), flags: &mut *flags, }) } } pub struct EntityMut<'w> { world: &'w mut World, entity: Entity, location: EntityLocation, } impl<'w> EntityMut<'w> { /// # Safety /// entity and location _must_ be valid #[inline] pub(crate) unsafe fn new( world: &'w mut World, entity: Entity, location: EntityLocation, ) -> Self { EntityMut { world, entity, location, } } #[inline] pub fn id(&self) -> Entity { self.entity } #[inline] pub fn location(&self) -> EntityLocation { self.location } #[inline] pub fn archetype(&self) -> &Archetype { // SAFE: EntityRefs always point to valid entities. Valid entities always have valid archetypes unsafe { self.world .archetypes .get_unchecked(self.location.archetype_id) } } #[inline] pub fn contains(&self) -> bool { self.contains_type_id(TypeId::of::()) } #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { // SAFE: entity location is valid unsafe { contains_component_with_id(self.world, component_id, self.location) } } #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { // SAFE: entity location is valid unsafe { contains_component_with_type(self.world, type_id, self.location) } } #[inline] pub fn get(&self) -> Option<&'w T> { // SAFE: entity location is valid and returned component is of type T unsafe { get_component_with_type(self.world, TypeId::of::(), self.entity, self.location) .map(|value| &*value.cast::()) } } #[inline] pub fn get_mut(&mut self) -> Option> { // SAFE: world access is unique, entity location is valid, and returned component is of type T unsafe { get_component_and_flags_with_type( self.world, TypeId::of::(), self.entity, self.location, ) .map(|(value, flags)| Mut { value: &mut *value.cast::(), flags: &mut *flags, }) } } /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component #[inline] pub unsafe fn get_unchecked_mut(&self) -> Option> { get_component_and_flags_with_type(self.world, TypeId::of::(), self.entity, self.location) .map(|(value, flags)| Mut { value: &mut *value.cast::(), flags: &mut *flags, }) } // TODO: factor out non-generic part to cut down on monomorphization (just check perf) // TODO: move relevant methods to World (add/remove bundle) pub fn insert_bundle(&mut self, bundle: T) -> &mut Self { let entity = self.entity; let entities = &mut self.world.entities; let archetypes = &mut self.world.archetypes; let components = &mut self.world.components; let storages = &mut self.world.storages; let bundle_info = self.world.bundles.init_info::(components); let current_location = self.location; let new_location = unsafe { // SAFE: component ids in `bundle_info` and self.location are valid let new_archetype_id = add_bundle_to_archetype( archetypes, storages, components, self.location.archetype_id, bundle_info, ); if new_archetype_id == current_location.archetype_id { current_location } else { let old_table_row; let old_table_id; { let old_archetype = archetypes.get_unchecked_mut(current_location.archetype_id); let result = old_archetype.swap_remove(current_location.index); if let Some(swapped_entity) = result.swapped_entity { // SAFE: entity is live and is contained in an archetype that exists entities.meta[swapped_entity.id as usize].location = current_location; } old_table_row = result.table_row; old_table_id = old_archetype.table_id() } let new_archetype = archetypes.get_unchecked_mut(new_archetype_id); if old_table_id == new_archetype.table_id() { new_archetype.allocate(entity, old_table_row) } else { let (old_table, new_table) = storages .tables .get_2_mut(old_table_id, new_archetype.table_id()); // PERF: store "non bundle" components in edge, then just move those to avoid redundant copies let move_result = old_table.move_to_superset_unchecked(old_table_row, new_table); let new_location = new_archetype.allocate(entity, move_result.new_row); // if an entity was moved into this entity's table spot, update its table row if let Some(swapped_entity) = move_result.swapped_entity { let swapped_location = entities.get(swapped_entity).unwrap(); // SAFE: entity is live and is therefore contained in an archetype that exists archetypes .get_unchecked_mut(swapped_location.archetype_id) .set_entity_table_row(swapped_location.index, old_table_row); } new_location } // Sparse set components are intentionally ignored here. They don't need to move } }; self.location = new_location; entities.meta[self.entity.id as usize].location = new_location; // SAFE: archetype was created if it didn't already exist let archetype = unsafe { archetypes.get_unchecked_mut(new_location.archetype_id) }; // SAFE: archetype tables always exists let table = unsafe { storages.tables.get_unchecked_mut(archetype.table_id()) }; let table_row = archetype.entity_table_row(new_location.index); let from_bundle = archetype.edges().get_from_bundle(bundle_info.id).unwrap(); // SAFE: table row is valid unsafe { bundle_info.write_components( &mut storages.sparse_sets, entity, table, table_row, &from_bundle.bundle_flags, bundle, ) }; self } pub fn remove_bundle(&mut self) -> Option { let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; let components = &mut self.world.components; let entities = &mut self.world.entities; let removed_components = &mut self.world.removed_components; let bundle_info = self.world.bundles.init_info::(components); let old_location = self.location; let new_archetype_id = unsafe { remove_bundle_from_archetype( archetypes, storages, components, old_location.archetype_id, bundle_info, false, )? }; if new_archetype_id == old_location.archetype_id { return None; } // SAFE: current entity archetype is valid let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) }; let mut bundle_components = bundle_info.component_ids.iter().cloned(); let entity = self.entity; // SAFE: bundle components are iterated in order, which guarantees that the component type matches let result = unsafe { T::from_components(|| { let component_id = bundle_components.next().unwrap(); // SAFE: entity location is valid and table row is removed below remove_component( components, storages, old_archetype, removed_components, component_id, entity, old_location, ) }) }; let remove_result = old_archetype.swap_remove(old_location.index); if let Some(swapped_entity) = remove_result.swapped_entity { entities.meta[swapped_entity.id as usize].location = old_location; } let old_table_row = remove_result.table_row; let old_table_id = old_archetype.table_id(); // SAFE: new archetype exists thanks to remove_bundle_from_archetype let new_archetype = unsafe { archetypes.get_unchecked_mut(new_archetype_id) }; let new_location = if old_table_id == new_archetype.table_id() { unsafe { new_archetype.allocate(entity, old_table_row) } } else { let (old_table, new_table) = storages .tables .get_2_mut(old_table_id, new_archetype.table_id()); // SAFE: table_row exists. All "missing" components have been extracted into the bundle above and the caller takes ownership let move_result = unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) }; // SAFE: new_table_row is a valid position in new_archetype's table let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; // if an entity was moved into this entity's table spot, update its table row if let Some(swapped_entity) = move_result.swapped_entity { let swapped_location = entities.get(swapped_entity).unwrap(); // SAFE: entity is live and is contained in an archetype that exists let archetype = unsafe { archetypes.get_unchecked_mut(swapped_location.archetype_id) }; archetype.set_entity_table_row(swapped_location.index, old_table_row); } new_location }; self.location = new_location; entities.meta[self.entity.id as usize].location = new_location; Some(result) } /// Remove any components in the bundle that the entity has. pub fn remove_bundle_intersection(&mut self) { let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; let components = &mut self.world.components; let entities = &mut self.world.entities; let removed_components = &mut self.world.removed_components; let bundle_info = self.world.bundles.init_info::(components); let old_location = self.location; let new_archetype_id = unsafe { remove_bundle_from_archetype( archetypes, storages, components, old_location.archetype_id, bundle_info, true, ) .expect("intersections should always return a result") }; if new_archetype_id == old_location.archetype_id { return; } // SAFE: current entity archetype is valid let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) }; let entity = self.entity; for component_id in bundle_info.component_ids.iter().cloned() { if old_archetype.contains(component_id) { // SAFE: entity location is valid and table row is removed below unsafe { remove_component( components, storages, old_archetype, removed_components, component_id, entity, old_location, ); } } } let remove_result = old_archetype.swap_remove(old_location.index); if let Some(swapped_entity) = remove_result.swapped_entity { entities.meta[swapped_entity.id as usize].location = old_location; } let old_table_row = remove_result.table_row; let old_table_id = old_archetype.table_id(); // SAFE: new archetype exists thanks to remove_bundle_from_archetype let new_archetype = unsafe { archetypes.get_unchecked_mut(new_archetype_id) }; let new_location = if old_table_id == new_archetype.table_id() { unsafe { new_archetype.allocate(entity, old_table_row) } } else { let (old_table, new_table) = storages .tables .get_2_mut(old_table_id, new_archetype.table_id()); // SAFE: table_row exists let move_result = unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) }; // SAFE: new_table_row is a valid position in new_archetype's table let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; // if an entity was moved into this entity's table spot, update its table row if let Some(swapped_entity) = move_result.swapped_entity { let swapped_location = entities.get(swapped_entity).unwrap(); // SAFE: entity is live and is contained in an archetype that exists let archetype = unsafe { archetypes.get_unchecked_mut(swapped_location.archetype_id) }; archetype.set_entity_table_row(swapped_location.index, old_table_row); } new_location }; self.location = new_location; entities.meta[self.entity.id as usize].location = new_location; } pub fn insert(&mut self, value: T) -> &mut Self { self.insert_bundle((value,)) } pub fn remove(&mut self) -> Option { self.remove_bundle::<(T,)>().map(|v| v.0) } pub fn despawn(self) { let world = self.world; world.flush(); let location = world .entities .free(self.entity) .expect("entity should exist at this point."); let table_row; let moved_entity; { // SAFE: entity is live and is contained in an archetype that exists let archetype = unsafe { world.archetypes.get_unchecked_mut(location.archetype_id) }; for component_id in archetype.components() { let removed_components = world .removed_components .get_or_insert_with(component_id, Vec::new); removed_components.push(self.entity); } let remove_result = archetype.swap_remove(location.index); if let Some(swapped_entity) = remove_result.swapped_entity { world.entities.meta[swapped_entity.id as usize].location = location; } table_row = remove_result.table_row; for component_id in archetype.sparse_set_components() { let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap(); sparse_set.remove(self.entity); } // SAFE: tables and table rows stored in archetypes always exist moved_entity = unsafe { world .storages .tables .get_unchecked_mut(archetype.table_id()) .swap_remove_unchecked(table_row) }; }; if let Some(moved_entity) = moved_entity { let moved_location = world.entities.get(moved_entity).unwrap(); // SAFE: entity is live and is contained in an archetype that exists let archetype = unsafe { world .archetypes .get_unchecked_mut(moved_location.archetype_id) }; archetype.set_entity_table_row(moved_location.index, table_row); } } #[inline] pub fn world(&mut self) -> &World { self.world } /// # Safety /// Caller must not modify the world in a way that changes the current entity's location /// If the caller _does_ do something that could change the location, self.update_location() must be /// called before using any other methods in EntityMut #[inline] pub unsafe fn world_mut(&mut self) -> &mut World { self.world } /// Updates the internal entity location to match the current location in the internal [World]. /// This is only needed if the user called [EntityMut::world], which enables the location to change. pub fn update_location(&mut self) { self.location = self.world.entities().get(self.entity).unwrap(); } } /// # Safety /// `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype #[inline] unsafe fn get_component( world: &World, component_id: ComponentId, entity: Entity, location: EntityLocation, ) -> Option<*mut u8> { let archetype = world.archetypes.get_unchecked(location.archetype_id); // SAFE: component_id exists and is therefore valid let component_info = world.components.get_info_unchecked(component_id); match component_info.storage_type() { StorageType::Table => { // SAFE: tables stored in archetype always exist let table = world.storages.tables.get_unchecked(archetype.table_id()); let components = table.get_column(component_id)?; let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T Some(components.get_unchecked(table_row)) } StorageType::SparseSet => world .storages .sparse_sets .get(component_id) .and_then(|sparse_set| sparse_set.get(entity)), } } /// # Safety /// Caller must ensure that `component_id` is valid #[inline] unsafe fn get_component_and_flags( world: &World, component_id: ComponentId, entity: Entity, location: EntityLocation, ) -> Option<(*mut u8, *mut ComponentFlags)> { let archetype = world.archetypes.get_unchecked(location.archetype_id); let component_info = world.components.get_info_unchecked(component_id); match component_info.storage_type() { StorageType::Table => { // SAFE: tables stored in archetype always exist let table = world.storages.tables.get_unchecked(archetype.table_id()); let components = table.get_column(component_id)?; let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T Some(( components.get_unchecked(table_row), components.get_flags_unchecked(table_row), )) } StorageType::SparseSet => world .storages .sparse_sets .get(component_id) .and_then(|sparse_set| sparse_set.get_with_flags(entity)), } } /// # Safety // `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype /// The relevant table row must be removed separately /// `component_id` must be valid #[inline] unsafe fn remove_component( components: &Components, storages: &mut Storages, archetype: &Archetype, removed_components: &mut SparseSet>, component_id: ComponentId, entity: Entity, location: EntityLocation, ) -> *mut u8 { let component_info = components.get_info_unchecked(component_id); let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); removed_components.push(entity); match component_info.storage_type() { StorageType::Table => { // SAFE: tables stored in archetype always exist let table = storages.tables.get_unchecked(archetype.table_id()); // SAFE: archetypes will always point to valid columns let components = table.get_column(component_id).unwrap(); let table_row = archetype.entity_table_row(location.index); // SAFE: archetypes only store valid table_rows and the stored component type is T components.get_unchecked(table_row) } StorageType::SparseSet => storages .sparse_sets .get_mut(component_id) .unwrap() .remove_and_forget(entity) .unwrap(), } } /// # Safety /// `entity_location` must be within bounds of an archetype that exists. unsafe fn get_component_with_type( world: &World, type_id: TypeId, entity: Entity, location: EntityLocation, ) -> Option<*mut u8> { let component_id = world.components.get_id(type_id)?; get_component(world, component_id, entity, location) } /// # Safety /// `entity_location` must be within bounds of an archetype that exists. pub(crate) unsafe fn get_component_and_flags_with_type( world: &World, type_id: TypeId, entity: Entity, location: EntityLocation, ) -> Option<(*mut u8, *mut ComponentFlags)> { let component_id = world.components.get_id(type_id)?; get_component_and_flags(world, component_id, entity, location) } /// # Safety /// `entity_location` must be within bounds of an archetype that exists. unsafe fn contains_component_with_type( world: &World, type_id: TypeId, location: EntityLocation, ) -> bool { if let Some(component_id) = world.components.get_id(type_id) { contains_component_with_id(world, component_id, location) } else { false } } /// # Safety /// `entity_location` must be within bounds of an archetype that exists. unsafe fn contains_component_with_id( world: &World, component_id: ComponentId, location: EntityLocation, ) -> bool { world .archetypes .get_unchecked(location.archetype_id) .contains(component_id) } /// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same [ArchetypeId], /// in the event that adding the given bundle does not result in an Archetype change. Results are cached in the /// Archetype Graph to avoid redundant work. /// # Safety /// `archetype_id` must exist and components in `bundle_info` must exist pub(crate) unsafe fn add_bundle_to_archetype( archetypes: &mut Archetypes, storages: &mut Storages, components: &mut Components, archetype_id: ArchetypeId, bundle_info: &BundleInfo, ) -> ArchetypeId { if let Some(archetype_id) = archetypes .get_unchecked(archetype_id) .edges() .get_add_bundle(bundle_info.id) { return archetype_id; } let mut new_table_components = Vec::new(); let mut new_sparse_set_components = Vec::new(); let mut tracking_flags = Vec::with_capacity(bundle_info.component_ids.len()); let current_archetype = archetypes.get_unchecked_mut(archetype_id); for component_id in bundle_info.component_ids.iter().cloned() { if current_archetype.contains(component_id) { tracking_flags.push(ComponentFlags::MUTATED); } else { tracking_flags.push(ComponentFlags::ADDED); let component_info = components.get_info_unchecked(component_id); match component_info.storage_type() { StorageType::Table => new_table_components.push(component_id), StorageType::SparseSet => { storages.sparse_sets.get_or_insert(component_info); new_sparse_set_components.push(component_id) } } } } if new_table_components.is_empty() && new_sparse_set_components.is_empty() { let edges = current_archetype.edges_mut(); // the archetype does not change when we add this bundle edges.set_add_bundle(bundle_info.id, archetype_id); edges.set_from_bundle(bundle_info.id, archetype_id, tracking_flags); archetype_id } else { let table_id; let table_components; let sparse_set_components; // the archetype changes when we add this bundle. prepare the new archetype and storages { let current_archetype = archetypes.get_unchecked_mut(archetype_id); table_components = if new_table_components.is_empty() { // if there are no new table components, we can keep using this table table_id = current_archetype.table_id(); current_archetype.table_components().to_vec() } else { new_table_components.extend(current_archetype.table_components()); // sort to ignore order while hashing new_table_components.sort(); // SAFE: all component ids in `new_table_components` exist table_id = storages .tables .get_id_or_insert(&new_table_components, components); new_table_components }; sparse_set_components = if new_sparse_set_components.is_empty() { current_archetype.sparse_set_components().to_vec() } else { new_sparse_set_components.extend(current_archetype.sparse_set_components()); // sort to ignore order while hashing new_sparse_set_components.sort(); new_sparse_set_components }; }; let new_archetype_id = archetypes.get_id_or_insert(table_id, table_components, sparse_set_components); // add an edge from the old archetype to the new archetype archetypes .get_unchecked_mut(archetype_id) .edges_mut() .set_add_bundle(bundle_info.id, new_archetype_id); // add a "from bundle" edge from the new archetype to the old archetype archetypes .get_unchecked_mut(new_archetype_id) .edges_mut() .set_from_bundle(bundle_info.id, new_archetype_id, tracking_flags); new_archetype_id } } /// Removes a bundle from the given archetype and returns the resulting archetype (or None if the removal was invalid). /// in the event that adding the given bundle does not result in an Archetype change. Results are cached in the /// Archetype Graph to avoid redundant work. /// if `intersection` is false, attempting to remove a bundle with components _not_ contained in the current archetype will fail, /// returning None. if `intersection` is true, components in the bundle but not in the current archetype will be ignored /// # Safety /// `archetype_id` must exist and components in `bundle_info` must exist unsafe fn remove_bundle_from_archetype( archetypes: &mut Archetypes, storages: &mut Storages, components: &mut Components, archetype_id: ArchetypeId, bundle_info: &BundleInfo, intersection: bool, ) -> Option { // check the archetype graph to see if the Bundle has been removed from this archetype in the past let remove_bundle_result = { // SAFE: entity location is valid and therefore the archetype exists let current_archetype = archetypes.get_unchecked_mut(archetype_id); if intersection { current_archetype .edges() .get_remove_bundle_intersection(bundle_info.id) } else { current_archetype.edges().get_remove_bundle(bundle_info.id) } }; let result = if let Some(result) = remove_bundle_result { // this Bundle removal result is cached. just return that! result } else { let mut next_table_components; let mut next_sparse_set_components; let next_table_id; { // SAFE: entity location is valid and therefore the archetype exists let current_archetype = archetypes.get_unchecked_mut(archetype_id); let mut removed_table_components = Vec::new(); let mut removed_sparse_set_components = Vec::new(); for component_id in bundle_info.component_ids.iter().cloned() { if current_archetype.contains(component_id) { // SAFE: bundle components were already initialized by bundles.get_info let component_info = components.get_info_unchecked(component_id); match component_info.storage_type() { StorageType::Table => removed_table_components.push(component_id), StorageType::SparseSet => removed_sparse_set_components.push(component_id), } } else if !intersection { // a component in the bundle was not present in the entity's archetype, so this removal is invalid // cache the result in the archetype graph current_archetype .edges_mut() .set_remove_bundle(bundle_info.id, None); return None; } } // sort removed components so we can do an efficient "sorted remove". archetype components are already sorted removed_table_components.sort(); removed_sparse_set_components.sort(); next_table_components = current_archetype.table_components().to_vec(); next_sparse_set_components = current_archetype.sparse_set_components().to_vec(); sorted_remove(&mut next_table_components, &removed_table_components); sorted_remove( &mut next_sparse_set_components, &removed_sparse_set_components, ); next_table_id = if removed_table_components.is_empty() { current_archetype.table_id() } else { // SAFE: all components in next_table_components exist storages .tables .get_id_or_insert(&next_table_components, components) }; } let new_archetype_id = archetypes.get_id_or_insert( next_table_id, next_table_components, next_sparse_set_components, ); Some(new_archetype_id) }; // SAFE: entity location is valid and therefore the archetype exists let current_archetype = archetypes.get_unchecked_mut(archetype_id); // cache the result in an edge if intersection { current_archetype .edges_mut() .set_remove_bundle_intersection(bundle_info.id, result); } else { current_archetype .edges_mut() .set_remove_bundle(bundle_info.id, result); } result } fn sorted_remove(source: &mut Vec, remove: &[T]) { let mut remove_index = 0; source.retain(|value| { while remove_index < remove.len() && *value > remove[remove_index] { remove_index += 1; } if remove_index < remove.len() { *value != remove[remove_index] } else { true } }) } #[cfg(test)] mod tests { #[test] fn sorted_remove() { let mut a = vec![1, 2, 3, 4, 5, 6, 7]; let b = vec![1, 2, 3, 5, 7]; super::sorted_remove(&mut a, &b); assert_eq!(a, vec![4, 6]); let mut a = vec![1]; let b = vec![1]; super::sorted_remove(&mut a, &b); assert_eq!(a, vec![]); let mut a = vec![1]; let b = vec![2]; super::sorted_remove(&mut a, &b); assert_eq!(a, vec![1]); } }