diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c2928aa93dd51..f21675103fb34 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4,10 +4,9 @@ use crate::{ change_detection::{MutUntyped, TicksMut}, component::{ Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, - TickCells, }, entity::{Entities, Entity, EntityLocation}, - storage::{Column, ComponentSparseSet, SparseSet, Storages}, + storage::{SparseSet, Storages}, world::{Mut, World}, }; use bevy_ptr::{OwningPtr, Ptr}; @@ -72,17 +71,18 @@ impl<'w> EntityRef<'w> { pub fn get(&self) -> Option<&'w T> { // SAFETY: // - entity location and entity is valid - // - returned component is of type T // - the storage type provided is correct for T + // - world access is immutable, lifetime tied to `&self` unsafe { - get_component_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|value| value.deref::()) + self.world + .get_component_with_type( + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + // SAFETY: returned component is of type T + .map(|value| value.deref::()) } } @@ -92,10 +92,10 @@ impl<'w> EntityRef<'w> { pub fn get_change_ticks(&self) -> Option { // SAFETY: // - entity location and entity is valid + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_ticks_with_type( - self.world, + self.world.get_ticks_with_type( TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -112,15 +112,13 @@ impl<'w> EntityRef<'w> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - if !self.contains_id(component_id) { - return None; - } - let info = self.world.components().get_info(component_id)?; - // SAFETY: Entity location is valid and component_id exists. + // SAFETY: + // - entity location and entity is valid + // - world access is immutable, lifetime tied to `&self` + // - the storage type provided is correct for T unsafe { - get_ticks( - self.world, + self.world.get_ticks( component_id, info.storage_type(), self.entity, @@ -149,20 +147,20 @@ impl<'w> EntityRef<'w> { // - entity location and entity is valid // - returned component is of type T // - the storage type provided is correct for T - get_component_and_ticks_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|(value, ticks)| Mut { - // SAFETY: - // - returned component is of type T - // - Caller guarantees that this reference will not alias. - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), - }) + self.world + .get_component_and_ticks_with_type( + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + // SAFETY: + // - returned component is of type T + // - Caller guarantees that this reference will not alias. + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), + }) } } @@ -179,12 +177,11 @@ impl<'w> EntityRef<'w> { pub fn get_by_id(&self, component_id: ComponentId) -> Option> { let info = self.world.components().get_info(component_id)?; // SAFETY: - // - entity_location is valid, - // - component_id is valid as checked by the line above + // - entity_location and entity are valid + // . component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self.world, + self.world.get_component( component_id, info.storage_type(), self.entity, @@ -257,19 +254,19 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get(&self) -> Option<&'_ T> { // SAFETY: - // - lifetimes enforce correct usage of returned borrow - // - entity location and entity is valid - // - returned component is of type T + // - entity location is valid + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_component_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|value| value.deref::()) + self.world + .get_component_with_type( + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + // SAFETY: returned component is of type T + .map(|value| value.deref::()) } } @@ -284,11 +281,11 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get_change_ticks(&self) -> Option { // SAFETY: - // - entity location and entity is valid + // - entity location is valid + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_ticks_with_type( - self.world, + self.world.get_ticks_with_type( TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -305,15 +302,13 @@ impl<'w> EntityMut<'w> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - if !self.contains_id(component_id) { - return None; - } - let info = self.world.components().get_info(component_id)?; - // SAFETY: Entity location is valid and component_id exists. + // SAFETY: + // - entity location is valid + // - world access is immutable, lifetime tied to `&self` + // - the storage type provided is correct for T unsafe { - get_ticks( - self.world, + self.world.get_ticks( component_id, info.storage_type(), self.entity, @@ -338,21 +333,21 @@ impl<'w> EntityMut<'w> { // - entity location and entity is valid // - returned component is of type T // - the storage type provided is correct for T - get_component_and_ticks_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|(value, ticks)| Mut { - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells( - ticks, - self.world.last_change_tick(), - self.world.read_change_tick(), - ), - }) + self.world + .get_component_and_ticks_with_type( + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells( + ticks, + self.world.last_change_tick(), + self.world.read_change_tick(), + ), + }) } /// Adds a [`Bundle`] of components to the entity. @@ -417,10 +412,13 @@ impl<'w> EntityMut<'w> { let result = unsafe { T::from_components(storages, &mut |storages| { let component_id = bundle_components.next().unwrap(); - // SAFETY: entity location is valid and table row is removed below + // SAFETY: + // - entity location is valid + // - table row is removed below, without dropping the contents + // - `components` comes from the same world as `storages` take_component( - components, storages, + components, removed_components, component_id, entity, @@ -671,8 +669,7 @@ impl<'w> EntityMut<'w> { // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self.world, + self.world.get_component( component_id, info.storage_type(), self.entity, @@ -697,213 +694,6 @@ impl<'w> EntityMut<'w> { } } -#[inline] -fn fetch_table( - world: &World, - location: EntityLocation, - component_id: ComponentId, -) -> Option<&Column> { - world.storages.tables[location.table_id].get_column(component_id) -} - -#[inline] -fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> { - world.storages.sparse_sets.get(component_id) -} - -// TODO: move to Storages? -/// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside -/// the archetype and table -/// - `component_id` must be valid -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -pub(crate) unsafe fn get_component( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option> { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(components.get_data_unchecked(location.table_row)) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get(entity), - } -} - -// TODO: move to Storages? -/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] on a particular [`Entity`] in the provided [World]. -/// -/// # Safety -/// - Caller must ensure that `component_id` is valid -/// - `location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_and_ticks( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option<(Ptr<'_>, TickCells<'_>)> { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(( - components.get_data_unchecked(location.table_row), - TickCells { - added: components.get_added_ticks_unchecked(location.table_row), - changed: components.get_changed_ticks_unchecked(location.table_row), - }, - )) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_with_ticks(entity), - } -} - -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `component_id` must be valid -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -unsafe fn get_ticks( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(components.get_ticks_unchecked(location.table_row)) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_ticks(entity), - } -} - -// TODO: move to Storages? -/// Moves component data out of storage. -/// -/// This function leaves the underlying memory unchanged, but the component behind -/// returned pointer is semantically owned by the caller and will not be dropped in its original location. -/// Caller is responsible to drop component data behind returned pointer. -/// -/// # Safety -/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside the archetype -/// and table. -/// - `component_id` must be valid -/// - The relevant table row **must be removed** by the caller once all components are taken -#[inline] -unsafe fn take_component<'a>( - components: &Components, - storages: &'a mut Storages, - removed_components: &mut SparseSet>, - component_id: ComponentId, - entity: Entity, - location: EntityLocation, -) -> OwningPtr<'a> { - 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 => { - let table = &mut storages.tables[location.table_id]; - // SAFETY: archetypes will always point to valid columns - let components = table.get_column_mut(component_id).unwrap(); - // SAFETY: archetypes only store valid table_rows and the stored component type is T - components - .get_data_unchecked_mut(location.table_row) - .promote() - } - StorageType::SparseSet => storages - .sparse_sets - .get_mut(component_id) - .unwrap() - .remove_and_forget(entity) - .unwrap(), - } -} - -/// Get a raw pointer to a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option> { - get_component( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - -/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_and_ticks_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option<(Ptr<'_>, TickCells<'_>)> { - get_component_and_ticks( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_ticks_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option { - get_ticks( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - 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) @@ -1044,21 +834,26 @@ pub(crate) unsafe fn get_mut( entity: Entity, location: EntityLocation, ) -> Option> { - // SAFETY: world access is unique, entity location is valid, and returned component is of type - // T let change_tick = world.change_tick(); let last_change_tick = world.last_change_tick(); - get_component_and_ticks_with_type( - world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - entity, - location, - ) - .map(|(value, ticks)| Mut { - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), - }) + // SAFETY: + // - world access is unique + // - entity location is valid + // - and returned component is of type T + world + .get_component_and_ticks_with_type( + TypeId::of::(), + T::Storage::STORAGE_TYPE, + entity, + location, + ) + .map(|(value, ticks)| Mut { + // SAFETY: + // - world access is unique and ties world lifetime to `Mut` lifetime + // - `value` is of type `T` + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), + }) } // SAFETY: EntityLocation must be valid, component_id must be valid @@ -1068,16 +863,66 @@ pub(crate) unsafe fn get_mut_by_id( entity: Entity, location: EntityLocation, component_id: ComponentId, -) -> Option { +) -> Option> { let change_tick = world.change_tick(); + // SAFETY: component_id is valid let info = world.components.get_info_unchecked(component_id); - // SAFETY: world access is unique, entity location and component_id required to be valid - get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map( - |(value, ticks)| MutUntyped { + // SAFETY: + // - world access is unique + // - entity location is valid + // - and returned component is of type T + world + .get_component_and_ticks(component_id, info.storage_type(), entity, location) + .map(|(value, ticks)| MutUntyped { + // SAFETY: world access is unique and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), - }, - ) + }) +} + +/// Moves component data out of storage. +/// +/// This function leaves the underlying memory unchanged, but the component behind +/// returned pointer is semantically owned by the caller and will not be dropped in its original location. +/// Caller is responsible to drop component data behind returned pointer. +/// +/// # Safety +/// - `location.table_row` must be in bounds of column of component id `component_id` +/// - `component_id` must be valid +/// - `components` must come from the same world as `self` +/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value +#[inline] +pub(crate) unsafe fn take_component<'a>( + storages: &'a mut Storages, + components: &Components, + removed_components: &mut SparseSet>, + component_id: ComponentId, + entity: Entity, + location: EntityLocation, +) -> OwningPtr<'a> { + // SAFETY: caller promises component_id to be valid + 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 => { + let table = &mut storages.tables[location.table_id]; + let components = table.get_column_mut(component_id).unwrap(); + // SAFETY: + // - archetypes only store valid table_rows + // - index is in bounds as promised by caller + // - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards + components + .get_data_unchecked_mut(location.table_row) + .promote() + } + StorageType::SparseSet => storages + .sparse_sets + .get_mut(component_id) + .unwrap() + .remove_and_forget(entity) + .unwrap(), + } } #[cfg(test)] diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8cd32201d697b..2243af65deb6e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3,7 +3,7 @@ mod spawn_batch; mod world_cell; pub use crate::change_detection::{Mut, Ref}; -pub use entity_ref::*; +pub use entity_ref::{EntityMut, EntityRef}; pub use spawn_batch::*; pub use world_cell::*; @@ -12,13 +12,14 @@ use crate::{ bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, change_detection::{MutUntyped, TicksMut}, component::{ - Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells, + Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components, + StorageType, TickCells, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, event::{Event, Events}, ptr::UnsafeCellDeref, query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery}, - storage::{ResourceData, SparseSet, Storages}, + storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr}; @@ -564,8 +565,10 @@ impl World { /// ``` #[inline] pub fn get_mut(&mut self, entity: Entity) -> Option> { - // SAFETY: lifetimes enforce correct usage of returned borrow - unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) } + // SAFETY: + // - lifetimes enforce correct usage of returned borrow + // - entity location is checked in `get_entity` + unsafe { entity_ref::get_mut(self, entity, self.get_entity(entity)?.location()) } } /// Despawns the given `entity`, if it exists. This will also remove all of the entity's @@ -1727,8 +1730,7 @@ impl World { // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self, + self.get_component( component_id, info.storage_type(), entity, @@ -1751,7 +1753,7 @@ impl World { self.components().get_info(component_id)?; // SAFETY: entity_location is valid, component_id is valid as checked by the line above unsafe { - get_mut_by_id( + entity_ref::get_mut_by_id( self, entity, self.get_entity(entity)?.location(), @@ -1761,6 +1763,170 @@ impl World { } } +impl World { + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`] + /// + /// # Safety + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `location` must refer to an archetype that contains `entity` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_and_ticks_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_component_and_ticks(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_and_ticks( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + match storage_type { + StorageType::Table => { + let (components, table_row) = self.fetch_table(location, component_id)?; + + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(( + components.get_data_unchecked(table_row), + TickCells { + added: components.get_added_ticks_unchecked(table_row), + changed: components.get_changed_ticks_unchecked(table_row), + }, + )) + } + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_with_ticks(entity), + } + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_component(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World). + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + // SAFETY: component_id exists and is therefore valid + match storage_type { + StorageType::Table => { + let (components, table_row) = self.fetch_table(location, component_id)?; + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(components.get_data_unchecked(table_row)) + } + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get(entity), + } + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_ticks_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_ticks(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_ticks( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + match storage_type { + StorageType::Table => { + let (components, table_row) = self.fetch_table(location, component_id)?; + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(components.get_ticks_unchecked(table_row)) + } + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_ticks(entity), + } + } + + #[inline] + fn fetch_table( + &self, + location: EntityLocation, + component_id: ComponentId, + ) -> Option<(&Column, TableRow)> { + let archetype = &self.archetypes[location.archetype_id]; + let table = &self.storages.tables[archetype.table_id()]; + let components = table.get_column(component_id)?; + let table_row = archetype.entity_table_row(location.archetype_row); + Some((components, table_row)) + } + + #[inline] + fn fetch_sparse_set(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> { + self.storages.sparse_sets.get(component_id) + } +} + impl fmt::Debug for World { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("World")