Add get_change_ticks method to EntityRef and EntityMut (#2539)

Direct access to the change ticks is useful for integrating the reliable change detection with external stuff.
This commit is contained in:
TheRawMeatball 2022-05-02 18:44:58 +00:00
parent 3fbe3683d9
commit 5ca78b1e27

View File

@ -4,7 +4,7 @@ use crate::{
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation},
ptr::{OwningPtr, Ptr},
ptr::{OwningPtr, Ptr, UnsafeCellDeref},
storage::{SparseSet, Storages},
world::{Mut, World},
};
@ -72,6 +72,17 @@ impl<'w> EntityRef<'w> {
}
}
/// Retrieves the change ticks for the given component. This can be useful for implementing change
/// detection in custom runtimes.
#[inline]
pub fn get_change_ticks<T: Component>(&self) -> Option<&'w ComponentTicks> {
// SAFE: entity location is valid
unsafe {
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|ticks| ticks.deref())
}
}
/// Gets a mutable reference to the component of type `T` associated with
/// this entity without ensuring there are no other borrows active and without
/// ensuring that the returned reference will stay valid.
@ -169,6 +180,17 @@ impl<'w> EntityMut<'w> {
unsafe { self.get_unchecked_mut::<T>() }
}
/// Retrieves the change ticks for the given component. This can be useful for implementing change
/// detection in custom runtimes.
#[inline]
pub fn get_change_ticks<T: Component>(&self) -> Option<&ComponentTicks> {
// SAFE: entity location is valid
unsafe {
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
.map(|ticks| ticks.deref())
}
}
/// Gets a mutable reference to the component of type `T` associated with
/// this entity without ensuring there are no other borrows active and without
/// ensuring that the returned reference will stay valid.
@ -531,6 +553,31 @@ unsafe fn get_component_and_ticks(
}
}
#[inline]
unsafe fn get_ticks(
world: &World,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> Option<&UnsafeCell<ComponentTicks>> {
let archetype = &world.archetypes[location.archetype_id];
let component_info = world.components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => {
let table = &world.storages.tables[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_ticks_unchecked(table_row))
}
StorageType::SparseSet => world
.storages
.sparse_sets
.get(component_id)
.and_then(|sparse_set| sparse_set.get_ticks(entity)),
}
}
// TODO: move to Storages?
/// Moves component data out of storage.
///
@ -601,6 +648,18 @@ pub(crate) unsafe fn get_component_and_ticks_with_type(
get_component_and_ticks(world, component_id, entity, location)
}
/// # Safety
/// `entity_location` must be within bounds of an archetype that exists.
pub(crate) unsafe fn get_ticks_with_type(
world: &World,
type_id: TypeId,
entity: Entity,
location: EntityLocation,
) -> Option<&UnsafeCell<ComponentTicks>> {
let component_id = world.components.get_id(type_id)?;
get_ticks(world, component_id, 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)