From c8c806da457c85f43eecdf1aa527a62a53f7feb3 Mon Sep 17 00:00:00 2001 From: TheRawMeatball Date: Mon, 2 May 2022 18:44:58 +0000 Subject: [PATCH] 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. --- crates/bevy_ecs/src/world/entity_ref.rs | 61 ++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 6adfb4db81ef90..9a53fe8992b6ca 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -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(&self) -> Option<&'w ComponentTicks> { + // SAFE: entity location is valid + unsafe { + get_ticks_with_type(self.world, TypeId::of::(), 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::() } } + /// 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(&self) -> Option<&ComponentTicks> { + // SAFE: entity location is valid + unsafe { + get_ticks_with_type(self.world, TypeId::of::(), 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> { + 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> { + 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)