a35811d088
183 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
a35811d088
|
Add Immutable Component Support (#16372)
# Objective - Fixes #16208 ## Solution - Added an associated type to `Component`, `Mutability`, which flags whether a component is mutable, or immutable. If `Mutability= Mutable`, the component is mutable. If `Mutability= Immutable`, the component is immutable. - Updated `derive_component` to default to mutable unless an `#[component(immutable)]` attribute is added. - Updated `ReflectComponent` to check if a component is mutable and, if not, panic when attempting to mutate. ## Testing - CI - `immutable_components` example. --- ## Showcase Users can now mark a component as `#[component(immutable)]` to prevent safe mutation of a component while it is attached to an entity: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` This prevents creating an exclusive reference to the component while it is attached to an entity. This is particularly powerful when combined with component hooks, as you can now fully track a component's value, ensuring whatever invariants you desire are upheld. Before this would be done my making a component private, and manually creating a `QueryData` implementation which only permitted read access. <details> <summary>Using immutable components as an index</summary> ```rust /// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)] #[component( immutable, on_insert = on_insert_name, on_replace = on_replace_name, )] pub struct Name(pub &'static str); /// This index allows for O(1) lookups of an [`Entity`] by its [`Name`]. #[derive(Resource, Default)] struct NameIndex { name_to_entity: HashMap<Name, Entity>, } impl NameIndex { fn get_entity(&self, name: &'static str) -> Option<Entity> { self.name_to_entity.get(&Name(name)).copied() } } fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.insert(name, entity); } fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.remove(&name); } // Setup our name index world.init_resource::<NameIndex>(); // Spawn some entities! let alyssa = world.spawn(Name("Alyssa")).id(); let javier = world.spawn(Name("Javier")).id(); // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Alyssa"), Some(alyssa)); assert_eq!(index.get_entity("Javier"), Some(javier)); // Changing the name of an entity is also fully capture by our index world.entity_mut(javier).insert(Name("Steven")); // Javier changed their name to Steven let steven = javier; // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Javier"), None); assert_eq!(index.get_entity("Steven"), Some(steven)); ``` </details> Additionally, users can use `Component<Mutability = ...>` in trait bounds to enforce that a component _is_ mutable or _is_ immutable. When using `Component` as a trait bound without specifying `Mutability`, any component is applicable. However, methods which only work on mutable or immutable components are unavailable, since the compiler must be pessimistic about the type. ## Migration Guide - When implementing `Component` manually, you must now provide a type for `Mutability`. The type `Mutable` provides equivalent behaviour to earlier versions of `Component`: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` - When working with generic components, you may need to specify that your generic parameter implements `Component<Mutability = Mutable>` rather than `Component` if you require mutable access to said component. - The entity entry API has had to have some changes made to minimise friction when working with immutable components. Methods which previously returned a `Mut<T>` will now typically return an `OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get the `Mut<T>` item again. ## Draft Release Notes Components can now be made immutable while stored within the ECS. Components are the fundamental unit of data within an ECS, and Bevy provides a number of ways to work with them that align with Rust's rules around ownership and borrowing. One part of this is hooks, which allow for defining custom behavior at key points in a component's lifecycle, such as addition and removal. However, there is currently no way to respond to _mutation_ of a component using hooks. The reasons for this are quite technical, but to summarize, their addition poses a significant challenge to Bevy's core promises around performance. Without mutation hooks, it's relatively trivial to modify a component in such a way that breaks invariants it intends to uphold. For example, you can use `core::mem::swap` to swap the components of two entities, bypassing the insertion and removal hooks. This means the only way to react to this modification is via change detection in a system, which then begs the question of what happens _between_ that alteration and the next run of that system? Alternatively, you could make your component private to prevent mutation, but now you need to provide commands and a custom `QueryData` implementation to allow users to interact with your component at all. Immutable components solve this problem by preventing the creation of an exclusive reference to the component entirely. Without an exclusive reference, the only way to modify an immutable component is via removal or replacement, which is fully captured by component hooks. To make a component immutable, simply add `#[component(immutable)]`: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` When implementing `Component` manually, there is an associated type `Mutability` which controls this behavior: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` Note that this means when working with generic components, you may need to specify that a component is mutable to gain access to certain methods: ```rust // Before fn bar<C: Component>() { // ... } // After fn bar<C: Component<Mutability = Mutable>>() { // ... } ``` With this new tool, creating index components, or caching data on an entity should be more user friendly, allowing libraries to provide APIs relying on components and hooks to uphold their invariants. ## Notes - ~~I've done my best to implement this feature, but I'm not happy with how reflection has turned out. If any reflection SMEs know a way to improve this situation I'd greatly appreciate it.~~ There is an outstanding issue around the fallibility of mutable methods on `ReflectComponent`, but the DX is largely unchanged from `main` now. - I've attempted to prevent all safe mutable access to a component that does not implement `Component<Mutability = Mutable>`, but there may still be some methods I have missed. Please indicate so and I will address them, as they are bugs. - Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever you do with unsafe is between you and your compiler. - I am marking this PR as ready, but I suspect it will undergo fairly major revisions based on SME feedback. - I've marked this PR as _Uncontroversial_ based on the feature, not the implementation. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Nuutti Kotivuori <naked@iki.fi> |
||
![]() |
d92fc1e456
|
Move required components doc to type doc (#16575)
# Objective Make documentation of a component's required components more visible by moving it to the type's docs ## Solution Change `#[require]` from a derive macro helper to an attribute macro. Disadvantages: - this silences any unused code warnings on the component, as it is used by the macro! - need to import `require` if not using the ecs prelude (I have not included this in the migration guilde as Rust tooling already suggests the fix) --- ## Showcase  --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> |
||
![]() |
2e267bba5a
|
Entity cloning (#16132)
## Objective Fixes #1515 This PR implements a flexible entity cloning system. The primary use case for it is to clone dynamically-generated entities. Example: ```rs #[derive(Component, Clone)] pub struct Projectile; #[derive(Component, Clone)] pub struct Damage { value: f32, } fn player_input( mut commands: Commands, projectiles: Query<Entity, With<Projectile>>, input: Res<ButtonInput<KeyCode>>, ) { // Fire a projectile if input.just_pressed(KeyCode::KeyF) { commands.spawn((Projectile, Damage { value: 10.0 })); } // Triplicate all active projectiles if input.just_pressed(KeyCode::KeyT) { for projectile in projectiles.iter() { // To triplicate a projectile we need to create 2 more clones for _ in 0..2{ commands.clone_entity(projectile) } } } } ``` ## Solution ### Commands Add a `clone_entity` command to create a clone of an entity with all components that can be cloned. Components that can't be cloned will be ignored. ```rs commands.clone_entity(entity) ``` If there is a need to configure the cloning process (like set to clone recursively), there is a second command: ```rs commands.clone_entity_with(entity, |builder| { builder.recursive(true) }); ``` Both of these commands return `EntityCommands` of the cloned entity, so the copy can be modified afterwards. ### Builder All these commands use `EntityCloneBuilder` internally. If there is a need to clone an entity using `World` instead, it is also possible: ```rs let entity = world.spawn(Component).id(); let entity_clone = world.spawn_empty().id(); EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone); ``` Builder has methods to `allow` or `deny` certain components during cloning if required and can be extended by implementing traits on it. This PR includes two `EntityCloneBuilder` extensions: `CloneEntityWithObserversExt` to configure adding cloned entity to observers of the original entity, and `CloneEntityRecursiveExt` to configure cloning an entity recursively. ### Clone implementations By default, all components that implement either `Clone` or `Reflect` will be cloned (with `Clone`-based implementation preferred in case component implements both). This can be overriden on a per-component basis: ```rs impl Component for SomeComponent { const STORAGE_TYPE: StorageType = StorageType::Table; fn get_component_clone_handler() -> ComponentCloneHandler { // Don't clone this component ComponentCloneHandler::Ignore } } ``` ### `ComponentCloneHandlers` Clone implementation specified in `get_component_clone_handler` will get registered in `ComponentCloneHandlers` (stored in `bevy_ecs::component::Components`) at component registration time. The clone handler implementation provided by a component can be overriden after registration like so: ```rs let component_id = world.components().component_id::<Component>().unwrap() world.get_component_clone_handlers_mut() .set_component_handler(component_id, ComponentCloneHandler::Custom(component_clone_custom)) ``` The default clone handler for all components that do not explicitly define one (or don't derive `Component`) is `component_clone_via_reflect` if `bevy_reflect` feature is enabled, and `component_clone_ignore` (noop) otherwise. Default handler can be overriden using `ComponentCloneHandlers::set_default_handler` ### Handlers Component clone handlers can be used to modify component cloning behavior. The general signature for a handler that can be used in `ComponentCloneHandler::Custom` is as follows: ```rs pub fn component_clone_custom( world: &mut DeferredWorld, entity_cloner: &EntityCloner, ) { // implementation } ``` The `EntityCloner` implementation (used internally by `EntityCloneBuilder`) assumes that after calling this custom handler, the `target` entity has the desired version of the component from the `source` entity. ### Builder handler overrides Besides component-defined and world-overriden handlers, `EntityCloneBuilder` also has a way to override handlers locally. It is mainly used to allow configuration methods like `recursive` and `add_observers`. ```rs // From observer clone handler implementation impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { fn add_observers(&mut self, add_observers: bool) -> &mut Self { if add_observers { self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom( component_clone_observed_by, )) } else { self.remove_component_clone_handler_override::<ObservedBy>() } } } ``` ## Testing Includes some basic functionality tests and doctests. Performance-wise this feature is the same as calling `clone` followed by `insert` for every entity component. There is also some inherent overhead due to every component clone handler having to access component data through `World`, but this can be reduced without breaking current public API in a later PR. |
||
![]() |
db1915a1f0
|
Use the fully qualified name for Component in the require attribute (#16378)
# Objective - Describe the objective or issue this PR addresses. Use the fully qualified name for `Component` in the `require` attribute - If you're fixing a specific issue, say "Fixes #X". Fixes #16377 ## Solution - Describe the solution used to achieve the objective above. Use the fully qualified name for `Component` in the `require` attribute, i.e.,`<#ident as #bevy_ecs_path::component::Component>` ## Testing - Did you test these changes? If so, how? `cargo run -p ci -- lints` `cargo run -p ci -- compile` `cargo run -p ci -- test` - Are there any parts that need more testing? no - How can other people (reviewers) test your changes? Is there anything specific they need to know? try to compile ```rust #[derive(::bevy::ecs::component::Component, Default)] pub struct A; #[derive(::bevy::ecs::component::Component)] #[require(A)] pub struct B; ``` - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? Mac only --- </details> ## Migration Guide > This section is optional. If there are no breaking changes, you can delete this section. - If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes - Simply adding new functionality is not a breaking change. - Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change. Co-authored-by: Volodymyr Enhelhardt <volodymyr.enhelhardt@ambr.net> |
||
![]() |
8039f34b0d
|
bevy_ecs: Replace panics in QueryData derive compile errors (#15691)
# Objective The current `QueryData` derive panics when it encounters an error. Additionally, it doesn't provide the clearest error message: ```rust #[derive(QueryData)] #[query_data(mut)] struct Foo { // ... } ``` ``` error: proc-macro derive panicked --> src/foo.rs:16:10 | 16 | #[derive(QueryData)] | ^^^^^^^^^ | = help: message: Invalid `query_data` attribute format ``` ## Solution Updated the derive logic to not panic and gave a bit more detail in the error message. This is makes the error message just a bit clearer and maintains the correct span: ``` error: invalid attribute, expected `mutable` or `derive` --> src/foo.rs:17:14 | 17 | #[query_data(mut)] | ^^^ ``` ## Testing You can test locally by running the following in `crates/bevy_ecs/compile_fail`: ``` cargo test --target-dir ../../../target ``` |
||
![]() |
f0704cffa4
|
Allow a closure to be used as a required component default (#15269)
# Objective Allow required component default values to be provided in-line. ```rust #[derive(Component)] #[require( FocusPolicy(block_focus_policy) )] struct SomeComponent; fn block_focus_policy() -> FocusPolicy { FocusPolicy::Block } ``` May now be expressed as: ```rust #[derive(Component)] #[require( FocusPolicy(|| FocusPolicy::Block) )] struct SomeComponent; ``` ## Solution Modified the #[require] proc macro to accept a closure. ## Testing Tested using my branch as a dependency, and switching between the inline closure syntax and function syntax for a bunch of different components. |
||
![]() |
8fb55dbf59
|
Implement SystemParam::queue() method for blanket implementation of ParamSet (#15599)
# Objective The `queue()` method is an optional trait method which is necessary for deferred operations (such as command queues) to work properly in the context of an observer. This method was omitted from the proc_macro blanket implementation of `ParamSet` for tuples; as a result, SystemParams with deferred application (such as Commands) would not work in observers if they were part of a ParamSet. This appears to have been a simple omission, as `queue()` was already implemented for the separate blanket implementation of `ParamSet` for `Vec<T>`. In both cases, it is a simple pass-through to the component SystemParams. ## Solution Add the `queue()` method implementation to the `impl_param_set` proco macro. ## Testing Added a unit test which clearly demonstrates the issue. It fails before the fix, and passes afterwards. --- |
||
![]() |
f3e8ae03cd
|
Runtime required components (#15458)
# Objective Fixes #15367. Currently, required components can only be defined through the `require` macro attribute. While this should be used in most cases, there are also several instances where you may want to define requirements at runtime, commonly in plugins. Example use cases: - Require components only if the relevant optional plugins are enabled. For example, a `SleepTimer` component (for physics) is only relevant if the `SleepPlugin` is enabled. - Third party crates can define their own requirements for first party types. For example, "each `Handle<Mesh>` should require my custom rendering data components". This also gets around the orphan rule. - Generic plugins that add marker components based on the existence of other components, like a generic `ColliderPlugin<C: AnyCollider>` that wants to add a `ColliderMarker` component for all types of colliders. - This is currently relevant for the retained render world in #15320. The `ExtractComponentPlugin<C>` should add `SyncToRenderWorld` to all components that should be extracted. This is currently done with observers, which is more expensive than required components, and causes archetype moves. - Replace some built-in components with custom versions. For example, if `GlobalTransform` required `Transform` through `TransformPlugin`, but we wanted to use a `CustomTransform` type, we could replace `TransformPlugin` with our own plugin. (This specific example isn't good, but there are likely better use cases where this may be useful) See #15367 for more in-depth reasoning. ## Solution Add `register_required_components::<T, R>` and `register_required_components_with::<T, R>` methods for `Default` and custom constructors respectively. These methods exist on `App` and `World`. ```rust struct BirdPlugin; impl Plugin for BirdPlugin { fn plugin(app: &mut App) { // Make `Bird` require `Wings` with a `Default` constructor. app.register_required_components::<Bird, Wings>(); // Make `Wings` require `FlapSpeed` with a custom constructor. // Fun fact: Some hummingbirds can flutter their wings 80 times per second! app.register_required_components_with::<Wings, FlapSpeed>(|| FlapSpeed::from_duration(1.0 / 80.0)); } } ``` The custom constructor is a function pointer to match the `require` API, though it could take a raw value too. Requirement inheritance works similarly as with the `require` attribute. If `Bird` required `FlapSpeed` directly, it would take precedence over indirectly requiring it through `Wings`. The same logic applies to all levels of the inheritance tree. Note that registering the same component requirement more than once will panic, similarly to trying to add multiple component hooks of the same type to the same component. This avoids constructor conflicts and confusing ordering issues. ### Implementation Runtime requirements have two additional challenges in comparison to the `require` attribute. 1. The `require` attribute uses recursion and macros with clever ordering to populate hash maps of required components for each component type. The expected semantics are that "more specific" requirements override ones deeper in the inheritance tree. However, at runtime, there is no representation of how "specific" each requirement is. 2. If you first register the requirement `X -> Y`, and later register `Y -> Z`, then `X` should also indirectly require `Z`. However, `Y` itself doesn't know that it is required by `X`, so it's not aware that it should update the list of required components for `X`. My solutions to these problems are: 1. Store the depth in the inheritance tree for each entry of a given component's `RequiredComponents`. This is used to determine how "specific" each requirement is. For `require`-based registration, these depths are computed as part of the recursion. 2. Store and maintain a `required_by` list in each component's `ComponentInfo`, next to `required_components`. For `require`-based registration, these are also added after each registration, as part of the recursion. When calling `register_required_components`, it works as follows: 1. Get the required components of `Foo`, and check that `Bar` isn't already a *direct* requirement. 3. Register `Bar` as a required component for `Foo`, and add `Foo` to the `required_by` list for `Bar`. 4. Find and register all indirect requirements inherited from `Bar`, adding `Foo` to the `required_by` list for each component. 5. Iterate through components that require `Foo`, registering the new inherited requires for them as indirect requirements. The runtime registration is likely slightly more expensive than the `require` version, but it is a one-time cost, and quite negligible in practice, unless projects have hundreds or thousands of runtime requirements. I have not benchmarked this however. This does also add a small amount of extra cost to the `require` attribute for updating `required_by` lists, but I expect it to be very minor. ## Testing I added some tests that are copies of the `require` versions, as well as some tests that are more specific to the runtime implementation. I might add a few more tests though. ## Discussion - Is `register_required_components` a good name? Originally I went for `register_component_requirement` to be consistent with `register_component_hooks`, but the general feature is often referred to as "required components", which is why I changed it to `register_required_components`. - Should we *not* panic for duplicate requirements? If so, should they just be ignored, or should the latest registration overwrite earlier ones? - If we do want to panic for duplicate, conflicting registrations, should we at least not panic if the registrations are *exactly* the same, i.e. same component and same constructor? The current implementation panics for all duplicate direct registrations regardless of the constructor. ## Next Steps - Allow `register_required_components` to take a `Bundle` instead of a single required component. - I could also try to do it in this PR if that would be preferable. - Not directly related, but archetype invariants? |
||
![]() |
f97eba2082
|
Add VisitEntities for generic and reflectable Entity iteration (#15425)
# Objective - Provide a generic and _reflectable_ way to iterate over contained entities ## Solution Adds two new traits: * `VisitEntities`: Reflectable iteration, accepts a closure rather than producing an iterator. Implemented by default for `IntoIterator` implementing types. A proc macro is also provided. * A `Mut` variant of the above. Its derive macro uses the same field attribute to avoid repetition. ## Testing Added a test for `VisitEntities` that also transitively tests its derive macro as well as the default `MapEntities` impl. |
||
![]() |
d70595b667
|
Add core and alloc over std Lints (#15281)
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
27bea6abf7
|
Bubbling observers traversal should use query data (#15385)
# Objective Fixes #14331 ## Solution - Make `Traversal` a subtrait of `ReadOnlyQueryData` - Update implementations and usages ## Testing - Updated unit tests ## Migration Guide Update implementations of `Traversal`. --------- Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com> |
||
![]() |
e312da8c52
|
Reduce runtime panics through SystemParam validation (#15276)
# Objective The goal of this PR is to introduce `SystemParam` validation in order to reduce runtime panics. Fixes #15265 ## Solution `SystemParam` now has a new method `validate_param(...) -> bool`, which takes immutable variants of `get_param` arguments. The returned value indicates whether the parameter can be acquired from the world. If parameters cannot be acquired for a system, it won't be executed, similarly to run conditions. This reduces panics when using params like `Res`, `ResMut`, etc. as well as allows for new, ergonomic params like #15264 or #15302. Param validation happens at the level of executors. All validation happens directly before executing a system, in case of normal systems they are skipped, in case of conditions they return false. Warning about system skipping is primitive and subject to change in subsequent PRs. ## Testing Two executor tests check that all executors: - skip systems which have invalid parameters: - piped systems get skipped together, - dependent systems still run correctly, - skip systems with invalid run conditions: - system conditions have invalid parameters, - system set conditions have invalid parameters. |
||
![]() |
fd329c0426
|
Allow to expect (adopted) (#15301)
# Objective > Rust 1.81 released the #[expect(...)] attribute, which works like #[allow(...)] but throws a warning if the lint isn't raised. This is preferred to #[allow(...)] because it tells us when it can be removed. - Adopts the parts of #15118 that are complete, and updates the branch so it can be merged. - There were a few conflicts, let me know if I misjudged any of 'em. Alice's [recommendation](https://github.com/bevyengine/bevy/issues/15059#issuecomment-2349263900) seems well-taken, let's do this crate by crate now that @BD103 has done the lion's share of this! (Relates to, but doesn't yet completely finish #15059.) Crates this _doesn't_ cover: - bevy_input - bevy_gilrs - bevy_window - bevy_winit - bevy_state - bevy_render - bevy_picking - bevy_core_pipeline - bevy_sprite - bevy_text - bevy_pbr - bevy_ui - bevy_gltf - bevy_gizmos - bevy_dev_tools - bevy_internal - bevy_dylib --------- Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: Ben Frankel <ben.frankel7@gmail.com> Co-authored-by: Antony <antony.m.3012@gmail.com> |
||
![]() |
a9d2a9ea37
|
Make QueryFilter an unsafe trait (#14790)
# Objective It's possible to create UB using an implementation of `QueryFilter` that performs mutable access, but that does not violate any documented safety invariants. This code: ```rust #[derive(Component)] struct Foo(usize); // This derive is a simple way to get a valid WorldQuery impl. The QueryData impl isn't used. #[derive(QueryData)] #[query_data(mutable)] struct BadFilter<'w> { foo: &'w mut Foo, } impl QueryFilter for BadFilter<'_> { const IS_ARCHETYPAL: bool = false; unsafe fn filter_fetch( fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow, ) -> bool { // SAFETY: fetch and filter_fetch have the same safety requirements let f: &mut usize = &mut unsafe { Self::fetch(fetch, entity, table_row) }.foo.0; println!("Got &mut at {f:p}"); true } } let mut world = World::new(); world.spawn(Foo(0)); world.run_system_once(|query: Query<&Foo, BadFilter>| { let f: &usize = &query.iter().next().unwrap().0; println!("Got & at {f:p}"); query.iter().next().unwrap(); println!("Still have & at {f:p}"); }); ``` prints: ``` Got &mut at 0x1924b92dfb0 Got & at 0x1924b92dfb0 Got &mut at 0x1924b92dfb0 Still have & at 0x1924b92dfb0 ``` Which means it had an `&` and `&mut` alive at the same time. The only `unsafe` there is around `Self::fetch`, but I believe that call correctly upholds the safety invariant, and matches what `Added` and `Changed` do. ## Solution Make `QueryFilter` an unsafe trait and document the requirement that the `WorldQuery` implementation be read-only. ## Migration Guide `QueryFilter` is now an `unsafe trait`. If you were manually implementing it, you will need to verify that the `WorldQuery` implementation is read-only and then add the `unsafe` keyword to the `impl`. |
||
![]() |
3a8d5598ad
|
Interpolate WorldQuery path in docs of generated types (#14985)
# Objective Fixes #14972 ## Solution Uses the `concat!` macro to interpolate the `path` variable. ## Testing * Run `cargo doc --workspace --open` * Check functionality of `WorldQuery` links within `NodeQueryItem`, `NodeQueryReadOnly`, `NodeQueryReadOnlyItem` docs |
||
![]() |
4be8e497ca
|
SystemParamBuilder - Allow deriving a SystemParamBuilder struct when deriving SystemParam. (#14818)
# Objective Allow `SystemParamBuilder` implementations for custom system parameters created using `#[derive(SystemParam)]`. ## Solution Extend the derive macro to accept a `#[system_param(builder)]` attribute. When present, emit a builder type with a field corresponding to each field of the param. ## Example ```rust #[derive(SystemParam)] #[system_param(builder)] struct CustomParam<'w, 's> { query: Query<'w, 's, ()>, local: Local<'s, usize>, } let system = (CustomParamBuilder { local: LocalBuilder(100), query: QueryParamBuilder::new(|builder| { builder.with::<A>(); }), },) .build_state(&mut world) .build_system(|param: CustomParam| *param.local + param.query.iter().count()); ``` |
||
![]() |
9cdb915809
|
Required Components (#14791)
## Introduction This is the first step in my [Next Generation Scene / UI Proposal](https://github.com/bevyengine/bevy/discussions/14437). Fixes https://github.com/bevyengine/bevy/issues/7272 #14800. Bevy's current Bundles as the "unit of construction" hamstring the UI user experience and have been a pain point in the Bevy ecosystem generally when composing scenes: * They are an additional _object defining_ concept, which must be learned separately from components. Notably, Bundles _are not present at runtime_, which is confusing and limiting. * They can completely erase the _defining component_ during Bundle init. For example, `ButtonBundle { style: Style::default(), ..default() }` _makes no mention_ of the `Button` component symbol, which is what makes the Entity a "button"! * They are not capable of representing "dependency inheritance" without completely non-viable / ergonomically crushing nested bundles. This limitation is especially painful in UI scenarios, but it applies to everything across the board. * They introduce a bunch of additional nesting when defining scenes, making them ugly to look at * They introduce component name "stutter": `SomeBundle { component_name: ComponentName::new() }` * They require copious sprinklings of `..default()` when spawning them in Rust code, due to the additional layer of nesting **Required Components** solve this by allowing you to define which components a given component needs, and how to construct those components when they aren't explicitly provided. This is what a `ButtonBundle` looks like with Bundles (the current approach): ```rust #[derive(Component, Default)] struct Button; #[derive(Bundle, Default)] struct ButtonBundle { pub button: Button, pub node: Node, pub style: Style, pub interaction: Interaction, pub focus_policy: FocusPolicy, pub border_color: BorderColor, pub border_radius: BorderRadius, pub image: UiImage, pub transform: Transform, pub global_transform: GlobalTransform, pub visibility: Visibility, pub inherited_visibility: InheritedVisibility, pub view_visibility: ViewVisibility, pub z_index: ZIndex, } commands.spawn(ButtonBundle { style: Style { width: Val::Px(100.0), height: Val::Px(50.0), ..default() }, focus_policy: FocusPolicy::Block, ..default() }) ``` And this is what it looks like with Required Components: ```rust #[derive(Component)] #[require(Node, UiImage)] struct Button; commands.spawn(( Button, Style { width: Val::Px(100.0), height: Val::Px(50.0), ..default() }, FocusPolicy::Block, )); ``` With Required Components, we mention only the most relevant components. Every component required by `Node` (ex: `Style`, `FocusPolicy`, etc) is automatically brought in! ### Efficiency 1. At insertion/spawn time, Required Components (including recursive required components) are initialized and inserted _as if they were manually inserted alongside the given components_. This means that this is maximally efficient: there are no archetype or table moves. 2. Required components are only initialized and inserted if they were not manually provided by the developer. For the code example in the previous section, because `Style` and `FocusPolicy` are inserted manually, they _will not_ be initialized and inserted as part of the required components system. Efficient! 3. The "missing required components _and_ constructors needed for an insertion" are cached in the "archetype graph edge", meaning they aren't computed per-insertion. When a component is inserted, the "missing required components" list is iterated (and that graph edge (AddBundle) is actually already looked up for us during insertion, because we need that for "normal" insert logic too). ### IDE Integration The `#[require(SomeComponent)]` macro has been written in such a way that Rust Analyzer can provide type-inspection-on-hover and `F12` / go-to-definition for required components. ### Custom Constructors The `require` syntax expects a `Default` constructor by default, but it can be overridden with a custom constructor: ```rust #[derive(Component)] #[require( Node, Style(button_style), UiImage )] struct Button; fn button_style() -> Style { Style { width: Val::Px(100.0), ..default() } } ``` ### Multiple Inheritance You may have noticed by now that this behaves a bit like "multiple inheritance". One of the problems that this presents is that it is possible to have duplicate requires for a given type at different levels of the inheritance tree: ```rust #[derive(Component) struct X(usize); #[derive(Component)] #[require(X(x1)) struct Y; fn x1() -> X { X(1) } #[derive(Component)] #[require( Y, X(x2), )] struct Z; fn x2() -> X { X(2) } // What version of X is inserted for Z? commands.spawn(Z); ``` This is allowed (and encouraged), although this doesn't appear to occur much in practice. First: only one version of `X` is initialized and inserted for `Z`. In the case above, I think we can all probably agree that it makes the most sense to use the `x2` constructor for `X`, because `Y`'s `x1` constructor exists "beneath" `Z` in the inheritance hierarchy; `Z`'s constructor is "more specific". The algorithm is simple and predictable: 1. Use all of the constructors (including default constructors) directly defined in the spawned component's require list 2. In the order the requires are defined in `#[require()]`, recursively visit the require list of each of the components in the list (this is a depth Depth First Search). When a constructor is found, it will only be used if one has not already been found. From a user perspective, just think about this as the following: 1. Specifying a required component constructor for `Foo` directly on a spawned component `Bar` will result in that constructor being used (and overriding existing constructors lower in the inheritance tree). This is the classic "inheritance override" behavior people expect. 2. For cases where "multiple inheritance" results in constructor clashes, Components should be listed in "importance order". List a component earlier in the requirement list to initialize its inheritance tree earlier. Required Components _does_ generally result in a model where component values are decoupled from each other at construction time. Notably, some existing Bundle patterns use bundle constructors to initialize multiple components with shared state. I think (in general) moving away from this is necessary: 1. It allows Required Components (and the Scene system more generally) to operate according to simple rules 2. The "do arbitrary init value sharing in Bundle constructors" approach _already_ causes data consistency problems, and those problems would be exacerbated in the context of a Scene/UI system. For cases where shared state is truly necessary, I think we are better served by observers / hooks. 3. If a situation _truly_ needs shared state constructors (which should be rare / generally discouraged), Bundles are still there if they are needed. ## Next Steps * **Require Construct-ed Components**: I have already implemented this (as defined in the [Next Generation Scene / UI Proposal](https://github.com/bevyengine/bevy/discussions/14437). However I've removed `Construct` support from this PR, as that has not landed yet. Adding this back in requires relatively minimal changes to the current impl, and can be done as part of a future Construct pr. * **Port Built-in Bundles to Required Components**: This isn't something we should do right away. It will require rethinking our public interfaces, which IMO should be done holistically after the rest of Next Generation Scene / UI lands. I think we should merge this PR first and let people experiment _inside their own code with their own Components_ while we wait for the rest of the new scene system to land. * **_Consider_ Automatic Required Component Removal**: We should evaluate _if_ automatic Required Component removal should be done. Ex: if all components that explicitly require a component are removed, automatically remove that component. This issue has been explicitly deferred in this PR, as I consider the insertion behavior to be desirable on its own (and viable on its own). I am also doubtful that we can find a design that has behavior we actually want. Aka: can we _really_ distinguish between a component that is "only there because it was automatically inserted" and "a component that was necessary / should be kept". See my [discussion response here](https://github.com/bevyengine/bevy/discussions/14437#discussioncomment-10268668) for more details. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: Pascal Hertleif <killercup@gmail.com> |
||
![]() |
938d810766
|
Apply unused_qualifications lint (#14828)
# Objective Fixes #14782 ## Solution Enable the lint and fix all upcoming hints (`--fix`). Also tried to figure out the false-positive (see review comment). Maybe split this PR up into multiple parts where only the last one enables the lint, so some can already be merged resulting in less many files touched / less potential for merge conflicts? Currently, there are some cases where it might be easier to read the code with the qualifier, so perhaps remove the import of it and adapt its cases? In the current stage it's just a plain adoption of the suggestions in order to have a base to discuss. ## Testing `cargo clippy` and `cargo run -p ci` are happy. |
||
![]() |
e9e9e5e15d
|
Add query reborrowing (#14690)
# Objective - Sometimes some method or function takes an owned `Query`, but we don't want to give up ours; - transmuting it technically a solution, but it more costly than necessary. - Make query iterators more flexible - this would allow the equivalent of `slice::split_first`/`slice::split_first_mut` for query iterators - helps with requests like #14685 ## Solution - Add a way for reborrowing queries, that is going from a `&'a mut Query<'w, 's, D, F>` to a `Query<'a, 's, D, F>`: - this is safe because the original query will be borrowed while the new query exists and thus no aliased access can happen; - it's basically the equivalent of going from `&'short mut &'long mut T` to `&'short mut T` the the compiler automatically implements. - Add a way for getting the remainder of a query iterator: - this is interesting also because the original iterator keeps its position, which was not possible before; - this in turn requires a way to reborrow query fetches, which I had to add to `WorldQuery`. ## Showcase - You can now reborrow a `Query`, getting an equivalent `Query` with a shorter lifetime. Previously this was possible for read-only queries by using `Query::to_readonly`, now it's possible for mutable queries too; - You can now separately iterate over the remainder of `QueryIter`. ## Migration Guide - `WorldQuery` now has an additional `shrink_fetch` method you have to implement if you were implementing `WorldQuery` manually. |
||
![]() |
71c5f1e3e4
|
Generate links to definition in source code pages on docs.rs and dev-docs.bevyengine.org (#12965)
# Objective - Fix issue #2611 ## Solution - Add `--generate-link-to-definition` to all the `rustdoc-args` arrays in the `Cargo.toml`s (for docs.rs) - Add `--generate-link-to-definition` to the `RUSTDOCFLAGS` environment variable in the docs workflow (for dev-docs.bevyengine.org) - Document all the workspace crates in the docs workflow (needed because otherwise only the source code of the `bevy` package will be included, making the argument useless) - I think this also fixes #3662, since it fixes the bug on dev-docs.bevyengine.org, while on docs.rs it has been fixed for a while on their side. --- ## Changelog - The source code viewer on docs.rs now includes links to the definitions. |
||
![]() |
8f5345573c
|
Remove manual --cfg docsrs (#14376)
# Objective - Fixes #14132 ## Solution - Remove the cfg docsrs |
||
![]() |
0f7c548a4a
|
Component Lifecycle Hook & Observer Trigger for replaced values (#14212)
# Objective Fixes #14202 ## Solution Add `on_replaced` component hook and `OnReplaced` observer trigger ## Testing - Did you test these changes? If so, how? - Updated & added unit tests --- ## Changelog - Added new `on_replaced` component hook and `OnReplaced` observer trigger for performing cleanup on component values when they are overwritten with `.insert()` |
||
![]() |
ed2b8e0f35
|
Minimal Bubbling Observers (#13991)
# Objective Add basic bubbling to observers, modeled off `bevy_eventlistener`. ## Solution - Introduce a new `Traversal` trait for components which point to other entities. - Provide a default `TraverseNone: Traversal` component which cannot be constructed. - Implement `Traversal` for `Parent`. - The `Event` trait now has an associated `Traversal` which defaults to `TraverseNone`. - Added a field `bubbling: &mut bool` to `Trigger` which can be used to instruct the runner to bubble the event to the entity specified by the event's traversal type. - Added an associated constant `SHOULD_BUBBLE` to `Event` which configures the default bubbling state. - Added logic to wire this all up correctly. Introducing the new associated information directly on `Event` (instead of a new `BubblingEvent` trait) lets us dispatch both bubbling and non-bubbling events through the same api. ## Testing I have added several unit tests to cover the common bugs I identified during development. Running the unit tests should be enough to validate correctness. The changes effect unsafe portions of the code, but should not change any of the safety assertions. ## Changelog Observers can now bubble up the entity hierarchy! To create a bubbling event, change your `Derive(Event)` to something like the following: ```rust #[derive(Component)] struct MyEvent; impl Event for MyEvent { type Traverse = Parent; // This event will propagate up from child to parent. const AUTO_PROPAGATE: bool = true; // This event will propagate by default. } ``` You can dispatch a bubbling event using the normal `world.trigger_targets(MyEvent, entity)`. Halting an event mid-bubble can be done using `trigger.propagate(false)`. Events with `AUTO_PROPAGATE = false` will not propagate by default, but you can enable it using `trigger.propagate(true)`. If there are multiple observers attached to a target, they will all be triggered by bubbling. They all share a bubbling state, which can be accessed mutably using `trigger.propagation_mut()` (`trigger.propagate` is just sugar for this). You can choose to implement `Traversal` for your own types, if you want to bubble along a different structure than provided by `bevy_hierarchy`. Implementers must be careful never to produce loops, because this will cause bevy to hang. ## Migration Guide + Manual implementations of `Event` should add associated type `Traverse = TraverseNone` and associated constant `AUTO_PROPAGATE = false`; + `Trigger::new` has new field `propagation: &mut Propagation` which provides the bubbling state. + `ObserverRunner` now takes the same `&mut Propagation` as a final parameter. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
d7080369a7
|
Fix intra-doc links and make CI test them (#14076)
# Objective - Bevy currently has lot of invalid intra-doc links, let's fix them! - Also make CI test them, to avoid future regressions. - Helps with #1983 (but doesn't fix it, as there could still be explicit links to docs.rs that are broken) ## Solution - Make `cargo r -p ci -- doc-check` check fail on warnings (could also be changed to just some specific lints) - Manually fix all the warnings (note that in some cases it was unclear to me what the fix should have been, I'll try to highlight them in a self-review) |
||
![]() |
8df10d2713
|
Bump Version after Release (#14219)
Bump version after release This PR has been auto-generated Co-authored-by: Bevy Auto Releaser <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
330911f1bf
|
Component Hook functions as attributes for Component derive macro (#14005)
# Objective Fixes https://github.com/bevyengine/bevy/issues/13972 ## Solution Added 3 new attributes to the `Component` macro. ## Testing Added `component_hook_order_spawn_despawn_with_macro_hooks`, that makes the same as `component_hook_order_spawn_despawn` but uses a struct, that defines it's hooks with the `Component` macro. --- --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
![]() |
eb3c81374a
|
Generalised ECS reactivity with Observers (#10839)
# Objective - Provide an expressive way to register dynamic behavior in response to ECS changes that is consistent with existing bevy types and traits as to provide a smooth user experience. - Provide a mechanism for immediate changes in response to events during command application in order to facilitate improved query caching on the path to relations. ## Solution - A new fundamental ECS construct, the `Observer`; inspired by flec's observers but adapted to better fit bevy's access patterns and rust's type system. --- ## Examples There are 3 main ways to register observers. The first is a "component observer" that looks like this: ```rust world.observe(|trigger: Trigger<OnAdd, Transform>, query: Query<&Transform>| { let transform = query.get(trigger.entity()).unwrap(); }); ``` The above code will spawn a new entity representing the observer that will run it's callback whenever the `Transform` component is added to an entity. This is a system-like function that supports dependency injection for all the standard bevy types: `Query`, `Res`, `Commands` etc. It also has a `Trigger` parameter that provides information about the trigger such as the target entity, and the event being triggered. Importantly these systems run during command application which is key for their future use to keep ECS internals up to date. There are similar events for `OnInsert` and `OnRemove`, and this will be expanded with things such as `ArchetypeCreated`, `TableEmpty` etc. in follow up PRs. Another way to register an observer is an "entity observer" that looks like this: ```rust world.entity_mut(entity).observe(|trigger: Trigger<Resize>| { // ... }); ``` Entity observers run whenever an event of their type is triggered targeting that specific entity. This type of observer will de-spawn itself if the entity (or entities) it is observing is ever de-spawned so as to not leave dangling observers. Entity observers can also be spawned from deferred contexts such as other observers, systems, or hooks using commands: ```rust commands.entity(entity).observe(|trigger: Trigger<Resize>| { // ... }); ``` Observers are not limited to in built event types, they can be used with any type that implements `Event` (which has been extended to implement Component). This means events can also carry data: ```rust #[derive(Event)] struct Resize { x: u32, y: u32 } commands.entity(entity).observe(|trigger: Trigger<Resize>, query: Query<&mut Size>| { let event = trigger.event(); // ... }); // Will trigger the observer when commands are applied. commands.trigger_targets(Resize { x: 10, y: 10 }, entity); ``` You can also trigger events that target more than one entity at a time: ```rust commands.trigger_targets(Resize { x: 10, y: 10 }, [e1, e2]); ``` Additionally, Observers don't _need_ entity targets: ```rust app.observe(|trigger: Trigger<Quit>| { }) commands.trigger(Quit); ``` In these cases, `trigger.entity()` will be a placeholder. Observers are actually just normal entities with an `ObserverState` and `Observer` component! The `observe()` functions above are just shorthand for: ```rust world.spawn(Observer::new(|trigger: Trigger<Resize>| {}); ``` This will spawn the `Observer` system and use an `on_add` hook to add the `ObserverState` component. Dynamic components and trigger types are also fully supported allowing for runtime defined trigger types. ## Possible Follow-ups 1. Deprecate `RemovedComponents`, observers should fulfill all use cases while being more flexible and performant. 2. Queries as entities: Swap queries to entities and begin using observers listening to archetype creation triggers to keep their caches in sync, this allows unification of `ObserverState` and `QueryState` as well as unlocking several API improvements for `Query` and the management of `QueryState`. 3. Trigger bubbling: For some UI use cases in particular users are likely to want some form of bubbling for entity observers, this is trivial to implement naively but ideally this includes an acceleration structure to cache hierarchy traversals. 4. All kinds of other in-built trigger types. 5. Optimization; in order to not bloat the complexity of the PR I have kept the implementation straightforward, there are several areas where performance can be improved. The focus for this PR is to get the behavior implemented and not incur a performance cost for users who don't use observers. I am leaving each of these to follow up PR's in order to keep each of them reviewable as this already includes significant changes. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
07a85676b3
|
Revert "constrain WorldQuery::init_state argument to ComponentInitial… (#13804)
…izer (#13442)"
This reverts commit
|
||
![]() |
5cfb063d4a
|
constrain WorldQuery::init_state argument to ComponentInitializer (#13442)
# Objective In #13343, `WorldQuery::get_state` was constrained from `&World` as the argument to `&Components`, but `WorldQuery::init_state` hasn't yet been changed from `&mut World` to match. Fixes #13358 ## Solution Create a wrapper around `&mut Components` and `&mut Storages` that can be obtained from `&mut World` with a `component_initializer` method. This new `ComponentInitializer` re-exposes the API on `&mut Components` minus the `&mut Storages` parameter where it was present. For the `&Components` API, it simply derefs to its `components` field. ## Changelog ### Added The `World::component_initializer` method. The `ComponentInitializer` struct that re-exposes `Components` API. ### Changed `WorldQuery::init_state` now takes `&mut ComponentInitializer` instead of `&mut World`. ## Migration Guide Instead of passing `&mut World` to `WorldQuery::init_state` directly, pass in a mutable reference to the struct returned from `World::component_initializer`. |
||
![]() |
0eb4bb6bab
|
constrain WorldQuery::get_state to only use &Components (#13343)
# Objective Passing `&World` in the `WorldQuery::get_state` method is unnecessary, as all implementations of this method in the engine either only access `Components` in `&World`, or do nothing with it. It can introduce UB by necessitating the creation of a `&World` from a `UnsafeWorldCell`. This currently happens in `Query::transmute_lens`, which obtains a `&World` from the internal `UnsafeWorldCell` solely to pass to `get_state`. `Query::join` suffers from the same issue. Other cases of UB come from allowing implementors of `WorldQuery` to freely access `&World`, like in the `bevy-trait-query` crate, where a [reference to a resource is obtained]( |
||
![]() |
b8832dc862
|
Computed State & Sub States (#11426)
## Summary/Description This PR extends states to allow support for a wider variety of state types and patterns, by providing 3 distinct types of state: - Standard [`States`] can only be changed by manually setting the [`NextState<S>`] resource. These states are the baseline on which the other state types are built, and can be used on their own for many simple patterns. See the [state example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/state.rs) for a simple use case - these are the states that existed so far in Bevy. - [`SubStates`] are children of other states - they can be changed manually using [`NextState<S>`], but are removed from the [`World`] if the source states aren't in the right state. See the [sub_states example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/sub_states.rs) for a simple use case based on the derive macro, or read the trait docs for more complex scenarios. - [`ComputedStates`] are fully derived from other states - they provide a [`compute`](ComputedStates::compute) method that takes in the source states and returns their derived value. They are particularly useful for situations where a simplified view of the source states is necessary - such as having an `InAMenu` computed state derived from a source state that defines multiple distinct menus. See the [computed state example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/computed_states.rscomputed_states.rs) to see a sampling of uses for these states. # Objective This PR is another attempt at allowing Bevy to better handle complex state objects in a manner that doesn't rely on strict equality. While my previous attempts (https://github.com/bevyengine/bevy/pull/10088 and https://github.com/bevyengine/bevy/pull/9957) relied on complex matching capacities at the point of adding a system to application, this one instead relies on deterministically deriving simple states from more complex ones. As a result, it does not require any special macros, nor does it change any other interactions with the state system once you define and add your derived state. It also maintains a degree of distinction between `State` and just normal application state - your derivations have to end up being discreet pre-determined values, meaning there is less of a risk/temptation to place a significant amount of logic and data within a given state. ### Addition - Sub States closes #9942 After some conversation with Maintainers & SMEs, a significant concern was that people might attempt to use this feature as if it were sub-states, and find themselves unable to use it appropriately. Since `ComputedState` is mainly a state matching feature, while `SubStates` are more of a state mutation related feature - but one that is easy to add with the help of the machinery introduced by `ComputedState`, it was added here as well. The relevant discussion is here: https://discord.com/channels/691052431525675048/1200556329803186316 ## Solution closes #11358 The solution is to create a new type of state - one implementing `ComputedStates` - which is deterministically tied to one or more other states. Implementors write a function to transform the source states into the computed state, and it gets triggered whenever one of the source states changes. In addition, we added the `FreelyMutableState` trait , which is implemented as part of the derive macro for `States`. This allows us to limit use of `NextState<S>` to states that are actually mutable, preventing mis-use of `ComputedStates`. --- ## Changelog - Added `ComputedStates` trait - Added `FreelyMutableState` trait - Converted `NextState` resource to an Enum, with `Unchanged` and `Pending` - Added `App::add_computed_state::<S: ComputedStates>()`, to allow for easily adding derived states to an App. - Moved the `StateTransition` schedule label from `bevy_app` to `bevy_ecs` - but maintained the export in `bevy_app` for continuity. - Modified the process for updating states. Instead of just having an `apply_state_transition` system that can be added anywhere, we now have a multi-stage process that has to run within the `StateTransition` label. First, all the state changes are calculated - manual transitions rely on `apply_state_transition`, while computed transitions run their computation process before both call `internal_apply_state_transition` to apply the transition, send out the transition event, trigger dependent states, and record which exit/transition/enter schedules need to occur. Once all the states have been updated, the transition schedules are called - first the exit schedules, then transition schedules and finally enter schedules. - Added `SubStates` trait - Adjusted `apply_state_transition` to be a no-op if the `State<S>` resource doesn't exist ## Migration Guide If the user accessed the NextState resource's value directly or created them from scratch they will need to adjust to use the new enum variants: - if they created a `NextState(Some(S))` - they should now use `NextState::Pending(S)` - if they created a `NextState(None)` -they should now use `NextState::Unchanged` - if they matched on the `NextState` value, they would need to make the adjustments above If the user manually utilized `apply_state_transition`, they should instead use systems that trigger the `StateTransition` schedule. --- ## Future Work There is still some future potential work in the area, but I wanted to keep these potential features and changes separate to keep the scope here contained, and keep the core of it easy to understand and use. However, I do want to note some of these things, both as inspiration to others and an illustration of what this PR could unlock. - `NextState::Remove` - Now that the `State` related mechanisms all utilize options (#11417), it's fairly easy to add support for explicit state removal. And while `ComputedStates` can add and remove themselves, right now `FreelyMutableState`s can't be removed from within the state system. While it existed originally in this PR, it is a different question with a separate scope and usability concerns - so having it as it's own future PR seems like the best approach. This feature currently lives in a separate branch in my fork, and the differences between it and this PR can be seen here: https://github.com/lee-orr/bevy/pull/5 - `NextState::ReEnter` - this would allow you to trigger exit & entry systems for the current state type. We can potentially also add a `NextState::ReEnterRecirsive` to also re-trigger any states that depend on the current one. - More mechanisms for `State` updates - This PR would finally make states that aren't a set of exclusive Enums useful, and with that comes the question of setting state more effectively. Right now, to update a state you either need to fully create the new state, or include the `Res<Option<State<S>>>` resource in your system, clone the state, mutate it, and then use `NextState.set(my_mutated_state)` to make it the pending next state. There are a few other potential methods that could be implemented in future PRs: - Inverse Compute States - these would essentially be compute states that have an additional (manually defined) function that can be used to nudge the source states so that they result in the computed states having a given value. For example, you could use set the `IsPaused` state, and it would attempt to pause or unpause the game by modifying the `AppState` as needed. - Closure-based state modification - this would involve adding a `NextState.modify(f: impl Fn(Option<S> -> Option<S>)` method, and then you can pass in closures or function pointers to adjust the state as needed. - Message-based state modification - this would involve either creating states that can respond to specific messages, similar to Elm or Redux. These could either use the `NextState` mechanism or the Event mechanism. - ~`SubStates` - which are essentially a hybrid of computed and manual states. In the simplest (and most likely) version, they would work by having a computed element that determines whether the state should exist, and if it should has the capacity to add a new version in, but then any changes to it's content would be freely mutated.~ this feature is now part of this PR. See above. - Lastly, since states are getting more complex there might be value in moving them out of `bevy_ecs` and into their own crate, or at least out of the `schedule` module into a `states` module. #11087 As mentioned, all these future work elements are TBD and are explicitly not part of this PR - I just wanted to provide them as potential explorations for the future. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Marcel Champagne <voiceofmarcel@gmail.com> Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> |
||
![]() |
54456b7ea6
|
Make SystemParam::new_archetype and QueryState::new_archetype unsafe functions (#13044)
# Objective Fix #2128. Both `Query::new_archetype` and `SystemParam::new_archetype` do not check if the `Archetype` comes from the same World the state is initialized from. This could result in unsoundness via invalid accesses if called incorrectly. ## Solution Make them `unsafe` functions and lift the invariant to the caller. This also caught one instance of us not validating the World in `SystemState::update_archetypes_unsafe_world_cell`'s implementation. --- ## Changelog Changed: `QueryState::new_archetype` is now an unsafe function. Changed: `SystemParam::new_archetype` is now an unsafe function. ## Migration Guide `QueryState::new_archetype` and `SystemParam::new_archetype` are now an unsafe functions that must be sure that the provided `Archetype` is from the same `World` that the state was initialized from. Callers may need to add additional assertions or propagate the safety invariant upwards through the callstack to ensure safety. |
||
![]() |
72c51cdab9
|
Make feature(doc_auto_cfg) work (#12642)
# Objective - In #12366 `![cfg_attr(docsrs, feature(doc_auto_cfg))] `was added. But to apply it it needs `--cfg=docsrs` in rustdoc-args. ## Solution - Apply `--cfg=docsrs` to all crates and CI. I also added `[package.metadata.docs.rs]` to all crates to avoid adding code behind a feature and forget adding the metadata. Before:  After:  |
||
![]() |
dc40cd134f
|
Remove ComponentStorage and associated types (#12311)
# Objective When doing a final pass for #3362, it appeared that `ComponentStorage` as a trait, the two types implementing it, and the associated type on `Component` aren't really necessary anymore. This likely was due to an earlier constraint on the use of consts in traits, but that definitely doesn't seem to be a problem in Rust 1.76. ## Solution Remove them. --- ## Changelog Changed: `Component::Storage` has been replaced with `Component::STORAGE_TYPE` as a const. Removed: `bevy::ecs::component::ComponentStorage` trait Removed: `bevy::ecs::component::TableStorage` struct Removed: `bevy::ecs::component::SparseSetStorage` struct ## Migration Guide If you were manually implementing `Component` instead of using the derive macro, replace the associated `Storage` associated type with the `STORAGE_TYPE` const: ```rust // in Bevy 0.13 impl Component for MyComponent { type Storage = TableStorage; } // in Bevy 0.14 impl Component for MyComponent { const STORAGE_TYPE: StorageType = StorageType::Table; } ``` Component is no longer object safe. If you were relying on `&dyn Component`, `Box<dyn Component>`, etc. please [file an issue ](https://github.com/bevyengine/bevy/issues) to get [this change](https://github.com/bevyengine/bevy/pull/12311) reverted. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
e7c3359c4b
|
Bump Version after Release (#12020)
Fixes #12016. Bump version after release This PR has been auto-generated Co-authored-by: Bevy Auto Releaser <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: François <mockersf@gmail.com> |
||
![]() |
abb8c353f4
|
Release 0.13.0 (#11920)
Bump Bevy crates to 0.13.0 in preparation for release. (Note that we accidentally skipped the `0.13.0-dev` step this cycle) |
||
![]() |
ff77adc045
|
Typo in [ScheduleLabel ] derive macro (#11764)
[`ScheduleLabel`] derive macro uses "ScheduleName" as the trait name by mistake. This only affects the error message when a user tries to use the derive macro on a union type. No other code is affected. |
||
![]() |
694c06f3d0
|
Inverse missing_docs logic (#11676)
# Objective Currently the `missing_docs` lint is allowed-by-default and enabled at crate level when their documentations is complete (see #3492). This PR proposes to inverse this logic by making `missing_docs` warn-by-default and mark crates with imcomplete docs allowed. ## Solution Makes `missing_docs` warn at workspace level and allowed at crate level when the docs is imcomplete. |
||
![]() |
43f83d5e7c
|
Remove duplicate #[automatically_derived] in ECS macro (#11388)
# Objective It's already provided by `item_attrs`, so it can be removed. # Solution Remove the extra `#[automatically_derived]`. |
||
![]() |
ea42d14344
|
Dynamic queries and builder API (#9774)
# Objective Expand the existing `Query` API to support more dynamic use cases i.e. scripting. ## Prior Art - #6390 - #8308 - #10037 ## Solution - Create a `QueryBuilder` with runtime methods to define the set of component accesses for a built query. - Create new `WorldQueryData` implementations `FilteredEntityMut` and `FilteredEntityRef` as variants of `EntityMut` and `EntityRef` that provide run time checked access to the components included in a given query. - Add new methods to `Query` to create "query lens" with a subset of the access of the initial query. ### Query Builder The `QueryBuilder` API allows you to define a query at runtime. At it's most basic use it will simply create a query with the corresponding type signature: ```rust let query = QueryBuilder::<Entity, With<A>>::new(&mut world).build(); // is equivalent to let query = QueryState::<Entity, With<A>>::new(&mut world); ``` Before calling `.build()` you also have the opportunity to add additional accesses and filters. Here is a simple example where we add additional filter terms: ```rust let entity_a = world.spawn((A(0), B(0))).id(); let entity_b = world.spawn((A(0), C(0))).id(); let mut query_a = QueryBuilder::<Entity>::new(&mut world) .with::<A>() .without::<C>() .build(); assert_eq!(entity_a, query_a.single(&world)); ``` This alone is useful in that allows you to decide which archetypes your query will match at runtime. However it is also very limited, consider a case like the following: ```rust let query_a = QueryBuilder::<&A>::new(&mut world) // Add an additional access .data::<&B>() .build(); ``` This will grant the query an additional read access to component B however we have no way of accessing the data while iterating as the type signature still only includes &A. For an even more concrete example of this consider dynamic components: ```rust let query_a = QueryBuilder::<Entity>::new(&mut world) // Adding a filter is easy since it doesn't need be read later .with_id(component_id_a) // How do I access the data of this component? .ref_id(component_id_b) .build(); ``` With this in mind the `QueryBuilder` API seems somewhat incomplete by itself, we need some way method of accessing the components dynamically. So here's one: ### Query Transmutation If the problem is not having the component in the type signature why not just add it? This PR also adds transmute methods to `QueryBuilder` and `QueryState`. Here's a simple example: ```rust world.spawn(A(0)); world.spawn((A(1), B(0))); let mut query = QueryBuilder::<()>::new(&mut world) .with::<B>() .transmute::<&A>() .build(); query.iter(&world).for_each(|a| assert_eq!(a.0, 1)); ``` The `QueryState` and `QueryBuilder` transmute methods look quite similar but are different in one respect. Transmuting a builder will always succeed as it will just add the additional accesses needed for the new terms if they weren't already included. Transmuting a `QueryState` will panic in the case that the new type signature would give it access it didn't already have, for example: ```rust let query = QueryState::<&A, Option<&B>>::new(&mut world); /// This is fine, the access for Option<&A> is less restrictive than &A query.transmute::<Option<&A>>(&world); /// Oh no, this would allow access to &B on entities that might not have it, so it panics query.transmute::<&B>(&world); /// This is right out query.transmute::<&C>(&world); ``` This is quite an appealing API to also have available on `Query` however it does pose one additional wrinkle: In order to to change the iterator we need to create a new `QueryState` to back it. `Query` doesn't own it's own state though, it just borrows it, so we need a place to borrow it from. This is why `QueryLens` exists, it is a place to store the new state so it can be borrowed when you call `.query()` leaving you with an API like this: ```rust fn function_that_takes_a_query(query: &Query<&A>) { // ... } fn system(query: Query<(&A, &B)>) { let lens = query.transmute_lens::<&A>(); let q = lens.query(); function_that_takes_a_query(&q); } ``` Now you may be thinking: Hey, wait a second, you introduced the problem with dynamic components and then described a solution that only works for static components! Ok, you got me, I guess we need a bit more: ### Filtered Entity References Currently the only way you can access dynamic components on entities through a query is with either `EntityMut` or `EntityRef`, however these can access all components and so conflict with all other accesses. This PR introduces `FilteredEntityMut` and `FilteredEntityRef` as alternatives that have additional runtime checking to prevent accessing components that you shouldn't. This way you can build a query with a `QueryBuilder` and actually access the components you asked for: ```rust let mut query = QueryBuilder::<FilteredEntityRef>::new(&mut world) .ref_id(component_id_a) .with(component_id_b) .build(); let entity_ref = query.single(&world); // Returns Some(Ptr) as we have that component and are allowed to read it let a = entity_ref.get_by_id(component_id_a); // Will return None even though the entity does have the component, as we are not allowed to read it let b = entity_ref.get_by_id(component_id_b); ``` For the most part these new structs have the exact same methods as their non-filtered equivalents. Putting all of this together we can do some truly dynamic ECS queries, check out the `dynamic` example to see it in action: ``` Commands: comp, c Create new components spawn, s Spawn entities query, q Query for entities Enter a command with no parameters for usage. > c A, B, C, Data 4 Component A created with id: 0 Component B created with id: 1 Component C created with id: 2 Component Data created with id: 3 > s A, B, Data 1 Entity spawned with id: 0v0 > s A, C, Data 0 Entity spawned with id: 1v0 > q &Data 0v0: Data: [1, 0, 0, 0] 1v0: Data: [0, 0, 0, 0] > q B, &mut Data 0v0: Data: [2, 1, 1, 1] > q B || C, &Data 0v0: Data: [2, 1, 1, 1] 1v0: Data: [0, 0, 0, 0] ``` ## Changelog - Add new `transmute_lens` methods to `Query`. - Add new types `QueryBuilder`, `FilteredEntityMut`, `FilteredEntityRef` and `QueryLens` - `update_archetype_component_access` has been removed, archetype component accesses are now determined by the accesses set in `update_component_access` - Added method `set_access` to `WorldQuery`, this is called before `update_component_access` for queries that have a restricted set of accesses, such as those built by `QueryBuilder` or `QueryLens`. This is primarily used by the `FilteredEntity*` variants and has an empty trait implementation. - Added method `get_state` to `WorldQuery` as a fallible version of `init_state` when you don't have `&mut World` access. ## Future Work Improve performance of `FilteredEntityMut` and `FilteredEntityRef`, currently they have to determine the accesses a query has in a given archetype during iteration which is far from ideal, especially since we already did the work when matching the archetype in the first place. To avoid making more internal API changes I have left it out of this PR. --------- Co-authored-by: Mike Hsu <mike.hsu@gmail.com> |
||
![]() |
bf0be9cc2c
|
Remove little warn on bevy_ecs (#11149)
# Objective - There is an warning about non snake case on system_param.rs generated by a macro ## Solution - Allow non snake case on the function at fault |
||
![]() |
5af2f022d8
|
Rename WorldQueryData & WorldQueryFilter to QueryData & QueryFilter (#10779)
# Rename `WorldQueryData` & `WorldQueryFilter` to `QueryData` & `QueryFilter` Fixes #10776 ## Solution Traits `WorldQueryData` & `WorldQueryFilter` were renamed to `QueryData` and `QueryFilter`, respectively. Related Trait types were also renamed. --- ## Changelog - Trait `WorldQueryData` has been renamed to `QueryData`. Derive macro's `QueryData` attribute `world_query_data` has been renamed to `query_data`. - Trait `WorldQueryFilter` has been renamed to `QueryFilter`. Derive macro's `QueryFilter` attribute `world_query_filter` has been renamed to `query_filter`. - Trait's `ExtractComponent` type `Query` has been renamed to `Data`. - Trait's `GetBatchData` types `Query` & `QueryFilter` has been renamed to `Data` & `Filter`, respectively. - Trait's `ExtractInstance` type `Query` has been renamed to `Data`. - Trait's `ViewNode` type `ViewQuery` has been renamed to `ViewData`. - Trait's `RenderCommand` types `ViewWorldQuery` & `ItemWorldQuery` has been renamed to `ViewData` & `ItemData`, respectively. ## Migration Guide Note: if merged before 0.13 is released, this should instead modify the migration guide of #10776 with the updated names. - Rename `WorldQueryData` & `WorldQueryFilter` trait usages to `QueryData` & `QueryFilter` and their respective derive macro attributes `world_query_data` & `world_query_filter` to `query_data` & `query_filter`. - Rename the following trait type usages: - Trait's `ExtractComponent` type `Query` to `Data`. - Trait's `GetBatchData` type `Query` to `Data`. - Trait's `ExtractInstance` type `Query` to `Data`. - Trait's `ViewNode` type `ViewQuery` to `ViewData`' - Trait's `RenderCommand` types `ViewWolrdQuery` & `ItemWorldQuery` to `ViewData` & `ItemData`, respectively. ```rust // Before #[derive(WorldQueryData)] #[world_query_data(derive(Debug))] struct EmptyQuery { empty: (), } // After #[derive(QueryData)] #[query_data(derive(Debug))] struct EmptyQuery { empty: (), } // Before #[derive(WorldQueryFilter)] struct CustomQueryFilter<T: Component, P: Component> { _c: With<ComponentC>, _d: With<ComponentD>, _or: Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>, _generic_tuple: (With<T>, With<P>), } // After #[derive(QueryFilter)] struct CustomQueryFilter<T: Component, P: Component> { _c: With<ComponentC>, _d: With<ComponentD>, _or: Or<(Added<ComponentC>, Changed<ComponentD>, Without<ComponentZ>)>, _generic_tuple: (With<T>, With<P>), } // Before impl ExtractComponent for ContrastAdaptiveSharpeningSettings { type Query = &'static Self; type Filter = With<Camera>; type Out = (DenoiseCAS, CASUniform); fn extract_component(item: QueryItem<Self::Query>) -> Option<Self::Out> { //... } } // After impl ExtractComponent for ContrastAdaptiveSharpeningSettings { type Data = &'static Self; type Filter = With<Camera>; type Out = (DenoiseCAS, CASUniform); fn extract_component(item: QueryItem<Self::Data>) -> Option<Self::Out> { //... } } // Before impl GetBatchData for MeshPipeline { type Param = SRes<RenderMeshInstances>; type Query = Entity; type QueryFilter = With<Mesh3d>; type CompareData = (MaterialBindGroupId, AssetId<Mesh>); type BufferData = MeshUniform; fn get_batch_data( mesh_instances: &SystemParamItem<Self::Param>, entity: &QueryItem<Self::Query>, ) -> (Self::BufferData, Option<Self::CompareData>) { // .... } } // After impl GetBatchData for MeshPipeline { type Param = SRes<RenderMeshInstances>; type Data = Entity; type Filter = With<Mesh3d>; type CompareData = (MaterialBindGroupId, AssetId<Mesh>); type BufferData = MeshUniform; fn get_batch_data( mesh_instances: &SystemParamItem<Self::Param>, entity: &QueryItem<Self::Data>, ) -> (Self::BufferData, Option<Self::CompareData>) { // .... } } // Before impl<A> ExtractInstance for AssetId<A> where A: Asset, { type Query = Read<Handle<A>>; type Filter = (); fn extract(item: QueryItem<'_, Self::Query>) -> Option<Self> { Some(item.id()) } } // After impl<A> ExtractInstance for AssetId<A> where A: Asset, { type Data = Read<Handle<A>>; type Filter = (); fn extract(item: QueryItem<'_, Self::Data>) -> Option<Self> { Some(item.id()) } } // Before impl ViewNode for PostProcessNode { type ViewQuery = ( &'static ViewTarget, &'static PostProcessSettings, ); fn run( &self, _graph: &mut RenderGraphContext, render_context: &mut RenderContext, (view_target, _post_process_settings): QueryItem<Self::ViewQuery>, world: &World, ) -> Result<(), NodeRunError> { // ... } } // After impl ViewNode for PostProcessNode { type ViewData = ( &'static ViewTarget, &'static PostProcessSettings, ); fn run( &self, _graph: &mut RenderGraphContext, render_context: &mut RenderContext, (view_target, _post_process_settings): QueryItem<Self::ViewData>, world: &World, ) -> Result<(), NodeRunError> { // ... } } // Before impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline { type Param = SRes<PipelineCache>; type ViewWorldQuery = (); type ItemWorldQuery = (); #[inline] fn render<'w>( item: &P, _view: (), _entity: (), pipeline_cache: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { // ... } } // After impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline { type Param = SRes<PipelineCache>; type ViewData = (); type ItemData = (); #[inline] fn render<'w>( item: &P, _view: (), _entity: (), pipeline_cache: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { // ... } } ``` |
||
![]() |
fd308571c4
|
Remove unnecessary path prefixes (#10749)
# Objective - Shorten paths by removing unnecessary prefixes ## Solution - Remove the prefixes from many paths which do not need them. Finding the paths was done automatically using built-in refactoring tools in Jetbrains RustRover. |
||
![]() |
f0a8994f55
|
Split WorldQuery into WorldQueryData and WorldQueryFilter (#9918)
# Objective - Fixes #7680 - This is an updated for https://github.com/bevyengine/bevy/pull/8899 which had the same objective but fell a long way behind the latest changes ## Solution The traits `WorldQueryData : WorldQuery` and `WorldQueryFilter : WorldQuery` have been added and some of the types and functions from `WorldQuery` has been moved into them. `ReadOnlyWorldQuery` has been replaced with `ReadOnlyWorldQueryData`. `WorldQueryFilter` is safe (as long as `WorldQuery` is implemented safely). `WorldQueryData` is unsafe - safely implementing it requires that `Self::ReadOnly` is a readonly version of `Self` (this used to be a safety requirement of `WorldQuery`) The type parameters `Q` and `F` of `Query` must now implement `WorldQueryData` and `WorldQueryFilter` respectively. This makes it impossible to accidentally use a filter in the data position or vice versa which was something that could lead to bugs. ~~Compile failure tests have been added to check this.~~ It was previously sometimes useful to use `Option<With<T>>` in the data position. Use `Has<T>` instead in these cases. The `WorldQuery` derive macro has been split into separate derive macros for `WorldQueryData` and `WorldQueryFilter`. Previously it was possible to derive both `WorldQuery` for a struct that had a mixture of data and filter items. This would not work correctly in some cases but could be a useful pattern in others. *This is no longer possible.* --- ## Notes - The changes outside of `bevy_ecs` are all changing type parameters to the new types, updating the macro use, or replacing `Option<With<T>>` with `Has<T>`. - All `WorldQueryData` types always returned `true` for `IS_ARCHETYPAL` so I moved it to `WorldQueryFilter` and replaced all calls to it with `true`. That should be the only logic change outside of the macro generation code. - `Changed<T>` and `Added<T>` were being generated by a macro that I have expanded. Happy to revert that if desired. - The two derive macros share some functions for implementing `WorldQuery` but the tidiest way I could find to implement them was to give them a ton of arguments and ask clippy to ignore that. ## Changelog ### Changed - Split `WorldQuery` into `WorldQueryData` and `WorldQueryFilter` which now have separate derive macros. It is not possible to derive both for the same type. - `Query` now requires that the first type argument implements `WorldQueryData` and the second implements `WorldQueryFilter` ## Migration Guide - Update derives ```rust // old #[derive(WorldQuery)] #[world_query(mutable, derive(Debug))] struct CustomQuery { entity: Entity, a: &'static mut ComponentA } #[derive(WorldQuery)] struct QueryFilter { _c: With<ComponentC> } // new #[derive(WorldQueryData)] #[world_query_data(mutable, derive(Debug))] struct CustomQuery { entity: Entity, a: &'static mut ComponentA, } #[derive(WorldQueryFilter)] struct QueryFilter { _c: With<ComponentC> } ``` - Replace `Option<With<T>>` with `Has<T>` ```rust /// old fn my_system(query: Query<(Entity, Option<With<ComponentA>>)>) { for (entity, has_a_option) in query.iter(){ let has_a:bool = has_a_option.is_some(); //todo!() } } /// new fn my_system(query: Query<(Entity, Has<ComponentA>)>) { for (entity, has_a) in query.iter(){ //todo!() } } ``` - Fix queries which had filters in the data position or vice versa. ```rust // old fn my_system(query: Query<(Entity, With<ComponentA>)>) { for (entity, _) in query.iter(){ //todo!() } } // new fn my_system(query: Query<Entity, With<ComponentA>>) { for entity in query.iter(){ //todo!() } } // old fn my_system(query: Query<AnyOf<(&ComponentA, With<ComponentB>)>>) { for (entity, _) in query.iter(){ //todo!() } } // new fn my_system(query: Query<Option<&ComponentA>, Or<(With<ComponentA>, With<ComponentB>)>>) { for entity in query.iter(){ //todo!() } } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
eeb0c2f2e4
|
Allow #[derive(Bundle)] on tuple structs (take 3) (#10561)
- rework of old @Veykril's work in [2499](https://github.com/bevyengine/bevy/pull/2499) - Fixes [3537](https://github.com/bevyengine/bevy/issues/3537) |
||
![]() |
951c9bb1a2
|
Add [lints] table, fix adding #![allow(clippy::type_complexity)] everywhere (#10011)
# Objective - Fix adding `#![allow(clippy::type_complexity)]` everywhere. like #9796 ## Solution - Use the new [lints] table that will land in 1.74 (https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lints) - inherit lint to the workspace, crates and examples. ``` [lints] workspace = true ``` ## Changelog - Bump rust version to 1.74 - Enable lints table for the workspace ```toml [workspace.lints.clippy] type_complexity = "allow" ``` - Allow type complexity for all crates and examples ```toml [lints] workspace = true ``` --------- Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com> |
||
![]() |
bf30a25efc
|
Release 0.12 (#10362)
Preparing next release This PR has been auto-generated --------- Co-authored-by: Bevy Auto Releaser <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: François <mockersf@gmail.com> |
||
![]() |
a830530be4
|
Replace all labels with interned labels (#7762)
# Objective First of all, this PR took heavy inspiration from #7760 and #5715. It intends to also fix #5569, but with a slightly different approach. This also fixes #9335 by reexporting `DynEq`. ## Solution The advantage of this API is that we can intern a value without allocating for zero-sized-types and for enum variants that have no fields. This PR does this automatically in the `SystemSet` and `ScheduleLabel` derive macros for unit structs and fieldless enum variants. So this should cover many internal and external use cases of `SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory will be allocated. - The interning returns a `Interned<dyn SystemSet>`, which is just a wrapper around a `&'static dyn SystemSet`. - `Hash` and `Eq` are implemented in terms of the pointer value of the reference, similar to my first approach of anonymous system sets in #7676. - Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`. - The debug output of `Interned<T>` is the same as the interned value. Edit: - `AppLabel` is now also interned and the old `derive_label`/`define_label` macros were replaced with the new interning implementation. - Anonymous set ids are reused for different `Schedule`s, reducing the amount of leaked memory. ### Pros - `InternedSystemSet` and `InternedScheduleLabel` behave very similar to the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied without an allocation. - Many use cases don't allocate at all. - Very fast lookups and comparisons when using `InternedSystemSet` and `InternedScheduleLabel`. - The `intern` module might be usable in other areas. - `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement `{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics. ### Cons - Implementors of `SystemSet` and `ScheduleLabel` still need to implement `Hash` and `Eq` (and `Clone`) for it to work. ## Changelog ### Added - Added `intern` module to `bevy_utils`. - Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`. ### Changed - Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with `InternedSystemSet` and `InternedScheduleLabel`. - Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`. - Replaced `AppLabelId` with `InternedAppLabel`. - Changed `AppLabel` to use `Debug` for error messages. - Changed `AppLabel` to use interning. - Changed `define_label`/`derive_label` to use interning. - Replaced `define_boxed_label`/`derive_boxed_label` with `define_label`/`derive_label`. - Changed anonymous set ids to be only unique inside a schedule, not globally. - Made interned label types implement their label trait. ### Removed - Removed `define_boxed_label` and `derive_boxed_label`. ## Migration guide - Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with `InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`. - Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with `InternedSystemSet` or `Interned<dyn SystemSet>`. - Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn AppLabel>`. - Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet` need to implement: - `dyn_hash` directly instead of implementing `DynHash` - `as_dyn_eq` - Pass labels to `World::try_schedule_scope`, `World::schedule_scope`, `World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`, `Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and `Schedules::get_mut` by value instead of by reference. --------- Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
0716922165
|
ParamSet s containing non-send parameters should also be non-send (#10211)
# Objective Fix #10207 ## Solution Mark a `ParamSet`'s `SystemMeta` as non-send if any of its component parameters are non-send. |
||
![]() |
9c004439b8
|
Remove States::variants and remove enum-only restriction its derive (#9945)
# Objective The `States::variants` method was once used to construct `OnExit` and `OnEnter` schedules for every possible value of a given `States` type. [Since the switch to lazily initialized schedules](https://github.com/bevyengine/bevy/pull/8028/files#diff-b2fba3a0c86e496085ce7f0e3f1de5960cb754c7d215ed0f087aa556e529f97f), we no longer need to track every possible value. This also opens the door to `States` types that aren't enums. ## Solution - Remove the unused `States::variants` method and its associated type. - Remove the enum-only restriction on derived States types. --- ## Changelog - Removed `States::variants` and its associated type. - Derived `States` can now be datatypes other than enums. ## Migration Guide - `States::variants` no longer exists. If you relied on this function, consider using a library that provides enum iterators. |