Add get_ref to EntityRef (#8818)

# Objective

To mirror the `Ref` added as `WorldQuery`, and the `Mut` in
`EntityMut::get_mut`, we add `EntityRef::get_ref`, which retrieves `T`
with tick information, but *immutably*.

## Solution

- Add the method in question, also add it to`UnsafeEntityCell` since
this seems to be the best way of getting that information.

Also update/add safety comments to neighboring code.

---

## Changelog

- Add `EntityRef::get_ref` to get an `Option<Ref<T>>` from `EntityRef`

---------

Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
Nicola Papale 2023-06-13 10:47:55 +02:00 committed by GitHub
parent 001b3eb97c
commit 019432af2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 6 deletions

View File

@ -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::<T>() }
}
/// 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<T: Component>(&self) -> Option<Ref<'w, T>> {
// SAFETY: &self implies shared access for duration of returned value
unsafe { self.as_unsafe_world_cell_readonly().get_ref::<T>() }
}
/// Retrieves the change ticks for the given component. This can be useful for implementing change
/// detection in custom runtimes.
#[inline]

View File

@ -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<T: Component>(self) -> Option<&'w T> {
let component_id = self.world.components().get_id(TypeId::of::<T>())?;
// 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<T: Component>(self) -> Option<Ref<'w, T>> {
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::<T>())?;
// 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::<T>(),
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::<T>(),
ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick),
})