# Objective
- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).
## Solution
- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.
---
## Changelog
- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.
## Migration Guide
- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.
## Future Work
- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](8e3488c880/crates/bevy_reflect/src/reflect.rs (L337-L342))
and make `FromReflect` a requirement to improve [`FromReflect`
ergonomics](https://github.com/bevyengine/rfcs/pull/59). This is
currently not possible because dynamic types cannot sensibly be
`FromReflect`.
- Since this is an alternative to #5772, #5781 would be made cleaner.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
145 lines
4.5 KiB
Rust
145 lines
4.5 KiB
Rust
//! Types that enable reflection support.
|
|
|
|
use std::any::TypeId;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use crate as bevy_ecs;
|
|
use crate::{system::Resource, world::World};
|
|
use bevy_reflect::std_traits::ReflectDefault;
|
|
use bevy_reflect::{
|
|
PartialReflect, Reflect, ReflectFromReflect, TypePath, TypeRegistry, TypeRegistryArc,
|
|
};
|
|
|
|
mod bundle;
|
|
mod component;
|
|
mod entity_commands;
|
|
mod from_world;
|
|
mod map_entities;
|
|
mod resource;
|
|
|
|
pub use bundle::{ReflectBundle, ReflectBundleFns};
|
|
pub use component::{ReflectComponent, ReflectComponentFns};
|
|
pub use entity_commands::ReflectCommandExt;
|
|
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
|
|
pub use map_entities::{ReflectMapEntities, ReflectMapEntitiesResource};
|
|
pub use resource::{ReflectResource, ReflectResourceFns};
|
|
|
|
/// A [`Resource`] storing [`TypeRegistry`] for
|
|
/// type registrations relevant to a whole app.
|
|
#[derive(Resource, Clone, Default)]
|
|
pub struct AppTypeRegistry(pub TypeRegistryArc);
|
|
|
|
impl Deref for AppTypeRegistry {
|
|
type Target = TypeRegistryArc;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for AppTypeRegistry {
|
|
#[inline]
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
/// A [`Resource`] storing [`FunctionRegistry`] for
|
|
/// function registrations relevant to a whole app.
|
|
///
|
|
/// [`FunctionRegistry`]: bevy_reflect::func::FunctionRegistry
|
|
#[cfg(feature = "reflect_functions")]
|
|
#[derive(Resource, Clone, Default)]
|
|
pub struct AppFunctionRegistry(pub bevy_reflect::func::FunctionRegistryArc);
|
|
|
|
#[cfg(feature = "reflect_functions")]
|
|
impl Deref for AppFunctionRegistry {
|
|
type Target = bevy_reflect::func::FunctionRegistryArc;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "reflect_functions")]
|
|
impl DerefMut for AppFunctionRegistry {
|
|
#[inline]
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
/// Creates a `T` from a `&dyn PartialReflect`.
|
|
///
|
|
/// This will try the following strategies, in this order:
|
|
///
|
|
/// - use the reflected `FromReflect`, if it's present and doesn't fail;
|
|
/// - use the reflected `Default`, if it's present, and then call `apply` on the result;
|
|
/// - use the reflected `FromWorld`, just like the `Default`.
|
|
///
|
|
/// The first one that is present and doesn't fail will be used.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// If any strategy produces a `Box<dyn Reflect>` that doesn't store a value of type `T`
|
|
/// this method will panic.
|
|
///
|
|
/// If none of the strategies succeed, this method will panic.
|
|
pub fn from_reflect_with_fallback<T: Reflect + TypePath>(
|
|
reflected: &dyn PartialReflect,
|
|
world: &mut World,
|
|
registry: &TypeRegistry,
|
|
) -> T {
|
|
fn different_type_error<T: TypePath>(reflected: &str) -> ! {
|
|
panic!(
|
|
"The registration for the reflected `{}` trait for the type `{}` produced \
|
|
a value of a different type",
|
|
reflected,
|
|
T::type_path(),
|
|
);
|
|
}
|
|
|
|
// First, try `FromReflect`. This is handled differently from the others because
|
|
// it doesn't need a subsequent `apply` and may fail.
|
|
if let Some(reflect_from_reflect) =
|
|
registry.get_type_data::<ReflectFromReflect>(TypeId::of::<T>())
|
|
{
|
|
// If it fails it's ok, we can continue checking `Default` and `FromWorld`.
|
|
if let Some(value) = reflect_from_reflect.from_reflect(reflected) {
|
|
return value
|
|
.take::<T>()
|
|
.unwrap_or_else(|_| different_type_error::<T>("FromReflect"));
|
|
}
|
|
}
|
|
|
|
// Create an instance of `T` using either the reflected `Default` or `FromWorld`.
|
|
let mut value = if let Some(reflect_default) =
|
|
registry.get_type_data::<ReflectDefault>(TypeId::of::<T>())
|
|
{
|
|
reflect_default
|
|
.default()
|
|
.take::<T>()
|
|
.unwrap_or_else(|_| different_type_error::<T>("Default"))
|
|
} else if let Some(reflect_from_world) =
|
|
registry.get_type_data::<ReflectFromWorld>(TypeId::of::<T>())
|
|
{
|
|
reflect_from_world
|
|
.from_world(world)
|
|
.take::<T>()
|
|
.unwrap_or_else(|_| different_type_error::<T>("FromWorld"))
|
|
} else {
|
|
panic!(
|
|
"Couldn't create an instance of `{}` using the reflected `FromReflect`, \
|
|
`Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \
|
|
or `#[reflect(FromWorld)]`?",
|
|
// FIXME: once we have unique reflect, use `TypePath`.
|
|
std::any::type_name::<T>(),
|
|
);
|
|
};
|
|
|
|
value.apply(reflected);
|
|
value
|
|
}
|