diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c97d9a2148..0a74cecfa2 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -12,7 +12,7 @@ use bevy_ptr::{OwningPtr, Ptr}; use bevy_utils::tracing::debug; use std::any::TypeId; -use super::unsafe_world_cell::UnsafeEntityCell; +use super::{unsafe_world_cell::UnsafeEntityCell, Ref}; /// A read-only reference to a particular [`Entity`] and all of its components #[derive(Copy, Clone)] @@ -121,6 +121,16 @@ impl<'w> EntityRef<'w> { unsafe { self.as_unsafe_world_cell_readonly().get::() } } + /// Gets access to the component of type `T` for the current entity, + /// including change detection information as a [`Ref`]. + /// + /// Returns `None` if the entity does not have a component of type `T`. + #[inline] + pub fn get_ref(&self) -> Option> { + // SAFETY: &self implies shared access for duration of returned value + unsafe { self.as_unsafe_world_cell_readonly().get_ref::() } + } + /// Retrieves the change ticks for the given component. This can be useful for implementing change /// detection in custom runtimes. #[inline] diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 997862981a..f95df96429 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -2,11 +2,11 @@ #![warn(unsafe_op_in_unsafe_fn)] -use super::{Mut, World, WorldId}; +use super::{Mut, Ref, World, WorldId}; use crate::{ archetype::{Archetype, ArchetypeComponentId, Archetypes}, bundle::Bundles, - change_detection::{MutUntyped, TicksMut}, + change_detection::{MutUntyped, Ticks, TicksMut}, component::{ ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, Tick, TickCells, }, @@ -652,10 +652,10 @@ impl<'w> UnsafeEntityCell<'w> { #[inline] pub unsafe fn get(self) -> Option<&'w T> { let component_id = self.world.components().get_id(TypeId::of::())?; - // SAFETY: - // - entity location is valid - // - proper world access is promised by caller + // - `storage_type` is correct (T component_id + T::STORAGE_TYPE) + // - `location` is valid + // - proper aliasing is promised by caller unsafe { get_component( self.world, @@ -669,6 +669,36 @@ impl<'w> UnsafeEntityCell<'w> { } } + /// # Safety + /// It is the callers responsibility to ensure that + /// - the [`UnsafeEntityCell`] has permission to access the component + /// - no other mutable references to the component exist at the same time + #[inline] + pub unsafe fn get_ref(self) -> Option> { + let last_change_tick = self.world.last_change_tick(); + let change_tick = self.world.change_tick(); + let component_id = self.world.components().get_id(TypeId::of::())?; + + // SAFETY: + // - `storage_type` is correct (T component_id + T::STORAGE_TYPE) + // - `location` is valid + // - proper aliasing is promised by caller + unsafe { + get_component_and_ticks( + self.world, + component_id, + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, cells)| Ref { + // SAFETY: returned component is of type T + value: value.deref::(), + ticks: Ticks::from_tick_cells(cells, last_change_tick, change_tick), + }) + } + } + /// Retrieves the change ticks for the given component. This can be useful for implementing change /// detection in custom runtimes. /// @@ -761,6 +791,7 @@ impl<'w> UnsafeEntityCell<'w> { self.location, ) .map(|(value, cells)| Mut { + // SAFETY: returned component is of type T value: value.assert_unique().deref_mut::(), ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), })