Add infallible resource getters for WorldCell (#4104)

# Objective

- Eliminate all `worldcell.get_resource().unwrap()` cases.
- Provide helpful messages on panic.

## Solution

- Adds infallible resource getters to `WorldCell`, mirroring `World`.
This commit is contained in:
Aevyrie 2022-04-25 23:19:13 +00:00
parent 989fb8a78d
commit 4aa56050b6
8 changed files with 140 additions and 74 deletions

View file

@ -180,6 +180,7 @@ impl<'w> WorldCell<'w> {
}
}
/// Gets a reference to the resource of the given type
pub fn get_resource<T: Resource>(&self) -> Option<WorldBorrow<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource();
@ -192,6 +193,26 @@ impl<'w> WorldCell<'w> {
))
}
/// Gets a reference to the resource of the given type
///
/// # Panics
///
/// Panics if the resource does not exist. Use [`get_resource`](WorldCell::get_resource) instead
/// if you want to handle this case.
pub fn resource<T: Resource>(&self) -> WorldBorrow<'_, T> {
match self.get_resource() {
Some(x) => x,
None => panic!(
"Requested resource {} does not exist in the `World`.
Did you forget to add it using `app.add_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
std::any::type_name::<T>()
),
}
}
/// Gets a mutable reference to the resource of the given type
pub fn get_resource_mut<T: Resource>(&self) -> Option<WorldBorrowMut<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource();
@ -207,7 +228,27 @@ impl<'w> WorldCell<'w> {
))
}
pub fn get_non_send<T: 'static>(&self) -> Option<WorldBorrow<'_, T>> {
/// Gets a mutable reference to the resource of the given type
///
/// # Panics
///
/// Panics if the resource does not exist. Use [`get_resource_mut`](WorldCell::get_resource_mut)
/// instead if you want to handle this case.
pub fn resource_mut<T: Resource>(&self) -> WorldBorrowMut<'_, T> {
match self.get_resource_mut() {
Some(x) => x,
None => panic!(
"Requested resource {} does not exist in the `World`.
Did you forget to add it using `app.add_resource` / `app.init_resource`?
Resources are also implicitly added via `app.add_event`,
and can be added by plugins.",
std::any::type_name::<T>()
),
}
}
/// Gets an immutable reference to the non-send resource of the given type, if it exists.
pub fn get_non_send_resource<T: 'static>(&self) -> Option<WorldBorrow<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource();
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
@ -219,7 +260,27 @@ impl<'w> WorldCell<'w> {
))
}
pub fn get_non_send_mut<T: 'static>(&self) -> Option<WorldBorrowMut<'_, T>> {
/// Gets an immutable reference to the non-send resource of the given type, if it exists.
///
/// # Panics
///
/// Panics if the resource does not exist. Use
/// [`get_non_send_resource`](WorldCell::get_non_send_resource) instead if you want to handle
/// this case.
pub fn non_send_resource<T: 'static>(&self) -> WorldBorrow<'_, T> {
match self.get_non_send_resource() {
Some(x) => x,
None => panic!(
"Requested non-send resource {} does not exist in the `World`.
Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`?
Non-send resources can also be be added by plugins.",
std::any::type_name::<T>()
),
}
}
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
pub fn get_non_send_resource_mut<T: 'static>(&self) -> Option<WorldBorrowMut<'_, T>> {
let component_id = self.world.components.get_resource_id(TypeId::of::<T>())?;
let resource_archetype = self.world.archetypes.resource();
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
@ -233,6 +294,25 @@ impl<'w> WorldCell<'w> {
self.access.clone(),
))
}
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
///
/// # Panics
///
/// Panics if the resource does not exist. Use
/// [`get_non_send_resource_mut`](WorldCell::get_non_send_resource_mut) instead if you want to
/// handle this case.
pub fn non_send_resource_mut<T: 'static>(&self) -> WorldBorrowMut<'_, T> {
match self.get_non_send_resource_mut() {
Some(x) => x,
None => panic!(
"Requested non-send resource {} does not exist in the `World`.
Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`?
Non-send resources can also be be added by plugins.",
std::any::type_name::<T>()
),
}
}
}
#[cfg(test)]
@ -248,28 +328,28 @@ mod tests {
world.insert_resource(1u64);
let cell = world.cell();
{
let mut a = cell.get_resource_mut::<u32>().unwrap();
let mut a = cell.resource_mut::<u32>();
assert_eq!(1, *a);
*a = 2;
}
{
let a = cell.get_resource::<u32>().unwrap();
let a = cell.resource::<u32>();
assert_eq!(2, *a, "ensure access is dropped");
let b = cell.get_resource::<u32>().unwrap();
let b = cell.resource::<u32>();
assert_eq!(
2, *b,
"ensure multiple immutable accesses can occur at the same time"
);
}
{
let a = cell.get_resource_mut::<u32>().unwrap();
let a = cell.resource_mut::<u32>();
assert_eq!(
2, *a,
"ensure both immutable accesses are dropped, enabling a new mutable access"
);
let b = cell.get_resource::<u64>().unwrap();
let b = cell.resource::<u64>();
assert_eq!(
1, *b,
"ensure multiple non-conflicting mutable accesses can occur at the same time"
@ -284,7 +364,7 @@ mod tests {
{
let cell = world.cell();
{
let mut a = cell.get_resource_mut::<u32>().unwrap();
let mut a = cell.resource_mut::<u32>();
assert_eq!(1, *a);
*a = 2;
}
@ -315,8 +395,8 @@ mod tests {
let mut world = World::default();
world.insert_resource(1u32);
let cell = world.cell();
let _value_a = cell.get_resource_mut::<u32>().unwrap();
let _value_b = cell.get_resource_mut::<u32>().unwrap();
let _value_a = cell.resource_mut::<u32>();
let _value_b = cell.resource_mut::<u32>();
}
#[test]
@ -325,8 +405,8 @@ mod tests {
let mut world = World::default();
world.insert_resource(1u32);
let cell = world.cell();
let _value_a = cell.get_resource::<u32>().unwrap();
let _value_b = cell.get_resource_mut::<u32>().unwrap();
let _value_a = cell.resource::<u32>();
let _value_b = cell.resource_mut::<u32>();
}
#[test]
@ -335,8 +415,8 @@ mod tests {
let mut world = World::default();
world.insert_resource(1u32);
let cell = world.cell();
let _value_a = cell.get_resource_mut::<u32>().unwrap();
let _value_b = cell.get_resource::<u32>().unwrap();
let _value_a = cell.resource_mut::<u32>();
let _value_b = cell.resource::<u32>();
}
#[test]
@ -345,7 +425,7 @@ mod tests {
let mut world = World::default();
world.insert_resource(1u32);
let cell = world.cell();
let _value_a = cell.get_resource_mut::<u32>().unwrap();
let _value_b = cell.get_resource::<u32>().unwrap();
let _value_a = cell.resource_mut::<u32>();
let _value_b = cell.resource::<u32>();
}
}

View file

@ -236,7 +236,7 @@ pub struct ShadowPipeline {
impl FromWorld for ShadowPipeline {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let render_device = world.get_resource::<RenderDevice>().unwrap();
let render_device = world.resource::<RenderDevice>();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
@ -255,7 +255,7 @@ impl FromWorld for ShadowPipeline {
label: Some("shadow_view_layout"),
});
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap();
let mesh_pipeline = world.resource::<MeshPipeline>();
let skinned_mesh_layout = mesh_pipeline.skinned_mesh_layout.clone();
ShadowPipeline {

View file

@ -39,7 +39,7 @@ pub struct SpritePipeline {
impl FromWorld for SpritePipeline {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let render_device = world.get_resource::<RenderDevice>().unwrap();
let render_device = world.resource::<RenderDevice>();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {

View file

@ -14,7 +14,7 @@ pub struct UiPipeline {
impl FromWorld for UiPipeline {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let render_device = world.get_resource::<RenderDevice>().unwrap();
let render_device = world.resource::<RenderDevice>();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {

View file

@ -300,8 +300,8 @@ pub fn winit_runner_with(mut app: App) {
..
} => {
let world = app.world.cell();
let winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let winit_windows = world.non_send_resource_mut::<WinitWindows>();
let mut windows = world.resource_mut::<Windows>();
let window_id =
if let Some(window_id) = winit_windows.get_window_id(winit_window_id) {
window_id
@ -324,8 +324,7 @@ pub fn winit_runner_with(mut app: App) {
match event {
WindowEvent::Resized(size) => {
window.update_actual_size_from_backend(size.width, size.height);
let mut resize_events =
world.get_resource_mut::<Events<WindowResized>>().unwrap();
let mut resize_events = world.resource_mut::<Events<WindowResized>>();
resize_events.send(WindowResized {
id: window_id,
width: window.width(),
@ -333,19 +332,17 @@ pub fn winit_runner_with(mut app: App) {
});
}
WindowEvent::CloseRequested => {
let mut window_close_requested_events = world
.get_resource_mut::<Events<WindowCloseRequested>>()
.unwrap();
let mut window_close_requested_events =
world.resource_mut::<Events<WindowCloseRequested>>();
window_close_requested_events.send(WindowCloseRequested { id: window_id });
}
WindowEvent::KeyboardInput { ref input, .. } => {
let mut keyboard_input_events =
world.get_resource_mut::<Events<KeyboardInput>>().unwrap();
world.resource_mut::<Events<KeyboardInput>>();
keyboard_input_events.send(converters::convert_keyboard_input(input));
}
WindowEvent::CursorMoved { position, .. } => {
let mut cursor_moved_events =
world.get_resource_mut::<Events<CursorMoved>>().unwrap();
let mut cursor_moved_events = world.resource_mut::<Events<CursorMoved>>();
let winit_window = winit_windows.get_window(window_id).unwrap();
let inner_size = winit_window.inner_size();
@ -363,19 +360,17 @@ pub fn winit_runner_with(mut app: App) {
}
WindowEvent::CursorEntered { .. } => {
let mut cursor_entered_events =
world.get_resource_mut::<Events<CursorEntered>>().unwrap();
world.resource_mut::<Events<CursorEntered>>();
cursor_entered_events.send(CursorEntered { id: window_id });
}
WindowEvent::CursorLeft { .. } => {
let mut cursor_left_events =
world.get_resource_mut::<Events<CursorLeft>>().unwrap();
let mut cursor_left_events = world.resource_mut::<Events<CursorLeft>>();
window.update_cursor_physical_position_from_backend(None);
cursor_left_events.send(CursorLeft { id: window_id });
}
WindowEvent::MouseInput { state, button, .. } => {
let mut mouse_button_input_events = world
.get_resource_mut::<Events<MouseButtonInput>>()
.unwrap();
let mut mouse_button_input_events =
world.resource_mut::<Events<MouseButtonInput>>();
mouse_button_input_events.send(MouseButtonInput {
button: converters::convert_mouse_button(button),
state: converters::convert_element_state(state),
@ -384,7 +379,7 @@ pub fn winit_runner_with(mut app: App) {
WindowEvent::MouseWheel { delta, .. } => match delta {
event::MouseScrollDelta::LineDelta(x, y) => {
let mut mouse_wheel_input_events =
world.get_resource_mut::<Events<MouseWheel>>().unwrap();
world.resource_mut::<Events<MouseWheel>>();
mouse_wheel_input_events.send(MouseWheel {
unit: MouseScrollUnit::Line,
x,
@ -393,7 +388,7 @@ pub fn winit_runner_with(mut app: App) {
}
event::MouseScrollDelta::PixelDelta(p) => {
let mut mouse_wheel_input_events =
world.get_resource_mut::<Events<MouseWheel>>().unwrap();
world.resource_mut::<Events<MouseWheel>>();
mouse_wheel_input_events.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: p.x as f32,
@ -402,8 +397,7 @@ pub fn winit_runner_with(mut app: App) {
}
},
WindowEvent::Touch(touch) => {
let mut touch_input_events =
world.get_resource_mut::<Events<TouchInput>>().unwrap();
let mut touch_input_events = world.resource_mut::<Events<TouchInput>>();
let mut location = touch.location.to_logical(window.scale_factor());
@ -416,9 +410,8 @@ pub fn winit_runner_with(mut app: App) {
touch_input_events.send(converters::convert_touch_input(touch, location));
}
WindowEvent::ReceivedCharacter(c) => {
let mut char_input_events = world
.get_resource_mut::<Events<ReceivedCharacter>>()
.unwrap();
let mut char_input_events =
world.resource_mut::<Events<ReceivedCharacter>>();
char_input_events.send(ReceivedCharacter {
id: window_id,
@ -429,9 +422,8 @@ pub fn winit_runner_with(mut app: App) {
scale_factor,
new_inner_size,
} => {
let mut backend_scale_factor_change_events = world
.get_resource_mut::<Events<WindowBackendScaleFactorChanged>>()
.unwrap();
let mut backend_scale_factor_change_events =
world.resource_mut::<Events<WindowBackendScaleFactorChanged>>();
backend_scale_factor_change_events.send(WindowBackendScaleFactorChanged {
id: window_id,
scale_factor,
@ -450,9 +442,8 @@ pub fn winit_runner_with(mut app: App) {
)
.to_physical::<u32>(forced_factor);
} else if approx::relative_ne!(new_factor, prior_factor) {
let mut scale_factor_change_events = world
.get_resource_mut::<Events<WindowScaleFactorChanged>>()
.unwrap();
let mut scale_factor_change_events =
world.resource_mut::<Events<WindowScaleFactorChanged>>();
scale_factor_change_events.send(WindowScaleFactorChanged {
id: window_id,
@ -465,8 +456,7 @@ pub fn winit_runner_with(mut app: App) {
if approx::relative_ne!(window.width() as f64, new_logical_width)
|| approx::relative_ne!(window.height() as f64, new_logical_height)
{
let mut resize_events =
world.get_resource_mut::<Events<WindowResized>>().unwrap();
let mut resize_events = world.resource_mut::<Events<WindowResized>>();
resize_events.send(WindowResized {
id: window_id,
width: new_logical_width as f32,
@ -480,38 +470,34 @@ pub fn winit_runner_with(mut app: App) {
}
WindowEvent::Focused(focused) => {
window.update_focused_status_from_backend(focused);
let mut focused_events =
world.get_resource_mut::<Events<WindowFocused>>().unwrap();
let mut focused_events = world.resource_mut::<Events<WindowFocused>>();
focused_events.send(WindowFocused {
id: window_id,
focused,
});
}
WindowEvent::DroppedFile(path_buf) => {
let mut events =
world.get_resource_mut::<Events<FileDragAndDrop>>().unwrap();
let mut events = world.resource_mut::<Events<FileDragAndDrop>>();
events.send(FileDragAndDrop::DroppedFile {
id: window_id,
path_buf,
});
}
WindowEvent::HoveredFile(path_buf) => {
let mut events =
world.get_resource_mut::<Events<FileDragAndDrop>>().unwrap();
let mut events = world.resource_mut::<Events<FileDragAndDrop>>();
events.send(FileDragAndDrop::HoveredFile {
id: window_id,
path_buf,
});
}
WindowEvent::HoveredFileCancelled => {
let mut events =
world.get_resource_mut::<Events<FileDragAndDrop>>().unwrap();
let mut events = world.resource_mut::<Events<FileDragAndDrop>>();
events.send(FileDragAndDrop::HoveredFileCancelled { id: window_id });
}
WindowEvent::Moved(position) => {
let position = ivec2(position.x, position.y);
window.update_actual_position_from_backend(position);
let mut events = world.get_resource_mut::<Events<WindowMoved>>().unwrap();
let mut events = world.resource_mut::<Events<WindowMoved>>();
events.send(WindowMoved {
id: window_id,
position,
@ -609,10 +595,10 @@ fn handle_create_window_events(
create_window_event_reader: &mut ManualEventReader<CreateWindow>,
) {
let world = world.cell();
let mut winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let create_window_events = world.get_resource::<Events<CreateWindow>>().unwrap();
let mut window_created_events = world.get_resource_mut::<Events<WindowCreated>>().unwrap();
let mut winit_windows = world.non_send_resource_mut::<WinitWindows>();
let mut windows = world.resource_mut::<Windows>();
let create_window_events = world.resource::<Events<CreateWindow>>();
let mut window_created_events = world.resource_mut::<Events<WindowCreated>>();
for create_window_event in create_window_event_reader.iter(&create_window_events) {
let window = winit_windows.create_window(
event_loop,
@ -628,10 +614,10 @@ fn handle_create_window_events(
fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) {
let world = world.cell();
let mut winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let mut create_window_events = world.get_resource_mut::<Events<CreateWindow>>().unwrap();
let mut window_created_events = world.get_resource_mut::<Events<WindowCreated>>().unwrap();
let mut winit_windows = world.non_send_resource_mut::<WinitWindows>();
let mut windows = world.resource_mut::<Windows>();
let mut create_window_events = world.resource_mut::<Events<CreateWindow>>();
let mut window_created_events = world.resource_mut::<Events<WindowCreated>>();
for create_window_event in create_window_events.drain() {
let window = winit_windows.create_window(
event_loop,

View file

@ -35,7 +35,7 @@ fn main() {
// This will add 3D render phases for the new camera.
render_app.add_system_to_stage(RenderStage::Extract, extract_first_pass_camera_phases);
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
let mut graph = render_app.world.resource_mut::<RenderGraph>();
// Add a node for the first pass.
graph.add_node(FIRST_PASS_DRIVER, driver);

View file

@ -186,10 +186,10 @@ pub struct CustomPipeline {
impl FromWorld for CustomPipeline {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let asset_server = world.get_resource::<AssetServer>().unwrap();
let asset_server = world.resource::<AssetServer>();
let shader = asset_server.load("shaders/animate_shader.wgsl");
let render_device = world.get_resource_mut::<RenderDevice>().unwrap();
let render_device = world.resource_mut::<RenderDevice>();
let time_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("time bind group"),
@ -205,7 +205,7 @@ impl FromWorld for CustomPipeline {
}],
});
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap();
let mesh_pipeline = world.resource::<MeshPipeline>();
CustomPipeline {
shader,

View file

@ -169,11 +169,11 @@ pub struct CustomPipeline {
impl FromWorld for CustomPipeline {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let asset_server = world.get_resource::<AssetServer>().unwrap();
let asset_server = world.resource::<AssetServer>();
asset_server.watch_for_changes().unwrap();
let shader = asset_server.load("shaders/instancing.wgsl");
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap();
let mesh_pipeline = world.resource::<MeshPipeline>();
CustomPipeline {
shader,