-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
implement insert and remove reflected entity commands #8895
implement insert and remove reflected entity commands #8895
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. Along with my general nitpicks and suggestions, I left a few comments open for discussion for other reviewers.
Overall, I'm still on the fence about this change. On the one hand, it makes reflection more immediately useful to users without having to understand the complexities of ReflectComponent
. On the other hand, the actual command is pretty straightforward if someone wanted to just write it themselves with a custom command or closure command (not trying to downplay this change btw, just want us to be mindful of what we put on a very core API).
It'd be nice to get some input from someone more on the ECS side of things (since for me, all the reflection stuff looks great!).
/// An extension trait for [`EntityCommands`] for reflection related functions | ||
pub trait EntityCommandsReflectExtension { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussion: This is based on my suggestion to use an extension trait in order to help create a separation from reflection-based behaviors. My original reasoning was that we don't want beginner users reaching for this option right out of the gate (especially since reflection still has some quirks that make it not very beginner friendly). And hopefully we can bring this in fully once reflection/scenes are a little more fleshed out.
However,— to argue against myself for a moment,— it's a seemingly "unnecessary" separation from a user perspective, especially since it requires a completely separate import. On top of this, it makes finding this functionality much more difficult for users who do need it.
I'd like to hear feedback from others (including you, @NoahShomette haha) on opinions here. Should we just make these proper methods on EntityCommands
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the separation makes logical sense and I do like it being separated because at least from a code perspective it keeps all the more special purpose reflection functions away from the general functions which should be much more commonly used. However it could go either way and I don't really have a strong opinion on it. I do think if we added more functions then the division might get more useful just from a code base perspective since the actual EntityCommands file is getting rather big already. It also depends on how "public" reflection related things are supposed to be which I don't have a great gauge on so I'll defer to others on that point.
Additionally I know at least for me I use an IDE and I very rarely purposefully import things. I might be an outlier but I would assume most people use something that will autocomplete and suggest related functions even if they aren't already imported which would make the discoverability point much less damning.
I'd overall be in favor of keeping the extension trait.
I definitely agree its a small change and easily implemented by the end user (although EntityCommands needs public fields for that technically first), however it was a bit disconcerting to me that there wasn't any way to deferred insert reflect components when I ran into it. I think Bevy focuses enough on this and its a big part of end user actions, specifically interacting with commands and thinking deferred and parallel, that it makes sense to me to have first party support for this kind of thing. Its a little thing but it helps to make reflect more usable and more in line with the rest of the engine imo. |
# Objective - Use `AppTypeRegistry` on API defined in `bevy_ecs` (#8895 (comment)) A lot of the API on `Reflect` depends on a registry. When it comes to the ECS. We should use `AppTypeRegistry` in the general case. This is however impossible in `bevy_ecs`, since `AppTypeRegistry` is defined in `bevy_app`. ## Solution - Move `AppTypeRegistry` resource definition from `bevy_app` to `bevy_ecs` - Still add the resource in the `App` plugin, since bevy_ecs itself doesn't know of plugins Note that `bevy_ecs` is a dependency of `bevy_app`, so nothing revolutionary happens. ## Alternative - Define the API as a trait in `bevy_app` over `bevy_ecs`. (though this prevents us from using bevy_ecs internals) - Do not rely on `AppTypeRegistry` for the API in question, requring users to extract themselves the resource and pass it to the API methods. --- ## Changelog - Moved `AppTypeRegistry` resource definition from `bevy_app` to `bevy_ecs` ## Migration Guide - If you were **not** using a `prelude::*` to import `AppTypeRegistry`, you should update your imports: ```diff - use bevy::app::AppTypeRegistry; + use bevy::ecs::reflect::AppTypeRegistry ```
With #8901, it's now possible to use |
FYI if you want this to go forward, you need to address the comments reviewers made. |
1192171
to
a677efc
Compare
Apologies for the absolutely horrible PR timeline now, but I think it should be fixed from a commit point of view and all of the conversations should be resolved. I added additional Also apologies for the delay in updating this but I fell ill and just got super busy :( |
I'm not sure what the standard is for commands, but would something like this be possible for maximum flexibility? We do have some operations in the actual command call but it allows any TypeRegistry to be used rather than just resources and it only involves cloning a ReflectComponent (Which I dont believe would be a huge problem) fn remove_reflected_with_registry(
&mut self,
component_type_name: String,
type_registry: &TypeRegistry
) -> &mut Self {
let type_info = self.component.type_name();
let Some(type_registration) = registry.read().get_with_name(type_info) else {
panic!("Could not get type registration (for component type {}) because it doesn't exist in the TypeRegistry.", component.type_name());
}
let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
panic!("Could not get ReflectComponent data (for component type {}) because it doesn't exist in this TypeRegistration.", component.type_name());
}
self.commands.add(RemoveReflectedWithRegistry {
entity: self.entity,
reflect_component: reflect_component.clone(),
});
self
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the first half of my review. I'm giving feedback on the actual code next.
/// } | ||
/// | ||
/// ``` | ||
fn remove_reflected(&mut self, component_type_name: String) -> &mut Self; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest using a impl Into<Cow<'static, str>>
here instead of String
so that it's possible to use the API without allocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this, I believe I put it in correctly but I would appreciate a double check as I wasn't able to get it to work without having to call .to_owned()
when passing stuff into the function and I'm not sure if thats an issue with it now. I was getting lifetime bounds errors which you can replicate by just removing the .to_owned()
from the tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks excellent! I'd require just some minor adjustments.
Awesome thank you so much! I implemented all your feedback so it should be good to go from that regards! |
Example |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! LGTM
return; | ||
|
||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return; | |
}; | |
return; | |
}; |
return; | ||
|
||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return; | |
}; | |
return; | |
}; |
Just don't worry about CI, it's broken. Also don't worry about formatting within |
Haha that works for me! Thank you very much for reviewing and helping me out! |
I realized that the two |
@NoahShomette this needs to be formatted, but once that's done I'll merge it! |
Done! |
# Objective To enable non exclusive system usage of reflected components and make reflection more ergonomic to use by making it more in line with standard entity commands. ## Solution - Implements a new `EntityCommands` extension trait for reflection related functions in the reflect module of bevy_ecs. - Implements 4 new commands, `insert_reflect`, `insert_reflect_with_registry`, `remove_reflect`, and `remove_reflect_with_registry`. Both insert commands take a `Box<dyn Reflect>` component while the remove commands take the component type name. - Made `EntityCommands` fields pub(crate) to allow access in the reflect module. (Might be worth making these just public to enable user end custom entity commands in a different pr) - Added basic tests to ensure the commands are actually working. - Documentation of functions. --- ## Changelog Added: - Implements 4 new commands on the new entity commands extension. - `insert_reflect` - `remove_reflect` - `insert_reflect_with_registry` - `remove_reflect_with_registry` The commands operate the same except the with_registry commands take a generic parameter for a resource that implements `AsRef<TypeRegistry>`. Otherwise the default commands use the `AppTypeRegistry` for reflection data. Changed: - Made `EntityCommands` fields pub(crate) to allow access in the reflect module. > Hopefully this time it works. Please don't make me rebase again ☹
# Objective To enable non exclusive system usage of reflected components and make reflection more ergonomic to use by making it more in line with standard entity commands. ## Solution - Implements a new `EntityCommands` extension trait for reflection related functions in the reflect module of bevy_ecs. - Implements 4 new commands, `insert_reflect`, `insert_reflect_with_registry`, `remove_reflect`, and `remove_reflect_with_registry`. Both insert commands take a `Box<dyn Reflect>` component while the remove commands take the component type name. - Made `EntityCommands` fields pub(crate) to allow access in the reflect module. (Might be worth making these just public to enable user end custom entity commands in a different pr) - Added basic tests to ensure the commands are actually working. - Documentation of functions. --- ## Changelog Added: - Implements 4 new commands on the new entity commands extension. - `insert_reflect` - `remove_reflect` - `insert_reflect_with_registry` - `remove_reflect_with_registry` The commands operate the same except the with_registry commands take a generic parameter for a resource that implements `AsRef<TypeRegistry>`. Otherwise the default commands use the `AppTypeRegistry` for reflection data. Changed: - Made `EntityCommands` fields pub(crate) to allow access in the reflect module. > Hopefully this time it works. Please don't make me rebase again ☹
…tyWorldMut` (#16999) ## Objective Commands were previously limited to structs that implemented `Command`. Now there are blanket implementations for closures, which (in my opinion) are generally preferable. Internal commands within `commands/mod.rs` have been switched from structs to closures, but there are a number of internal commands in other areas of the engine that still use structs. I'd like to tidy these up by moving their implementations to methods on `World`/`EntityWorldMut` and changing `Commands` to use those methods through closures. This PR handles the following: - `TriggerEvent` and `EmitDynamicTrigger` double as commands and helper structs, and can just be moved to `World` methods. - Four structs that enabled insertion/removal of components via reflection. This functionality shouldn't be exclusive to commands, and can be added to `EntityWorldMut`. - Five structs that mostly just wrapped `World` methods, and can be replaced with closures that do the same thing. ## Solution - __Observer Triggers__ (`observer/trigger_event.rs` and `observer/mod.rs`) - Moved the internals of `TriggerEvent` to the `World` methods that used it. - Replaced `EmitDynamicTrigger` with two `World` methods: - `trigger_targets_dynamic` - `trigger_targets_dynamic_ref` - `TriggerTargets` was now the only thing in `observer/trigger_event.rs`, so it's been moved to `observer/mod.rs` and `trigger_event.rs` was deleted. - __Reflection Insert/Remove__ (`reflect/entity_commands.rs`) - Replaced the following `Command` impls with equivalent methods on `EntityWorldMut`: - `InsertReflect` -> `insert_reflect` - `InsertReflectWithRegistry` -> `insert_reflect_with_registry` - `RemoveReflect` -> `remove_reflect` - `RemoveReflectWithRegistry` -> `remove_reflect_with_registry` - __System Registration__ (`system/system_registry.rs`) - The following `Command` impls just wrapped a `World` method and have been replaced with closures: - `RunSystemWith` - `UnregisterSystem` - `RunSystemCachedWith` - `UnregisterSystemCached` - `RegisterSystem` called a helper function that basically worked as a constructor for `RegisteredSystem` and made sure it came with a marker component. That helper function has been replaced with `RegisteredSystem::new` and a `#[require]`. ## Possible Addition The extension trait that adds the reflection commands, `ReflectCommandExt`, isn't strictly necessary; we could just `impl EntityCommands`. We could even move them to the same files as the main impls and put it behind a `#[cfg]`. The PR that added it [had a similar conversation](#8895 (comment)) and decided to stick with the trait, but we could revisit it here if so desired.
Objective
To enable non exclusive system usage of reflected components and make reflection more ergonomic to use by making it more in line with standard entity commands.
Solution
Implements a new
EntityCommands
extension trait for reflection related functions in the reflect module of bevy_ecs.Implements 4 new commands,
insert_reflect
,insert_reflect_with_registry
,remove_reflect
, andremove_reflect_with_registry
. Both insert commands take aBox<dyn Reflect>
component while the remove commands take the component type name.Made
EntityCommands
fields pub(crate) to allow access in the reflect module. (Might be worth making these just public to enable user end custom entity commands in a different pr)Added basic tests to ensure the commands are actually working.
Documentation of functions.
Changelog
Added:
insert_reflect
remove_reflect
insert_reflect_with_registry
remove_reflect_with_registry
The commands operate the same except the with_registry commands take a generic parameter for a resource that implements
AsRef<TypeRegistry>
. Otherwise the default commands use theAppTypeRegistry
for reflection data.Changed:
EntityCommands
fields pub(crate) to allow access in the reflect module.