Support using FilteredResources with ReflectResource. (#15624)

# Objective

Support accessing resources using reflection when using
`FilteredResources` in a dynamic system. This is similar to how
components can be queried using reflection when using
`FilteredEntityRef|Mut`.

## Solution

Change `ReflectResource` from taking `&World` and `&mut World` to taking
`impl Into<FilteredResources>` and `impl Into<FilteredResourcesMut>`,
similar to how `ReflectComponent` takes `impl Into<FilteredEntityRef>`
and `impl Into<FilteredEntityMut>`. There are `From` impls that ensure
code passing `&World` and `&mut World` continues to work as before.

## Migration Guide

If you are manually creating a `ReflectComponentFns` struct, the
`reflect` function now takes `FilteredResources` instead `&World`, and
there is a new `reflect_mut` function that takes `FilteredResourcesMut`.
This commit is contained in:
Chris Russell 2025-02-16 14:56:19 -05:00 committed by GitHub
parent d7fd00a8b9
commit 0a32450715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 58 additions and 10 deletions

View File

@ -8,7 +8,7 @@ use crate::{
change_detection::Mut,
component::ComponentId,
resource::Resource,
world::{unsafe_world_cell::UnsafeWorldCell, World},
world::{unsafe_world_cell::UnsafeWorldCell, FilteredResources, FilteredResourcesMut, World},
};
use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry};
@ -52,7 +52,9 @@ pub struct ReflectResourceFns {
/// Function pointer implementing [`ReflectResource::remove()`].
pub remove: fn(&mut World),
/// Function pointer implementing [`ReflectResource::reflect()`].
pub reflect: fn(&World) -> Option<&dyn Reflect>,
pub reflect: for<'w> fn(FilteredResources<'w, '_>) -> Option<&'w dyn Reflect>,
/// Function pointer implementing [`ReflectResource::reflect_mut()`].
pub reflect_mut: for<'w> fn(FilteredResourcesMut<'w, '_>) -> Option<Mut<'w, dyn Reflect>>,
/// Function pointer implementing [`ReflectResource::reflect_unchecked_mut()`].
///
/// # Safety
@ -111,14 +113,23 @@ impl ReflectResource {
}
/// Gets the value of this [`Resource`] type from the world as a reflected reference.
pub fn reflect<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> {
(self.0.reflect)(world)
///
/// Note that [`&World`](World) is a valid type for `resources`.
pub fn reflect<'w, 's>(
&self,
resources: impl Into<FilteredResources<'w, 's>>,
) -> Option<&'w dyn Reflect> {
(self.0.reflect)(resources.into())
}
/// Gets the value of this [`Resource`] type from the world as a mutable reflected reference.
pub fn reflect_mut<'a>(&self, world: &'a mut World) -> Option<Mut<'a, dyn Reflect>> {
// SAFETY: unique world access
unsafe { (self.0.reflect_unchecked_mut)(world.as_unsafe_world_cell()) }
///
/// Note that [`&mut World`](World) is a valid type for `resources`.
pub fn reflect_mut<'w, 's>(
&self,
resources: impl Into<FilteredResourcesMut<'w, 's>>,
) -> Option<Mut<'w, dyn Reflect>> {
(self.0.reflect_mut)(resources.into())
}
/// # Safety
@ -212,7 +223,12 @@ impl<R: Resource + FromReflect + TypePath> FromType<R> for ReflectResource {
remove: |world| {
world.remove_resource::<R>();
},
reflect: |world| world.get_resource::<R>().map(|res| res as &dyn Reflect),
reflect: |world| world.get::<R>().map(|res| res.into_inner() as &dyn Reflect),
reflect_mut: |world| {
world
.into_mut::<R>()
.map(|res| res.map_unchanged(|value| value as &mut dyn Reflect))
},
reflect_unchecked_mut: |world| {
// SAFETY: all usages of `reflect_unchecked_mut` guarantee that there is either a single mutable
// reference or multiple immutable ones alive at any given point

View File

@ -715,9 +715,11 @@ mod tests {
use crate::{
entity::Entities,
prelude::{Component, Query},
reflect::ReflectResource,
system::{Local, RunSystemOnce},
};
use alloc::vec;
use bevy_reflect::{FromType, Reflect, ReflectRef};
use super::*;
@ -730,8 +732,11 @@ mod tests {
#[derive(Component)]
struct C;
#[derive(Resource, Default)]
struct R;
#[derive(Resource, Default, Reflect)]
#[reflect(Resource)]
struct R {
foo: usize,
}
fn local_system(local: Local<u64>) -> u64 {
*local
@ -1071,4 +1076,31 @@ mod tests {
.build_state(&mut world)
.build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
}
#[test]
fn filtered_resource_reflect() {
let mut world = World::new();
world.insert_resource(R { foo: 7 });
let system = (FilteredResourcesParamBuilder::new(|builder| {
builder.add_read::<R>();
}),)
.build_state(&mut world)
.build_system(|res: FilteredResources| {
let reflect_resource = <ReflectResource as FromType<R>>::from_type();
let ReflectRef::Struct(reflect_struct) =
reflect_resource.reflect(res).unwrap().reflect_ref()
else {
panic!()
};
*reflect_struct
.field("foo")
.unwrap()
.try_downcast_ref::<usize>()
.unwrap()
});
let output = world.run_system_once(system).unwrap();
assert_eq!(output, 7);
}
}