-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automatically register types for Reflect
#3936
Comments
I'd be strongly opposed to requiring |
That means you would now only sometimes need to manually register it, which I think is less clear. In addition because it is only rarely needed, it may be very confusing for the cases where it is necessary. |
Just calling out that if we had specialization, it could be both optional and implicit: pub trait RegisterReflect {
fn register_reflect(registry: &mut Registry);
}
impl<T> RegisterReflect for T {
default fn register_reflect(registry: &mut Registry) {
// Do nothing
}
}
#[derive(Reflect, Component)]
struct Foo {
}
// in derive(Reflect), generate this RegisterReflect impl for type
impl RegisterReflect for Foo {
fn register_reflect(registry: &mut Registry) {
registry.register::<Foo>()
}
}
// Component traits could then do something with the reflect impl
trait Component: RegisterReflect {
fn register_reflection(world: &mut World) {
let mut registry = world.get_resource_mut::<Registry>();
Self::register_reflect(&mut registry);
}
} |
I do think its worth entertaining the idea of marrying Component + Reflect. But I am also very wary of doing so. The pros have already been stated, so here are some cons:
Not (yet) convinced that this is a pattern we should support. Very non-rustey (getting all instances of a trait isn't a rust pattern) and footgun-ey (because it requires that the user manually ties the trait info to the reflected type in the derive. forgetting to do so for every relevant component type breaks the integrity of the query). Worth considering because it would be powerful, but I wouldn't use that as justification for merging Component + Reflect until that conversation has come to a conclusion. |
Ambee and Cart have convinced me that tying |
In case it's useful to anyone in the meantime I've written a macro (with the help of discord) that makes this a bit less arduous: macro_rules! define_components {
(@out_pass $plugin_name:ident
$(
$(#[$outer:meta])*
$vis:vis struct $name:ident$structdef:tt$(;)?
)*
) => {
fn build(&self, app: &mut bevy::prelude::App) {
app$(.register_type::<$name>())*;
}
};
(Type registration plugin: $plugin_name:ident $($tokens:tt)*) => {
$($tokens)*
pub struct $plugin_name;
impl Plugin for $plugin_name {
define_components!(
@out_pass $plugin_name $($tokens)*
);
}
};
} Used as follows: define_components! {
Type registration plugin: MyPluginName
#[derive(Component, Default, Reflect)]
#[reflect(Component)
pub struct Position(pub IVec2);
#[derive(Component, Default, Reflect)]
#[reflect(Component)
pub struct SomeComplexStruct {
with: String,
fields: u16,
}
} And then just You need to add impl blocks outside of the macro, but someone with better macro skills might be able to modify the macro to allow those inside too. |
#12332 provides a (hacky) way to do this without forcing a subtrait, conditionally injecting code via the #4154 also helped by removing the need for dependent types to be registered as well, so just the top level types need to be registered. Alternatively, have we entertained the idea of using |
dtolnay/inventory#71 does seem to suggest there is a path forward for using |
I have attempted something like this based on I would be interested in forking this concept to a bevy-specific auto-registration PoC, that hopefully can support WASM once |
What problem does this solve or what need does it fill?
As seen in the examples, each type that we wish to reflect must be manually registered.
This is a serious frustration when using type reflection, and severely limits the ability to implement richer features using it (like trait queries), as we have no guarantee that resources / components are reflectable.
What solution would you like?
Reflect
trait to fully support all reasonable data types. Add reflection for enum types #1347 is particularly critical.Resource
opt-in, using a derive macro.Resource
, and makeRes<T>: Resource + Send + Sync
.Resource
and force users to use#[derive(Resource)]
.Reflect
) all of the types used in it to theApp
that contains it. See the prior art on trait queries for an idea of how this might be done.Open questions
We may need to force an explicit
Event
trait / derive that handles reflection as well in order to ensure thatRes<Events<T>>
is reflectable.What alternative(s) have you considered?
Reducing this boilerplate is quite essential: currently, using scenes is very tedious due to this error-prone boilerplate.
However, there are several reasonable changes to consider:
Reflect
a required subtrait ofComponent
andResource
. This does not play nice when external types are embedded in components and resources.Reflect
in theComponent
/Resource
derive. This is technically clearer, but introduces advanced concepts right away and adds to boilerplate.Additional context
This is an old idea, and certainly not entirely my own. It was previously discussed in #1843 / #2254 / bevyengine/rfcs#27.
This strategy is also useful for including other information in the appropriate traits, such as the size of assets / components / resources (seen in https://github.com/BGR360/bevy_datasize).
The same technology used above for trait registration would be very useful for #1515.
The text was updated successfully, but these errors were encountered: