Skip to content

Commit

Permalink
Add get_change_ticks method to EntityRef and EntityMut (#2539)
Browse files Browse the repository at this point in the history
Direct access to the change ticks is useful for integrating the reliable change detection with external stuff.
  • Loading branch information
TheRawMeatball committed May 2, 2022
1 parent 3fbe368 commit 5ca78b1
Showing 1 changed file with 60 additions and 1 deletion.
61 changes: 60 additions & 1 deletion crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 5ca78b1

Please sign in to comment.