diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 346e2244872e6..fb9a3f89243c3 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -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(&self) -> Res { + 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::() + ), + } + } + /// 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(&self) -> Option> { + // 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(&mut self) -> Option> { @@ -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(); diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 428a1c1157650..a851024a701e4 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -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(self) -> Option> { + let component_id = self.components().get_resource_id(TypeId::of::())?; + + // 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::() }; + + // 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.