added ability to get Res<T> from World with World::get_resource_ref (#11561)

# Objective

It's sometimes desirable to get a `Res<T>` rather than `&T` from
`World::get_resource`.
Alternative to #9940, partly adresses #9926

## Solution

added additional methods to `World` and `UnsafeWorldCell` to retrieve a
resource wrapped in a `Res`.
- `UnsafeWorldCell::get_resource_ref`
- `World::get_resource_ref`
- `World::resource_ref`

I can change it so `World::resource_mut` returns `ResMut` instead of
`Mut` as well if that's desired, but that could also be added later in a
seperate pr.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Mike <mike.hsu@gmail.com>
Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com>
This commit is contained in:
poopy 2024-01-28 02:38:21 +01:00 committed by GitHub
parent 56c3079586
commit 2391e44fa0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 3 deletions

View file

@ -28,7 +28,7 @@ use crate::{
removal_detection::RemovedComponentEvents,
schedule::{Schedule, ScheduleLabel, Schedules},
storage::{ResourceData, Storages},
system::Resource,
system::{Res, Resource},
world::error::TryRunScheduleError,
};
use bevy_ptr::{OwningPtr, Ptr};
@ -1302,6 +1302,30 @@ impl World {
}
}
/// Gets a reference to the resource of the given type
///
/// # Panics
///
/// Panics if the resource does not exist.
/// Use [`get_resource_ref`](World::get_resource_ref) instead if you want to handle this case.
///
/// If you want to instead insert a value if the resource does not exist,
/// use [`get_resource_or_insert_with`](World::get_resource_or_insert_with).
#[inline]
#[track_caller]
pub fn resource_ref<R: Resource>(&self) -> Res<R> {
match self.get_resource_ref() {
Some(x) => x,
None => panic!(
"Requested resource {} does not exist in the `World`.
Did you forget to add it using `app.insert_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
std::any::type_name::<R>()
),
}
}
/// Gets a mutable reference to the resource of the given type
///
/// # Panics
@ -1335,6 +1359,15 @@ impl World {
unsafe { self.as_unsafe_world_cell_readonly().get_resource() }
}
/// Gets a reference including change detection to the resource of the given type if it exists.
#[inline]
pub fn get_resource_ref<R: Resource>(&self) -> Option<Res<R>> {
// SAFETY:
// - `as_unsafe_world_cell_readonly` gives permission to access everything immutably
// - `&self` ensures nothing in world is borrowed mutably
unsafe { self.as_unsafe_world_cell_readonly().get_resource_ref() }
}
/// Gets a mutable reference to the resource of the given type if it exists
#[inline]
pub fn get_resource_mut<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
@ -1875,7 +1908,7 @@ impl World {
}
/// Runs both [`clear_entities`](Self::clear_entities) and [`clear_resources`](Self::clear_resources),
/// invalidating all [`Entity`] and resource fetches such as [`Res`](crate::system::Res), [`ResMut`](crate::system::ResMut)
/// invalidating all [`Entity`] and resource fetches such as [`Res`], [`ResMut`](crate::system::ResMut)
pub fn clear_all(&mut self) {
self.clear_entities();
self.clear_resources();

View file

@ -14,7 +14,7 @@ use crate::{
prelude::Component,
removal_detection::RemovedComponentEvents,
storage::{Column, ComponentSparseSet, Storages},
system::Resource,
system::{Res, Resource},
};
use bevy_ptr::Ptr;
use std::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData};
@ -342,6 +342,30 @@ impl<'w> UnsafeWorldCell<'w> {
}
}
/// Gets a reference including change detection to the resource of the given type if it exists.
///
/// # Safety
/// It is the callers responsibility to ensure that
/// - the [`UnsafeWorldCell`] has permission to access the resource
/// - no mutable reference to the resource exists at the same time
#[inline]
pub unsafe fn get_resource_ref<R: Resource>(self) -> Option<Res<'w, R>> {
let component_id = self.components().get_resource_id(TypeId::of::<R>())?;
// SAFETY: caller ensures `self` has permission to access the resource
// caller also ensure that no mutable reference to the resource exists
let (ptr, ticks) = unsafe { self.get_resource_with_ticks(component_id)? };
// SAFETY: `component_id` was obtained from the type ID of `R`
let value = unsafe { ptr.deref::<R>() };
// SAFETY: caller ensures that no mutable reference to the resource exists
let ticks =
unsafe { Ticks::from_tick_cells(ticks, self.last_change_tick(), self.change_tick()) };
Some(Res { value, ticks })
}
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
/// The returned pointer must not be used to modify the resource, and must not be
/// dereferenced after the borrow of the [`World`] ends.