f5250dbb50
662 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
951c4dac7e
|
bevy_ecs/system/commands/ folder docs pass (#18639)
- Lots of nits, formatting, and rephrasing, with the goal of making things more consistent. - Fix outdated error handler explanation in `Commands` and `EntityCommands` docs. - Expand docs for system-related commands. - Remove panic notes if the command only panics with the default error handler. - Update error handling notes for `try_` variants. - Hide `prelude` import in most doctest examples, unless the example uses something that people might not realize is in the prelude (like `Name`). - Remove a couple doctest examples that (in my opinion) didn't make sense. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
b4614dadcd
|
Use Display instead of Debug in the default error handler (#18629)
# Objective Improve error messages for missing resources. The default error handler currently prints the `Debug` representation of the error type instead of `Display`. Most error types use `#[derive(Debug)]`, resulting in a dump of the structure, but will have a user-friendly message for `Display`. Follow-up to #18593 ## Solution Change the default error handler to use `Display` instead of `Debug`. Change `BevyError` to include the backtrace in the `Display` format in addition to `Debug` so that it is still included. ## Showcase Before: ``` Encountered an error in system `system_name`: SystemParamValidationError { skipped: false, message: "Resource does not exist", param: "bevy_ecs::change_detection::Res<app_name::ResourceType>" } Encountered an error in system `other_system_name`: "String message with\nmultiple lines." ``` After ``` Encountered an error in system `system_name`: Parameter `Res<ResourceType>` failed validation: Resource does not exist Encountered an error in system `other_system_name`: String message with multiple lines. ``` |
||
![]() |
bb87bd4d02
|
Improve Query 's top-level documentation (#18622)
# Objective - There's been several changes to `Query` for this release cycle, and `Query`'s top-level documentation has gotten slightly out-of-date. - Alternative to #18615. ## Solution - Edit `Query`'s docs for consistency, clarity, and correctness. - Make sure to group `get()` and `get_many()` together instead of `single()` and `get_many()`, to enforce the distinction from https://github.com/bevyengine/bevy/pull/18615#issuecomment-2764355672. - Reformat doc tests so they would be readable if extracted into their own file. (Which mainly involves adding more spacing.) - Move link definitions to be nearer where they are used. - Fix the tables so they are up-to-date and correctly escape square brackets `\[ \]`. ## Testing I ran `cargo doc -p bevy_ecs --no-deps` to view the docs and `cargo test -p bevy_ecs --doc` to test the doc comments. ## Reviewing The diff is difficult to read, so I don't recommend _just_ looking at that. Instead, run `cargo doc -p bevy_ecs --no-deps` locally and read through the new version. It should theoretically read smoother with less super-technical jargon. :) ## Follow-up I want to go through some of `Query`'s methods, such as `single()`, `get()`, and `get_many()`, but I'll leave that for another PR. --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
35cfef7cf2
|
Rename EntityBorrow/TrustedEntityBorrow to ContainsEntity/EntityEquivalent (#18470)
# Objective Fixes #9367. Yet another follow-up to #16547. These traits were initially based on `Borrow<Entity>` because that trait was what they were replacing, and it felt close enough in meaning. However, they ultimately don't quite match: `borrow` always returns references, whereas `EntityBorrow` always returns a plain `Entity`. Additionally, `EntityBorrow` can imply that we are borrowing an `Entity` from the ECS, which is not what it does. Due to its safety contract, `TrustedEntityBorrow` is important an important and widely used trait for `EntitySet` functionality. In contrast, the safe `EntityBorrow` does not see much use, because even outside of `EntitySet`-related functionality, it is a better idea to accept `TrustedEntityBorrow` over `EntityBorrow`. Furthermore, as #9367 points out, abstracting over returning `Entity` from pointers/structs that contain it can skip some ergonomic friction. On top of that, there are aspects of #18319 and #18408 that are relevant to naming: We've run into the issue that relying on a type default can switch generic order. This is livable in some contexts, but unacceptable in others. To remedy that, we'd need to switch to a type alias approach: The "defaulted" `Entity` case becomes a `UniqueEntity*`/`Entity*Map`/`Entity*Set` alias, and the base type receives a more general name. `TrustedEntityBorrow` does not mesh clearly with sensible base type names. ## Solution Replace any `EntityBorrow` bounds with `TrustedEntityBorrow`. + Rename them as such: `EntityBorrow` -> `ContainsEntity` `TrustedEntityBorrow` -> `EntityEquivalent` For `EntityBorrow` we produce a change in meaning; We designate it for types that aren't necessarily strict wrappers around `Entity` or some pointer to `Entity`, but rather any of the myriad of types that contain a single associated `Entity`. This pattern can already be seen in the common `entity`/`id` methods across the engine. We do not mean for `ContainsEntity` to be a trait that abstracts input API (like how `AsRef<T>` is often used, f.e.), because eliding `entity()` would be too implicit in the general case. We prefix "Contains" to match the intuition of a struct with an `Entity` field, like some contain a `length` or `capacity`. It gives the impression of structure, which avoids the implication of a relationship to the `ECS`. `HasEntity` f.e. could be interpreted as "a currently live entity", As an input trait for APIs like #9367 envisioned, `TrustedEntityBorrow` is a better fit, because it *does* restrict itself to strict wrappers and pointers. Which is why we replace any `EntityBorrow`/`ContainsEntity` bounds with `TrustedEntityBorrow`/`EntityEquivalent`. Here, the name `EntityEquivalent` is a lot closer to its actual meaning, which is "A type that is both equivalent to an `Entity`, and forms the same total order when compared". Prior art for this is the [`Equivalent`](https://docs.rs/hashbrown/latest/hashbrown/trait.Equivalent.html) trait in `hashbrown`, which utilizes both `Borrow` and `Eq` for its one blanket impl! Given that we lose the `Borrow` moniker, and `Equivalent` can carry various meanings, we expand on the safety comment of `EntityEquivalent` somewhat. That should help prevent the confusion we saw in [#18408](https://github.com/bevyengine/bevy/pull/18408#issuecomment-2742094176). The new name meshes a lot better with the type aliasing approach in #18408, by aligning with the base name `EntityEquivalentHashMap`. For a consistent scheme among all set types, we can use this scheme for the `UniqueEntity*` wrapper types as well! This allows us to undo the switched generic order that was introduced to `UniqueEntityArray` by its `Entity` default. Even without the type aliases, I think these renames are worth doing! ## Migration Guide Any use of `EntityBorrow` becomes `ContainsEntity`. Any use of `TrustedEntityBorrow` becomes `EntityEquivalent`. |
||
![]() |
f57c7a43c4
|
reexport entity set collections in entity module (#18413)
# Objective Unlike for their helper typers, the import paths for `unique_array::UniqueEntityArray`, `unique_slice::UniqueEntitySlice`, `unique_vec::UniqueEntityVec`, `hash_set::EntityHashSet`, `hash_map::EntityHashMap`, `index_set::EntityIndexSet`, `index_map::EntityIndexMap` are quite redundant. When looking at the structure of `hashbrown`, we can also see that while both `HashSet` and `HashMap` have their own modules, the main types themselves are re-exported to the crate level. ## Solution Re-export the types in their shared `entity` parent module, and simplify the imports where they're used. |
||
![]() |
30ee5ffe3b
|
Improve error message for missing resources (#18593)
# Objective Fixes #18515 After the recent changes to system param validation, the panic message for a missing resource is currently: ``` Encountered an error in system `missing_resource_error::res_system`: SystemParamValidationError { skipped: false } ``` Add the parameter type name and a descriptive message, improving the panic message to: ``` Encountered an error in system `missing_resource_error::res_system`: SystemParamValidationError { skipped: false, message: "Resource does not exist", param: "bevy_ecs::change_detection::Res<missing_resource_error::MissingResource>" } ``` ## Solution Add fields to `SystemParamValidationError` for error context. Include the `type_name` of the param and a message. Store them as `Cow<'static, str>` and only format them into a friendly string in the `Display` impl. This lets us create errors using a `&'static str` with no allocation or formatting, while still supporting runtime `String` values if necessary. Add a unit test that verifies the panic message. ## Future Work If we change the default error handling to use `Display` instead of `Debug`, and to use `ShortName` for the system name, the panic message could be further improved to: ``` Encountered an error in system `res_system`: Parameter `Res<MissingResource>` failed validation: Resource does not exist ``` However, `BevyError` currently includes the backtrace in `Debug` but not `Display`, and I didn't want to try to change that in this PR. |
||
![]() |
5d1fe16bfd
|
Fix run_system for adapter systems wrapping exclusive systems (#18406)
# Objective Fix panic in `run_system` when running an exclusive system wrapped in a `PipeSystem` or `AdapterSystem`. #18076 introduced a `System::run_without_applying_deferred` method. It normally calls `System::run_unsafe`, but `ExclusiveFunctionSystem::run_unsafe` panics, so it was overridden for that type. Unfortunately, `PipeSystem::run_without_applying_deferred` still calls `PipeSystem::run_unsafe`, which can then call `ExclusiveFunctionSystem::run_unsafe` and panic. ## Solution Make `ExclusiveFunctionSystem::run_unsafe` work instead of panicking. Clarify the safety requirements that make this sound. The alternative is to override `run_without_applying_deferred` in `PipeSystem`, `CombinatorSystem`, `AdapterSystem`, `InfallibleSystemWrapper`, and `InfallibleObserverWrapper`. That seems like a lot of extra code just to preserve a confusing special case! Remove some implementations of `System::run` that are no longer necessary with this change. This slightly changes the behavior of `PipeSystem` and `CombinatorSystem`: Currently `run` will call `apply_deferred` on the first system before running the second, but after this change it will only call it after *both* systems have run. The new behavior is consistent with `run_unsafe` and `run_without_applying_deferred`, and restores the behavior prior to #11823. The panic was originally necessary because [`run_unsafe` took `&World`](https://github.com/bevyengine/bevy/pull/6083/files#diff-708dfc60ec5eef432b20a6f471357a7ea9bfb254dc2f918d5ed4a66deb0e85baR90). Now that it takes `UnsafeWorldCell`, it is possible to make it work. See also Cart's concerns at https://github.com/bevyengine/bevy/pull/4166#discussion_r979140356, although those also predate `UnsafeWorldCell`. And see #6698 for a previous bug caused by this panic. |
||
![]() |
837991a5b5
|
Replace ValidationOutcome with Result (#18541)
# Objective Make it easier to short-circuit system parameter validation. Simplify the API surface by combining `ValidationOutcome` with `SystemParamValidationError`. ## Solution Replace `ValidationOutcome` with `Result<(), SystemParamValidationError>`. Move the docs from `ValidationOutcome` to `SystemParamValidationError`. Add a `skipped` field to `SystemParamValidationError` to distinguish the `Skipped` and `Invalid` variants. Use the `?` operator to short-circuit validation in tuples of system params. |
||
![]() |
6a981aaa6f
|
Define system param validation on a per-system parameter basis (#18504)
# Objective When introduced, `Single` was intended to simply be silently skipped, allowing for graceful and efficient handling of systems during invalid game states (such as when the player is dead). However, this also caused missing resources to *also* be silently skipped, leading to confusing and very hard to debug failures. In 0.15.1, this behavior was reverted to a panic, making missing resources easier to debug, but largely making `Single` (and `Populated`) worthless, as they would panic during expected game states. Ultimately, the consensus is that this behavior should differ on a per-system-param basis. However, there was no sensible way to *do* that before this PR. ## Solution Swap `SystemParam::validate_param` from a `bool` to: ```rust /// The outcome of system / system param validation, /// used by system executors to determine what to do with a system. pub enum ValidationOutcome { /// All system parameters were validated successfully and the system can be run. Valid, /// At least one system parameter failed validation, and an error must be handled. /// By default, this will result in1 a panic. See [crate::error] for more information. /// /// This is the default behavior, and is suitable for system params that should *always* be valid, /// either because sensible fallback behavior exists (like [`Query`] or because /// failures in validation should be considered a bug in the user's logic that must be immediately addressed (like [`Res`]). Invalid, /// At least one system parameter failed validation, but the system should be skipped due to [`ValidationBehavior::Skip`]. /// This is suitable for system params that are intended to only operate in certain application states, such as [`Single`]. Skipped, } ``` Then, inside of each `SystemParam` implementation, return either Valid, Invalid or Skipped. Currently, only `Single`, `Option<Single>` and `Populated` use the `Skipped` behavior. Other params (like resources) retain their current failing ## Testing Messed around with the fallible_params example. Added a pair of tests: one for panicking when resources are missing, and another for properly skipping `Single` and `Populated` system params. ## To do - [x] get https://github.com/bevyengine/bevy/pull/18454 merged - [x] fix the todo!() in the macro-powered tuple implementation (please help 🥺) - [x] test - [x] write a migration guide - [x] update the example comments ## Migration Guide Various system and system parameter validation methods (`SystemParam::validate_param`, `System::validate_param` and `System::validate_param_unsafe`) now return and accept a `ValidationOutcome` enum, rather than a `bool`. The previous `true` values map to `ValidationOutcome::Valid`, while `false` maps to `ValidationOutcome::Invalid`. However, if you wrote a custom schedule executor, you should now respect the new `ValidationOutcome::Skipped` parameter, skipping any systems whose validation was skipped. By contrast, `ValidationOutcome::Invalid` systems should also be skipped, but you should call the `default_error_handler` on them first, which by default will result in a panic. If you are implementing a custom `SystemParam`, you should consider whether failing system param validation is an error or an expected state, and choose between `Invalid` and `Skipped` accordingly. In Bevy itself, `Single` and `Populated` now once again skip the system when their conditions are not met. This is the 0.15.0 behavior, but stands in contrast to the 0.15.1 behavior, where they would panic. --------- Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> Co-authored-by: Dmytro Banin <banind@cs.washington.edu> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
ce7d4e41d6
|
Make system param validation rely on the unified ECS error handling via the GLOBAL_ERROR_HANDLER (#18454)
# Objective There are two related problems here: 1. Users should be able to change the fallback behavior of *all* ECS-based errors in their application by setting the `GLOBAL_ERROR_HANDLER`. See #18351 for earlier work in this vein. 2. The existing solution (#15500) for customizing this behavior is high on boilerplate, not global and adds a great deal of complexity. The consensus is that the default behavior when a parameter fails validation should be set based on the kind of system parameter in question: `Single` / `Populated` should silently skip the system, but `Res` should panic. Setting this behavior at the system level is a bandaid that makes getting to that ideal behavior more painful, and can mask real failures (if a resource is missing but you've ignored a system to make the Single stop panicking you're going to have a bad day). ## Solution I've removed the existing `ParamWarnPolicy`-based configuration, and wired up the `GLOBAL_ERROR_HANDLER`/`default_error_handler` to the various schedule executors to properly plumb through errors . Additionally, I've done a small cleanup pass on the corresponding example. ## Testing I've run the `fallible_params` example, with both the default and a custom global error handler. The former panics (as expected), and the latter spams the error console with warnings 🥲 ## Questions for reviewers 1. Currently, failed system param validation will result in endless console spam. Do you want me to implement a solution for warn_once-style debouncing somehow? 2. Currently, the error reporting for failed system param validation is very limited: all we get is that a system param failed validation and the name of the system. Do you want me to implement improved error reporting by bubbling up errors in this PR? 3. There is broad consensus that the default behavior for failed system param validation should be set on a per-system param basis. Would you like me to implement that in this PR? My gut instinct is that we absolutely want to solve 2 and 3, but it will be much easier to do that work (and review it) if we split the PRs apart. ## Migration Guide `ParamWarnPolicy` and the `WithParamWarnPolicy` have been removed completely. Failures during system param validation are now handled via the `GLOBAL_ERROR_HANDLER`: please see the `bevy_ecs::error` module docs for more information. --------- Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> |
||
![]() |
8d9f948684
|
Create new NonSendMarker (#18301)
# Objective Create new `NonSendMarker` that does not depend on `NonSend`. Required, in order to accomplish #17682. In that issue, we are trying to replace `!Send` resources with `thread_local!` in order to unblock the resources-as-components effort. However, when we remove all the `!Send` resources from a system, that allows the system to run on a thread other than the main thread, which is against the design of the system. So this marker gives us the control to require a system to run on the main thread without depending on `!Send` resources. ## Solution Create a new `NonSendMarker` to replace the existing one that does not depend on `NonSend`. ## Testing Other than running tests, I ran a few examples: - `window_resizing` - `wireframe` - `volumetric_fog` (looks so cool) - `rotation` - `button` There is a Mac/iOS-specific change and I do not have a Mac or iOS device to test it. I am doubtful that it would cause any problems for 2 reasons: 1. The change is the same as the non-wasm change which I did test 2. The Pixel Eagle tests run Mac tests But it wouldn't hurt if someone wanted to spin up an example that utilizes the `bevy_render` crate, which is where the Mac/iSO change was. ## Migration Guide If `NonSendMarker` is being used from `bevy_app::prelude::*`, replace it with `bevy_ecs::system::NonSendMarker` or use it from `bevy_ecs::prelude::*`. In addition to that, `NonSendMarker` does not need to be wrapped like so: ```rust fn my_system(_non_send_marker: Option<NonSend<NonSendMarker>>) { ... } ``` Instead, it can be used without any wrappers: ```rust fn my_system(_non_send_marker: NonSendMarker) { ... } ``` --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
5d0505a85e
|
Unify and simplify command and system error handling (#18351)
# Objective - ECS error handling is a lovely flagship feature for Bevy 0.16, all in the name of reducing panics and encouraging better error handling (#14275). - Currently though, command and system error handling are completely disjoint and use different mechanisms. - Additionally, there's a number of distinct ways to set the default/fallback/global error handler that have limited value. As far as I can tell, this will be cfg flagged to toggle between dev and production builds in 99.9% of cases, with no real value in more granular settings or helpers. - Fixes #17272 ## Solution - Standardize error handling on the OnceLock global error mechanisms ironed out in https://github.com/bevyengine/bevy/pull/17215 - As discussed there, there are serious performance concerns there, especially for commands - I also think this is a better fit for the use cases, as it's truly global - Move from `SystemErrorContext` to a more general purpose `ErrorContext`, which can handle observers and commands more clearly - Cut the superfluous setter methods on `App` and `SubApp` - Rename the limited (and unhelpful) `fallible_systems` example to `error_handling`, and add an example of command error handling ## Testing Ran the `error_handling` example. ## Notes for reviewers - Do you see a clear way to allow commands to retain &mut World access in the per-command custom error handlers? IMO that's a key feature here (allowing the ad-hoc creation of custom commands), but I'm not sure how to get there without exploding complexity. - I've removed the feature gate on the default_error_handler: contrary to @cart's opinion in #17215 I think that virtually all apps will want to use this. Can you think of a category of app that a) is extremely performance sensitive b) is fine with shipping to production with the panic error handler? If so, I can try to gather performance numbers and/or reintroduce the feature flag. UPDATE: see benches at the end of this message. - ~~`OnceLock` is in `std`: @bushrat011899 what should we do here?~~ - Do you have ideas for more automated tests for this collection of features? ## Benchmarks I checked the impact of the feature flag introduced: benchmarks might show regressions. This bears more investigation. I'm still skeptical that there are users who are well-served by a fast always panicking approach, but I'm going to re-add the feature flag here to avoid stalling this out.  --------- Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
![]() |
6d6054116a
|
Support skipping Relationship on_replace hooks (#18378)
# Objective Fixes #18357 ## Solution Generalize `RelationshipInsertHookMode` to `RelationshipHookMode`, wire it up to on_replace execution, and use it in the `Relationship::on_replace` hook. |
||
![]() |
9b32e09551
|
bevy_reflect: Add clone registrations project-wide (#18307)
# Objective Now that #13432 has been merged, it's important we update our reflected types to properly opt into this feature. If we do not, then this could cause issues for users downstream who want to make use of reflection-based cloning. ## Solution This PR is broken into 4 commits: 1. Add `#[reflect(Clone)]` on all types marked `#[reflect(opaque)]` that are also `Clone`. This is mandatory as these types would otherwise cause the cloning operation to fail for any type that contains it at any depth. 2. Update the reflection example to suggest adding `#[reflect(Clone)]` on opaque types. 3. Add `#[reflect(clone)]` attributes on all fields marked `#[reflect(ignore)]` that are also `Clone`. This prevents the ignored field from causing the cloning operation to fail. Note that some of the types that contain these fields are also `Clone`, and thus can be marked `#[reflect(Clone)]`. This makes the `#[reflect(clone)]` attribute redundant. However, I think it's safer to keep it marked in the case that the `Clone` impl/derive is ever removed. I'm open to removing them, though, if people disagree. 4. Finally, I added `#[reflect(Clone)]` on all types that are also `Clone`. While not strictly necessary, it enables us to reduce the generated output since we can just call `Clone::clone` directly instead of calling `PartialReflect::reflect_clone` on each variant/field. It also means we benefit from any optimizations or customizations made in the `Clone` impl, including directly dereferencing `Copy` values and increasing reference counters. Along with that change I also took the liberty of adding any missing registrations that I saw could be applied to the type as well, such as `Default`, `PartialEq`, and `Hash`. There were hundreds of these to edit, though, so it's possible I missed quite a few. That last commit is **_massive_**. There were nearly 700 types to update. So it's recommended to review the first three before moving onto that last one. Additionally, I can break the last commit off into its own PR or into smaller PRs, but I figured this would be the easiest way of doing it (and in a timely manner since I unfortunately don't have as much time as I used to for code contributions). ## Testing You can test locally with a `cargo check`: ``` cargo check --workspace --all-features ``` |
||
![]() |
b7d5254762
|
implement get_many_unique (#18315)
# Objective Continuation to #16547 and #17954. The `get_many` family are the last methods on `Query`/`QueryState` for which we're still missing a `unique` version. ## Solution Offer `get_many_unique`/`get_many_unique_mut` and `get_many_unique_inner`! Their implementation is the same as `get_many`, the difference lies in their guaranteed-to-be unique inputs, meaning we never do any aliasing checks. To reduce confusion, we also rename `get_many_readonly` into `get_many_inner` and the current `get_many_inner` into `get_many_mut_inner` to clarify their purposes. ## Testing Doc examples. ## Migration Guide `get_many_inner` is now called `get_many_mut_inner`. `get_many_readonly` is now called `get_many_inner`. |
||
![]() |
ecccd57417
|
Generic system config (#17962)
# Objective Prevents duplicate implementation between IntoSystemConfigs and IntoSystemSetConfigs using a generic, adds a NodeType trait for more config flexibility (opening the door to implement https://github.com/bevyengine/bevy/issues/14195?). ## Solution Followed writeup by @ItsDoot: https://hackmd.io/@doot/rJeefFHc1x Removes IntoSystemConfigs and IntoSystemSetConfigs, instead using IntoNodeConfigs with generics. ## Testing Pending --- ## Showcase N/A ## Migration Guide SystemSetConfigs -> NodeConfigs<InternedSystemSet> SystemConfigs -> NodeConfigs<ScheduleSystem> IntoSystemSetConfigs -> IntoNodeConfigs<InternedSystemSet, M> IntoSystemConfigs -> IntoNodeConfigs<ScheduleSystem, M> --------- Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
8570af1d96
|
Add print_stdout and print_stderr lints (#17446) (#18233)
# Objective - Prevent usage of `println!`, `eprintln!` and the like because they require `std` - Fixes #17446 ## Solution - Enable the `print_stdout` and `print_stderr` clippy lints - Replace all `println!` and `eprintln!` occurrences with `log::*` where applicable or alternatively ignore the warnings ## Testing - Run `cargo clippy --workspace` to ensure that there are no warnings relating to printing to `stdout` or `stderr` |
||
![]() |
246ce590e5
|
Queued component registration (#18173)
# Objective This is an alternative to #17871 and #17701 for tracking issue #18155. This thanks to @maniwani for help with this design. The goal is to enable component ids to be reserved from multiple threads concurrently and with only `&World`. This contributes to assets as entities, read-only query and system parameter initialization, etc. ## What's wrong with #17871 ? In #17871, I used my proposed staging utilities to allow *fully* registering components from any thread concurrently with only `&Components`. However, if we want to pursue components as entities (which is desirable for a great many reasons. See [here](https://discord.com/channels/691052431525675048/692572690833473578/1346499196655505534) on discord), this staging isn't going to work. After all, if registering a component requires spawning an entity, and spawning an entity requires `&mut World`, it is impossible to register a component fully with only `&World`. ## Solution But what if we don't have to register it all the way? What if it's enough to just know the `ComponentId` it will have once it is registered and to queue it to be registered at a later time? Spoiler alert: That is all we need for these features. Here's the basic design: Queue a registration: 1. Check if it has already been registered. 2. Check if it has already been queued. 3. Reserve a `ComponentId`. 4. Queue the registration at that id. Direct (normal) registration: 1. Check if this registration has been queued. 2. If it has, use the queued registration instead. 3. Otherwise, proceed like normal. Appllying the queue: 1. Pop queued items off one by one. 2. Register them directly. One other change: The whole point of this design over #17871 is to facilitate coupling component registration with the World. To ensure that this would fully work with that, I went ahead and moved the `ComponentId` generator onto the world itself. That stemmed a couple of minor organizational changes (see migration guide). As we do components as entities, we will replace this generator with `Entities`, which lives on `World` too. Doing this move early let me verify the design and will reduce migration headaches in the future. If components as entities is as close as I think it is, I don't think splitting this up into different PRs is worth it. If it is not as close as it is, it might make sense to still do #17871 in the meantime (see the risks section). I'll leave it up to y'all what we end up doing though. ## Risks and Testing The biggest downside of this compared to #17871 is that now we have to deal with correct but invalid `ComponentId`s. They are invalid because the component still isn't registered, but they are correct because, once registered, the component will have exactly that id. However, the only time this becomes a problem is if some code violates safety rules by queuing a registration and using the returned id as if it was valid. As this is a new feature though, nothing in Bevy does this, so no new tests were added for it. When we do use it, I left detailed docs to help mitigate issues here, and we can test those usages. Ex: we will want some tests on using queries initialized from queued registrations. ## Migration Guide Component registration can now be queued with only `&World`. To facilitate this, a few APIs needed to be moved around. The following functions have moved from `Components` to `ComponentsRegistrator`: - `register_component` - `register_component_with_descriptor` - `register_resource_with_descriptor` - `register_non_send` - `register_resource` - `register_required_components_manual` Accordingly, functions in `Bundle` and `Component` now take `ComponentsRegistrator` instead of `Components`. You can obtain `ComponentsRegistrator` from the new `World::components_registrator`. You can obtain `ComponentsQueuedRegistrator` from the new `World::components_queue`, and use it to stage component registration if desired. # Open Question Can we verify that it is enough to queue registration with `&World`? I don't think it would be too difficult to package this up into a `Arc<MyComponentsManager>` type thing if we need to, but keeping this on `&World` certainly simplifies things. If we do need the `Arc`, we'll need to look into partitioning `Entities` for components as entities, so we can keep most of the allocation fast on `World` and only keep a smaller partition in the `Arc`. I'd love an SME on assets as entities to shed some light on this. --------- Co-authored-by: andriyDev <andriydzikh@gmail.com> |
||
![]() |
54247bcf86
|
Recursive run_system (#18076)
# Objective Fixes #18030 ## Solution When running a one-shot system, requeue the system's command queue onto the world's command queue, then execute the later. If running the entire command queue of the world is undesired, I could add a new method to `RawCommandQueue` to only apply part of it. ## Testing See the new test. --- ## Showcase ```rust #[derive(Resource)] pub struct Test { id: SystemId, counter: u32, } let mut world = World::new(); let id = world.register_system(|mut commands: Commands, mut test: ResMut<Test>| { print!("{:?} ", test.counter); test.counter -= 1; if test.counter > 0 { commands.run_system(test.id); } }); world.insert_resource(Test { id, counter: 5 }); world.run_system(id).unwrap(); ``` ``` 5 4 3 2 1 ``` |
||
![]() |
6df711ce7f
|
Fix unsound lifetimes in Query::join and Query::join_filtered (#17972)
# Objective Fix unsound lifetimes in `Query::join` and `Query::join_filtered`. The joined query allowed access from either input query, but it only took the `'world` lifetime from `self`, not from `other`. This meant that after the borrow of `other` ended, the joined query would unsoundly alias `other`. ## Solution Change the lifetimes on `join` and `join_filtered` to require mutable borrows of the *same* lifetime for the input queries. This ensures both input queries are borrowed for the full lifetime of the joined query. Change `join_inner` to take `other` by value instead of reference so that the returned query is still usable without needing to borrow from a local variable. ## Testing Added a compile-fail test. |
||
![]() |
cbc931723e
|
Remove lifetime from QueryEntityError (#18157)
# Objective - Allow `Query` methods such as `Query::get` to have their error short-circuited using `?` in systems using Bevy's `Error` type ## Solution - Removed `UnsafeWorldCell<'w>` from `QueryEntityError` and instead store `ArchetypeId` (the information the error formatter was extracting anyway). - Replaced trait implementations with derives now that the type is plain-old-data. ## Testing - CI --- ## Migration Guide - `QueryEntityError::QueryDoesNotMatch.1` is of type `ArchetypeId` instead of `UnsafeWorldCell`. It is up to the caller to obtain an `UnsafeWorldCell` now. - `QueryEntityError` no longer has a lifetime parameter, remove it from type signatures where required. ## Notes This was discussed on Discord and accepted by Cart as the desirable path forward in [this message](https://discord.com/channels/691052431525675048/749335865876021248/1346611950527713310). Scroll up from this point for context. --------- Co-authored-by: SpecificProtagonist <30270112+SpecificProtagonist@users.noreply.github.com> |
||
![]() |
5bc1d68a65
|
Deprecated Query::many and many_mut (#18183)
# Objective Alternative to and closes #18120. Sibling to #18082, see that PR for broader reasoning. Folks weren't sold on the name `many` (get_many is clearer, and this is rare), and that PR is much more complex. ## Solution - Simply deprecate `Query::many` and `Query::many_mut` - Clean up internal usages Mentions of this in the docs can wait until it's fully removed in the 0.17 cycle IMO: it's much easier to catch the problems when doing that. ## Testing CI! ## Migration Guide `Query::many` and `Query::many_mut` have been deprecated to reduce panics and API duplication. Use `Query::get_many` and `Query::get_many_mut` instead, and handle the `Result`. --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
cca5813472
|
BevyError: Bevy's new catch-all error type (#18144)
## Objective Fixes #18092 Bevy's current error type is a simple type alias for `Box<dyn Error + Send + Sync + 'static>`. This largely works as a catch-all error, but it is missing a critical feature: the ability to capture a backtrace at the point that the error occurs. The best way to do this is `anyhow`-style error handling: a new error type that takes advantage of the fact that the `?` `From` conversion happens "inline" to capture the backtrace at the point of the error. ## Solution This PR adds a new `BevyError` type (replacing our old `std::error::Error` type alias), which uses the "from conversion backtrace capture" approach: ```rust fn oh_no() -> Result<(), BevyError> { // this fails with Rust's built in ParseIntError, which // is converted into the catch-all BevyError type let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` This also updates our exported `Result` type alias to default to `BevyError`, meaning you can write this instead: ```rust fn oh_no() -> Result { let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` When a BevyError is encountered in a system, it will use Bevy's default system error handler (which panics by default). BevyError does custom "backtrace filtering" by default, meaning we can cut out the _massive_ amount of "rust internals", "async executor internals", and "bevy system scheduler internals" that show up in backtraces. It also trims out the first generally-unnecssary `From` conversion backtrace lines that make it harder to locate the real error location. The result is a blissfully simple backtrace by default:  The full backtrace can be shown by setting the `BEVY_BACKTRACE=full` environment variable. Non-BevyError panics still use the default Rust backtrace behavior. One issue that prevented the truly noise-free backtrace during panics that you see above is that Rust's default panic handler will print the unfiltered (and largely unhelpful real-panic-point) backtrace by default, in _addition_ to our filtered BevyError backtrace (with the helpful backtrace origin) that we capture and print. To resolve this, I have extended Bevy's existing PanicHandlerPlugin to wrap the default panic handler. If we panic from the result of a BevyError, we will skip the default "print full backtrace" panic handler. This behavior can be enabled and disabled using the new `error_panic_hook` cargo feature in `bevy_app` (which is enabled by default). One downside to _not_ using `Box<dyn Error>` directly is that we can no longer take advantage of the built-in `Into` impl for strings to errors. To resolve this, I have added the following: ```rust // Before Err("some error")? // After Err(BevyError::message("some error"))? ``` We can discuss adding shorthand methods or macros for this (similar to anyhow's `anyhow!("some error")` macro), but I'd prefer to discuss that later. I have also added the following extension method: ```rust // Before some_option.ok_or("some error")?; // After some_option.ok_or_message("some error")?; ``` I've also moved all of our existing error infrastructure from `bevy_ecs::result` to `bevy_ecs::error`, as I think that is the better home for it ## Why not anyhow (or eyre)? The biggest reason is that `anyhow` needs to be a "generically useful error type", whereas Bevy is a much narrower scope. By using our own error, we can be significantly more opinionated. For example, anyhow doesn't do the extensive (and invasive) backtrace filtering that BevyError does because it can't operate on Bevy-specific context, and needs to be generically useful. Bevy also has a lot of operational context (ex: system info) that could be useful to attach to errors. If we have control over the error type, we can add whatever context we want to in a structured way. This could be increasingly useful as we add more visual / interactive error handling tools and editor integrations. Additionally, the core approach used is simple and requires almost no code. anyhow clocks in at ~2500 lines of code, but the impl here uses 160. We are able to boil this down to exactly what we need, and by doing so we improve our compile times and the understandability of our code. |
||
![]() |
ed7b366b24
|
Deprecate insert_or_spawn function family (#18147)
# Objective Based on #18054, this PR builds on #18035 to deprecate: - `Commands::insert_or_spawn_batch` - `Entities::alloc_at_without_replacement` - `Entities::alloc_at` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` ## Testing Just deprecation, so no new tests. Note that as of writing #18035 is still under testing and review. ## Open Questions - [x] Should `entity::AllocAtWithoutReplacement` be deprecated? It is internal and only used in `Entities::alloc_at_without_replacement`. **EDIT:** Now deprecated. ## Migration Guide The following functions have been deprecated: - `Commands::insert_or_spawn_batch` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` These functions, when used incorrectly, can cause major performance problems and are generally viewed as anti-patterns and foot guns. These are planned to be removed altogether in 0.17. Instead of these functions consider doing one of the following: Option A) Instead of despawing entities and re-spawning them at a particular id, insert the new `Disabled` component without despawning the entity, and use `try_insert_batch` or `insert_batch` and remove `Disabled` instead of re-spawning it. Option B) Instead of giving special meaning to an entity id, simply use `spawn_batch` and ensure entity references are valid when despawning. --------- Co-authored-by: JaySpruce <jsprucebruce@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
06cb5c5fd9
|
Fix Component require() IDE integration (#18165)
# Objective Component `require()` IDE integration is fully broken, as of #16575. ## Solution This reverts us back to the previous "put the docs on Component trait" impl. This _does_ reduce the accessibility of the required components in rust docs, but the complete erasure of "required component IDE experience" is not worth the price of slightly increased prominence of requires in docs. Additionally, Rust Analyzer has recently started including derive attributes in suggestions, so we aren't losing that benefit of the proc_macro attribute impl. |
||
![]() |
a530c07bc5
|
Preserve spawned RelationshipTarget order and other improvements (#17858)
Fixes #17720 ## Objective Spawning RelationshipTargets from scenes currently fails to preserve RelationshipTarget ordering (ex: `Children` has an arbitrary order). This is because it uses the normal hook flow to set up the collection, which means we are pushing onto the collection in _spawn order_ (which is currently in archetype order, which will often produce mismatched orderings). We need to preserve the ordering in the original RelationshipTarget collection. Ideally without expensive checking / fixups. ## Solution One solution would be to spawn in hierarchy-order. However this gets complicated as there can be multiple hierarchies, and it also means we can't spawn in more cache-friendly orders (ex: the current per-archetype spawning, or future even-smarter per-table spawning). Additionally, same-world cloning has _slightly_ more nuanced needs (ex: recursively clone linked relationships, while maintaining _original_ relationships outside of the tree via normal hooks). The preferred approach is to directly spawn the remapped RelationshipTarget collection, as this trivially preserves the ordering. Unfortunately we can't _just_ do that, as when we spawn the children with their Relationships (ex: `ChildOf`), that will insert a duplicate. We could "fixup" the collection retroactively by just removing the back half of duplicates, but this requires another pass / more lookups / allocating twice as much space. Additionally, it becomes complicated because observers could insert additional children, making it harder (aka more expensive) to determine which children are dupes and which are not. The path I chose is to support "opting out" of the relationship target hook in the contexts that need that, as this allows us to just cheaply clone the mapped collection. The relationship hook can look for this configuration when it runs and skip its logic when that happens. A "simple" / small-amount-of-code way to do this would be to add a "skip relationship spawn" flag to World. Sadly, any hook / observer that runs _as the result of an insert_ would also read this flag. We really need a way to scope this setting to a _specific_ insert. Therefore I opted to add a new `RelationshipInsertHookMode` enum and an `entity.insert_with_relationship_insert_hook_mode` variant. Obviously this is verbose and ugly. And nobody wants _more_ insert variants. But sadly this was the best I could come up with from a performance and capability perspective. If you have alternatives let me know! There are three variants: 1. `RelationshipInsertHookMode::Run`: always run relationship insert hooks (this is the default) 2. `RelationshipInsertHookMode::Skip`: do not run any relationship insert hooks for this insert (this is used by spawner code) 3. `RelationshipInsertHookMode::RunIfNotLinked`: only run hooks for _unlinked_ relationships (this is used in same-world recursive entity cloning to preserve relationships outside of the deep-cloned tree) Note that I have intentionally only added "insert with relationship hook mode" variants to the cases we absolutely need (everything else uses the default `Run` mode), just to keep the code size in check. I do not think we should add more without real _very necessary_ use cases. I also made some other minor tweaks: 1. I split out `SourceComponent` from `ComponentCloneCtx`. Reading the source component no longer needlessly blocks mutable access to `ComponentCloneCtx`. 2. Thanks to (1), I've removed the `RefCell` wrapper over the cloned component queue. 3. (1) also allowed me to write to the EntityMapper while queuing up clones, meaning we can reserve entities during the component clone and write them to the mapper _before_ inserting the component, meaning cloned collections can be mapped on insert. 4. I've removed the closure from `write_target_component_ptr` to simplify the API / make it compatible with the split `SourceComponent` approach. 5. I've renamed `EntityCloner::recursive` to `EntityCloner::linked_cloning` to connect that feature more directly with `RelationshipTarget::LINKED_SPAWN` 6. I've removed `EntityCloneBehavior::RelationshipTarget`. This was always intended to be temporary, and this new behavior removes the need for it. --------- Co-authored-by: Viktor Gustavsson <villor94@gmail.com> |
||
![]() |
1f6642df4c
|
Fix unsound query transmutes on queries obtained from Query::as_readonly() (#17973)
# Objective Fix unsound query transmutes on queries obtained from `Query::as_readonly()`. The following compiles, and the call to `transmute_lens()` should panic, but does not: ```rust fn bad_system(query: Query<&mut A>) { let mut readonly = query.as_readonly(); let mut lens: QueryLens<&mut A> = readonly.transmute_lens(); let other_readonly: Query<&A> = query.as_readonly(); // `lens` and `other_readonly` alias, and are both alive here! } ``` To make `Query::as_readonly()` zero-cost, we pointer-cast `&QueryState<D, F>` to `&QueryState<D::ReadOnly, F>`. This means that the `component_access` for a read-only query's state may include accesses for the original mutable version, but the `Query` does not have exclusive access to those components! `transmute` and `join` use that access to ensure that a join is valid, and will incorrectly allow a transmute that includes mutable access. As a bonus, allow `Query::join`s that output `FilteredEntityRef` or `FilteredEntityMut` to receive access from the `other` query. Currently they only receive access from `self`. ## Solution When transmuting or joining from a read-only query, remove any writes before performing checking that the transmute is valid. For joins, be sure to handle the case where one input query was the result of `as_readonly()` but the other has valid mutable access. This requires identifying read-only queries, so add a `QueryData::IS_READ_ONLY` associated constant. Note that we only call `QueryState::as_transmuted_state()` with `NewD: ReadOnlyQueryData`, so checking for read-only queries is sufficient to check for `as_transmuted_state()`. Removing writes requires allocating a new `FilteredAccess`, so only do so if the query is read-only and the state has writes. Otherwise, the existing access is correct and we can continue using a reference to it. Use the new read-only state to call `NewD::set_access`, so that transmuting to a `FilteredAccessMut` results in a read-only `FilteredAccessMut`. Otherwise, it would take the original write access, and then the transmute would panic because it had too much access. Note that `join` was previously passing `self.component_access` to `NewD::set_access`. Switching it to `joined_component_access` also allows a join that outputs `FilteredEntity(Ref|Mut)` to receive access from `other`. The fact that it didn't do that before seems like an oversight, so I didn't try to prevent that change. ## Testing Added unit tests with the unsound transmute and join. |
||
![]() |
c819beb02c
|
Emphasize no structural changes in SystemParam::get_param (#17996)
# Objective Many systems like `Schedule` rely on the fact that every structural ECS changes are deferred until an exclusive system flushes the `World` itself. This gives us the benefits of being able to run systems in parallel without worrying about dangling references caused by memory (re)allocations, which will in turn lead to **Undefined Behavior**. However, this isn't explicitly documented in `SystemParam`; currently it only vaguely hints that in `init_state`, based on the fact that structural ECS changes require mutable access to the _whole_ `World`. ## Solution Document this behavior explicitly in `SystemParam`'s type-level documentations. |
||
![]() |
0a841ba9c1
|
Cache systems by S instead of S::System (#16694)
# Objective - Fixes the issue described in this comment: https://github.com/bevyengine/bevy/issues/16680#issuecomment-2522764239. ## Solution - Cache one-shot systems by `S: IntoSystem` (which is const-asserted to be a ZST) rather than `S::System`. ## Testing Added a new unit test named `cached_system_into_same_system_type` to `system_registry.rs`. --- ## Migration Guide The `CachedSystemId` resource has been changed: ```rust // Before: let cached_id = CachedSystemId::<S::System>(id); assert!(id == cached_id.0); // After: let cached_id = CachedSystemId::<S>::new(id); assert!(id == SystemId::from_entity(cached_id.entity)); ``` |
||
![]() |
2ad5908e58
|
Make Query::single (and friends) return a Result (#18082)
# Objective As discussed in #14275, Bevy is currently too prone to panic, and makes the easy / beginner-friendly way to do a large number of operations just to panic on failure. This is seriously frustrating in library code, but also slows down development, as many of the `Query::single` panics can actually safely be an early return (these panics are often due to a small ordering issue or a change in game state. More critically, in most "finished" products, panics are unacceptable: any unexpected failures should be handled elsewhere. That's where the new With the advent of good system error handling, we can now remove this. Note: I was instrumental in a) introducing this idea in the first place and b) pushing to make the panicking variant the default. The introduction of both `let else` statements in Rust and the fancy system error handling work in 0.16 have changed my mind on the right balance here. ## Solution 1. Make `Query::single` and `Query::single_mut` (and other random related methods) return a `Result`. 2. Handle all of Bevy's internal usage of these APIs. 3. Deprecate `Query::get_single` and friends, since we've moved their functionality to the nice names. 4. Add detailed advice on how to best handle these errors. Generally I like the diff here, although `get_single().unwrap()` in tests is a bit of a downgrade. ## Testing I've done a global search for `.single` to track down any missed deprecated usages. As to whether or not all the migrations were successful, that's what CI is for :) ## Future work ~~Rename `Query::get_single` and friends to `Query::single`!~~ ~~I've opted not to do this in this PR, and smear it across two releases in order to ease the migration. Successive deprecations are much easier to manage than the semantics and types shifting under your feet.~~ Cart has convinced me to change my mind on this; see https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085. ## Migration guide `Query::single`, `Query::single_mut` and their `QueryState` equivalents now return a `Result`. Generally, you'll want to: 1. Use Bevy 0.16's system error handling to return a `Result` using the `?` operator. 2. Use a `let else Ok(data)` block to early return if it's an expected failure. 3. Use `unwrap()` or `Ok` destructuring inside of tests. The old `Query::get_single` (etc) methods which did this have been deprecated. |
||
![]() |
ad1691e44a
|
Update EntityCommands::trigger to check for the entity's existence (#18071)
## Objective `EntityCommands::trigger` internally uses `Commands::trigger_targets`, which means it gets queued using `Commands::queue` rather `EntityCommands::queue`. This previously wouldn't have made much difference, but now entity commands check whether the entity exists, and that check never happens in this case. ## Solution - Add `entity_command::trigger`, which calls the same function as before (`World::trigger_targets_with_caller`) but through the `EntityWorldMut` passed to entity commands. - Change `EntityCommands::trigger` to queue the new entity command normally. |
||
![]() |
058497e0bb
|
Change Commands::get_entity to return Result and remove panic from Commands::entity (#18043)
## Objective Alternative to #18001. - Now that systems can handle the `?` operator, `get_entity` returning `Result` would be more useful than `Option`. - With `get_entity` being more flexible, combined with entity commands now checking the entity's existence automatically, the panic in `entity` isn't really necessary. ## Solution - Changed `Commands::get_entity` to return `Result<EntityCommands, EntityDoesNotExistError>`. - Removed panic from `Commands::entity`. |
||
![]() |
67146bdef7
|
Add missing unsafe to entity_command::insert_by_id and make it more configurable (#18052)
## Objective `insert_by_id` is unsafe, but I forgot to add that to the manually-queueable version in `entity_command`. It also can only insert using `InsertMode::Replace`, when it could easily be configurable by threading an `InsertMode` parameter to the final `BundleInserter::insert` call. ## Solution - Add `unsafe` and safety comment. - Add `InsertMode` parameter to `entity_command::insert_by_id`, `EntityWorldMut::insert_by_id_with_caller`, and `EntityWorldMut::insert_dynamic_bundle`. - Add `InsertMode` parameter to `entity_command::insert` and remove `entity_command::insert_if_new`, for consistency with the other manually-queued insertion commands. |
||
![]() |
5241e09671
|
Upgrade to Rust Edition 2024 (#17967)
# Objective - Fixes #17960 ## Solution - Followed the [edition upgrade guide](https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html) ## Testing - CI --- ## Summary of Changes ### Documentation Indentation When using lists in documentation, proper indentation is now linted for. This means subsequent lines within the same list item must start at the same indentation level as the item. ```rust /* Valid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; /* Invalid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; ``` ### Implicit `!` to `()` Conversion `!` (the never return type, returned by `panic!`, etc.) no longer implicitly converts to `()`. This is particularly painful for systems with `todo!` or `panic!` statements, as they will no longer be functions returning `()` (or `Result<()>`), making them invalid systems for functions like `add_systems`. The ideal fix would be to accept functions returning `!` (or rather, _not_ returning), but this is blocked on the [stabilisation of the `!` type itself](https://doc.rust-lang.org/std/primitive.never.html), which is not done. The "simple" fix would be to add an explicit `-> ()` to system signatures (e.g., `|| { todo!() }` becomes `|| -> () { todo!() }`). However, this is _also_ banned, as there is an existing lint which (IMO, incorrectly) marks this as an unnecessary annotation. So, the "fix" (read: workaround) is to put these kinds of `|| -> ! { ... }` closuers into variables and give the variable an explicit type (e.g., `fn()`). ```rust // Valid let system: fn() = || todo!("Not implemented yet!"); app.add_systems(..., system); // Invalid app.add_systems(..., || todo!("Not implemented yet!")); ``` ### Temporary Variable Lifetimes The order in which temporary variables are dropped has changed. The simple fix here is _usually_ to just assign temporaries to a named variable before use. ### `gen` is a keyword We can no longer use the name `gen` as it is reserved for a future generator syntax. This involved replacing uses of the name `gen` with `r#gen` (the raw-identifier syntax). ### Formatting has changed Use statements have had the order of imports changed, causing a substantial +/-3,000 diff when applied. For now, I have opted-out of this change by amending `rustfmt.toml` ```toml style_edition = "2021" ``` This preserves the original formatting for now, reducing the size of this PR. It would be a simple followup to update this to 2024 and run `cargo fmt`. ### New `use<>` Opt-Out Syntax Lifetimes are now implicitly included in RPIT types. There was a handful of instances where it needed to be added to satisfy the borrow checker, but there may be more cases where it _should_ be added to avoid breakages in user code. ### `MyUnitStruct { .. }` is an invalid pattern Previously, you could match against unit structs (and unit enum variants) with a `{ .. }` destructuring. This is no longer valid. ### Pretty much every use of `ref` and `mut` are gone Pattern binding has changed to the point where these terms are largely unused now. They still serve a purpose, but it is far more niche now. ### `iter::repeat(...).take(...)` is bad New lint recommends using the more explicit `iter::repeat_n(..., ...)` instead. ## Migration Guide The lifetimes of functions using return-position impl-trait (RPIT) are likely _more_ conservative than they had been previously. If you encounter lifetime issues with such a function, please create an issue to investigate the addition of `+ use<...>`. ## Notes - Check the individual commits for a clearer breakdown for what _actually_ changed. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
283654cf4d
|
Small Commands error handling cleanup (#17904)
- Remove references to the short-lived `CommandError` type. - Add a sentence to the explanation of error handlers. - Clean up spacing/linebreaks. - Use `where` notation for command-related trait `impl`s to make the big ones easier to parse. |
||
![]() |
5f86668bbb
|
Renamed EventWriter::send methods to write . (#17977)
Fixes #17856. ## Migration Guide - `EventWriter::send` has been renamed to `EventWriter::write`. - `EventWriter::send_batch` has been renamed to `EventWriter::write_batch`. - `EventWriter::send_default` has been renamed to `EventWriter::write_default`. --------- Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
ee44560523
|
Add EntityDoesNotExistError , replace cases of Entity as an error, do some easy Resultification (#17855)
## Objective There's no general error for when an entity doesn't exist, and some methods are going to need one when they get Resultified. The closest thing is `EntityFetchError`, but that error has a slightly more specific purpose. ## Solution - Added `EntityDoesNotExistError`. - Contains `Entity` and `EntityDoesNotExistDetails`. - Changed `EntityFetchError` and `QueryEntityError`: - Changed `NoSuchEntity` variant to wrap `EntityDoesNotExistError` and renamed the variant to `EntityDoesNotExist`. - Renamed `EntityFetchError` to `EntityMutableFetchError` to make its purpose clearer. - Renamed `TryDespawnError` to `EntityDespawnError` to make it more general. - Changed `World::inspect_entity` to return `Result<[ok], EntityDoesNotExistError>` instead of panicking. - Changed `World::get_entity` and `WorldEntityFetch::fetch_ref` to return `Result<[ok], EntityDoesNotExistError>` instead of `Result<[ok], Entity>`. - Changed `UnsafeWorldCell::get_entity` to return `Result<UnsafeEntityCell, EntityDoesNotExistError>` instead of `Option<UnsafeEntityCell>`. ## Migration Guide - `World::inspect_entity` now returns `Result<impl Iterator<Item = &ComponentInfo>, EntityDoesNotExistError>` instead of `impl Iterator<Item = &ComponentInfo>`. - `World::get_entity` now returns `EntityDoesNotExistError` as an error instead of `Entity`. You can still access the entity's ID through the error's `entity` field. - `UnsafeWorldCell::get_entity` now returns `Result<UnsafeEntityCell, EntityDoesNotExistError>` instead of `Option<UnsafeEntityCell>`. |
||
![]() |
794bf6a332
|
Move implementations of Query methods from QueryState to Query . (#17822)
# Objective Simplify the API surface by removing duplicated functionality between `Query` and `QueryState`. Reduce the amount of `unsafe` code required in `QueryState`. This is a follow-up to #15858. ## Solution Move implementations of `Query` methods from `QueryState` to `Query`. Instead of the original methods being on `QueryState`, with `Query` methods calling them by passing the individual parameters, the original methods are now on `Query`, with `QueryState` methods calling them by constructing a `Query`. This also adds two `_inner` methods that were missed in #15858: `iter_many_unique_inner` and `single_inner`. One goal here is to be able to deprecate and eventually remove many of the methods on `QueryState`, reducing the overall API surface. (I expected to do that in this PR, but this change was large enough on its own!) Now that the `QueryState` methods each consist of a simple expression like `self.query(world).get_inner(entity)`, a future PR can deprecate some or all of them with simple migration instructions. The other goal is to reduce the amount of `unsafe` code. The current implementation of a read-only method like `QueryState::get` directly calls the `unsafe fn get_unchecked_manual` and needs to repeat the proof that `&World` has enough access. With this change, `QueryState::get` is entirely safe code, with the proof that `&World` has enough access done by the `query()` method and shared across all read-only operations. ## Future Work The next step will be to mark the `QueryState` methods as `#[deprecated]` and migrate callers to the methods on `Query`. |
||
![]() |
0a32450715
|
Support using FilteredResources with ReflectResource. (#15624)
# Objective Support accessing resources using reflection when using `FilteredResources` in a dynamic system. This is similar to how components can be queried using reflection when using `FilteredEntityRef|Mut`. ## Solution Change `ReflectResource` from taking `&World` and `&mut World` to taking `impl Into<FilteredResources>` and `impl Into<FilteredResourcesMut>`, similar to how `ReflectComponent` takes `impl Into<FilteredEntityRef>` and `impl Into<FilteredEntityMut>`. There are `From` impls that ensure code passing `&World` and `&mut World` continues to work as before. ## Migration Guide If you are manually creating a `ReflectComponentFns` struct, the `reflect` function now takes `FilteredResources` instead `&World`, and there is a new `reflect_mut` function that takes `FilteredResourcesMut`. |
||
![]() |
05e61d64f5
|
implement par_iter_many and par_iter_many_unique (#17815)
# Objective Continuation of #16547. We do not yet have parallel versions of `par_iter_many` and `par_iter_many_unique`. It is currently very painful to try and use parallel iteration over entity lists. Even if a list is not long, each operation might still be very expensive, and worth parallelizing. Plus, it has been requested several times! ## Solution Once again, we implement what we lack! These parallel iterators collect their input entity list into a `Vec`/`UniqueEntityVec`, then chunk that over the available threads, inspired by the original `par_iter`. Since no order guarantee is given to the caller, we could sort the input list according to `EntityLocation`, but that would likely only be worth it for very large entity lists. There is some duplication which could likely be improved, but I'd like to leave that for a follow-up. ## Testing The doc tests on `for_each_init` of `QueryParManyIter` and `QueryParManyUniqueIter`. |
||
![]() |
62c1812e72
|
Shorten the 'world lifetime returned from QueryLens::query() . (#17694)
# Objective Fix unsoundness introduced by #15858. `QueryLens::query()` would hand out a `Query` with the full `'w` lifetime, and the new `_inner` methods would let the results outlive the `Query`. This could be used to create aliasing mutable references, like ```rust fn bad<'w>(mut lens: QueryLens<'w, EntityMut>, entity: Entity) { let one: EntityMut<'w> = lens.query().get_inner(entity).unwrap(); let two: EntityMut<'w> = lens.query().get_inner(entity).unwrap(); assert!(one.entity() == two.entity()); } ``` Fixes #17693 ## Solution Restrict the `'world` lifetime in the `Query` returned by `QueryLens::query()` to `'_`, the lifetime of the borrow of the `QueryLens`. The model here is that `Query<'w, 's, D, F>` and `QueryLens<'w, D, F>` have permission to access their components for the lifetime `'w`. So going from `&'a mut QueryLens<'w>` to `Query<'w, 'a>` would borrow the permission only for the `'a` lifetime, but incorrectly give it out for the full `'w` lifetime. To handle any cases where users were calling `get_inner()` or `iter_inner()` on the `Query` and expecting the full `'w` lifetime, we introduce a new `QueryLens::query_inner()` method. This is only valid for `ReadOnlyQueryData`, so it may safely hand out a copy of the permission for the full `'w` lifetime. Since `get_inner()` and `iter_inner()` were only valid on `ReadOnlyQueryData` prior to #15858, that should cover any uses that relied on the longer lifetime. ## Migration Guide Users of `QueryLens::query()` who were calling `get_inner()` or `iter_inner()` will need to replace the call with `QueryLens::query_inner()`. |
||
![]() |
7d8504f30e
|
feat(ecs): implement fallible observer systems (#17731)
This commit builds on top of the work done in #16589 and #17051, by adding support for fallible observer systems. As with the previous work, the actual results of the observer system are suppressed for now, but the intention is to provide a way to handle errors in a global way. Until then, you can use a `PipeSystem` to manually handle results. --------- Signed-off-by: Jean Mertz <git@jeanmertz.com> |
||
![]() |
c34a2c2fba
|
Query::get_many should not check for duplicates (#17724)
# Objective Restore the behavior of `Query::get_many` prior to #15858. When passed duplicate `Entity`s, `get_many` is supposed to return results for all of them, since read-only queries don't alias. However, #15858 merged the implementation with `get_many_mut` and caused it to return `QueryEntityError::AliasedMutability`. ## Solution Introduce a new `Query::get_many_readonly` method that consumes the `Query` like `get_many_inner`, but that is constrained to `D: ReadOnlyQueryData` so that it can skip the aliasing check. Implement `Query::get_many` in terms of that new method. Add a test, and a comment explaining why it doesn't match the pattern of the other `&self` methods. |
||
![]() |
eee7fd5b3e
|
Encapsulate cfg(feature = "track_location") in a type. (#17602)
# Objective Eliminate the need to write `cfg(feature = "track_location")` every time one uses an API that may use location tracking. It's verbose, and a little intimidating. And it requires code outside of `bevy_ecs` that wants to use location tracking needs to either unconditionally enable the feature, or include conditional compilation of its own. It would be good for users to be able to log locations when they are available without needing to add feature flags to their own crates. Reduce the number of cases where code compiles with the `track_location` feature enabled, but not with it disabled, or vice versa. It can be hard to remember to test it both ways! Remove the need to store a `None` in `HookContext` when the `track_location` feature is disabled. ## Solution Create an `MaybeLocation<T>` type that contains a `T` if the `track_location` feature is enabled, and is a ZST if it is not. The overall API is similar to `Option`, but whether the value is `Some` or `None` is set at compile time and is the same for all values. Default `T` to `&'static Location<'static>`, since that is the most common case. Remove all `cfg(feature = "track_location")` blocks outside of the implementation of that type, and instead call methods on it. When `track_location` is disabled, `MaybeLocation` is a ZST and all methods are `#[inline]` and empty, so they should be entirely removed by the compiler. But the code will still be visible to the compiler and checked, so if it compiles with the feature disabled then it should also compile with it enabled, and vice versa. ## Open Questions Where should these types live? I put them in `change_detection` because that's where the existing `MaybeLocation` types were, but we now use these outside of change detection. While I believe that the compiler should be able to remove all of these calls, I have not actually tested anything. If we want to take this approach, what testing is required to ensure it doesn't impact performance? ## Migration Guide Methods like `Ref::changed_by()` that return a `&'static Location<'static>` will now be available even when the `track_location` feature is disabled, but they will return a new `MaybeLocation` type. `MaybeLocation` wraps a `&'static Location<'static>` when the feature is enabled, and is a ZST when the feature is disabled. Existing code that needs a `&Location` can call `into_option().unwrap()` to recover it. Many trait impls are forwarded, so if you only need `Display` then no changes will be necessary. If that code was conditionally compiled, you may instead want to use the methods on `MaybeLocation` to remove the need for conditional compilation. Code that constructs a `Ref`, `Mut`, `Res`, or `ResMut` will now need to provide location information unconditionally. If you are creating them from existing Bevy types, you can obtain a `MaybeLocation` from methods like `Table::get_changed_by_slice_for()` or `ComponentSparseSet::get_with_ticks`. Otherwise, you will need to store a `MaybeLocation` next to your data and use methods like `as_ref()` or `as_mut()` to obtain wrapped references. |
||
![]() |
ea578415e1
|
Improved Spawn APIs and Bundle Effects (#17521)
## Objective A major critique of Bevy at the moment is how boilerplatey it is to compose (and read) entity hierarchies: ```rust commands .spawn(Foo) .with_children(|p| { p.spawn(Bar).with_children(|p| { p.spawn(Baz); }); p.spawn(Bar).with_children(|p| { p.spawn(Baz); }); }); ``` There is also currently no good way to statically define and return an entity hierarchy from a function. Instead, people often do this "internally" with a Commands function that returns nothing, making it impossible to spawn the hierarchy in other cases (direct World spawns, ChildSpawner, etc). Additionally, because this style of API results in creating the hierarchy bits _after_ the initial spawn of a bundle, it causes ECS archetype changes (and often expensive table moves). Because children are initialized after the fact, we also can't count them to pre-allocate space. This means each time a child inserts itself, it has a high chance of overflowing the currently allocated capacity in the `RelationshipTarget` collection, causing literal worst-case reallocations. We can do better! ## Solution The Bundle trait has been extended to support an optional `BundleEffect`. This is applied directly to World immediately _after_ the Bundle has fully inserted. Note that this is [intentionally](https://github.com/bevyengine/bevy/discussions/16920) _not done via a deferred Command_, which would require repeatedly copying each remaining subtree of the hierarchy to a new command as we walk down the tree (_not_ good performance). This allows us to implement the new `SpawnRelated` trait for all `RelationshipTarget` impls, which looks like this in practice: ```rust world.spawn(( Foo, Children::spawn(( Spawn(( Bar, Children::spawn(Spawn(Baz)), )), Spawn(( Bar, Children::spawn(Spawn(Baz)), )), )) )) ``` `Children::spawn` returns `SpawnRelatedBundle<Children, L: SpawnableList>`, which is a `Bundle` that inserts `Children` (preallocated to the size of the `SpawnableList::size_hint()`). `Spawn<B: Bundle>(pub B)` implements `SpawnableList` with a size of 1. `SpawnableList` is also implemented for tuples of `SpawnableList` (same general pattern as the Bundle impl). There are currently three built-in `SpawnableList` implementations: ```rust world.spawn(( Foo, Children::spawn(( Spawn(Name::new("Child1")), SpawnIter(["Child2", "Child3"].into_iter().map(Name::new), SpawnWith(|parent: &mut ChildSpawner| { parent.spawn(Name::new("Child4")); parent.spawn(Name::new("Child5")); }) )), )) ``` We get the benefits of "structured init", but we have nice flexibility where it is required! Some readers' first instinct might be to try to remove the need for the `Spawn` wrapper. This is impossible in the Rust type system, as a tuple of "child Bundles to be spawned" and a "tuple of Components to be added via a single Bundle" is ambiguous in the Rust type system. There are two ways to resolve that ambiguity: 1. By adding support for variadics to the Rust type system (removing the need for nested bundles). This is out of scope for this PR :) 2. Using wrapper types to resolve the ambiguity (this is what I did in this PR). For the single-entity spawn cases, `Children::spawn_one` does also exist, which removes the need for the wrapper: ```rust world.spawn(( Foo, Children::spawn_one(Bar), )) ``` ## This works for all Relationships This API isn't just for `Children` / `ChildOf` relationships. It works for any relationship type, and they can be mixed and matched! ```rust world.spawn(( Foo, Observers::spawn(( Spawn(Observer::new(|trigger: Trigger<FuseLit>| {})), Spawn(Observer::new(|trigger: Trigger<Exploded>| {})), )), OwnerOf::spawn(Spawn(Bar)) Children::spawn(Spawn(Baz)) )) ``` ## Macros While `Spawn` is necessary to satisfy the type system, we _can_ remove the need to express it via macros. The example above can be expressed more succinctly using the new `children![X]` macro, which internally produces `Children::spawn(Spawn(X))`: ```rust world.spawn(( Foo, children![ ( Bar, children![Baz], ), ( Bar, children![Baz], ), ] )) ``` There is also a `related!` macro, which is a generic version of the `children!` macro that supports any relationship type: ```rust world.spawn(( Foo, related!(Children[ ( Bar, related!(Children[Baz]), ), ( Bar, related!(Children[Baz]), ), ]) )) ``` ## Returning Hierarchies from Functions Thanks to these changes, the following pattern is now possible: ```rust fn button(text: &str, color: Color) -> impl Bundle { ( Node { width: Val::Px(300.), height: Val::Px(100.), ..default() }, BackgroundColor(color), children![ Text::new(text), ] ) } fn ui() -> impl Bundle { ( Node { width: Val::Percent(100.0), height: Val::Percent(100.0), ..default(), }, children![ button("hello", BLUE), button("world", RED), ] ) } // spawn from a system fn system(mut commands: Commands) { commands.spawn(ui()); } // spawn directly on World world.spawn(ui()); ``` ## Additional Changes and Notes * `Bundle::from_components` has been split out into `BundleFromComponents::from_components`, enabling us to implement `Bundle` for types that cannot be "taken" from the ECS (such as the new `SpawnRelatedBundle`). * The `NoBundleEffect` trait (which implements `BundleEffect`) is implemented for empty tuples (and tuples of empty tuples), which allows us to constrain APIs to only accept bundles that do not have effects. This is critical because the current batch spawn APIs cannot efficiently apply BundleEffects in their current form (as doing so in-place could invalidate the cached raw pointers). We could consider allocating a buffer of the effects to be applied later, but that does have performance implications that could offset the balance and value of the batched APIs (and would likely require some refactors to the underlying code). I've decided to be conservative here. We can consider relaxing that requirement on those APIs later, but that should be done in a followup imo. * I've ported a few examples to illustrate real-world usage. I think in a followup we should port all examples to the `children!` form whenever possible (and for cases that require things like SpawnIter, use the raw APIs). * Some may ask "why not use the `Relationship` to spawn (ex: `ChildOf::spawn(Foo)`) instead of the `RelationshipTarget` (ex: `Children::spawn(Spawn(Foo))`)?". That _would_ allow us to remove the `Spawn` wrapper. I've explicitly chosen to disallow this pattern. `Bundle::Effect` has the ability to create _significant_ weirdness. Things in `Bundle` position look like components. For example `world.spawn((Foo, ChildOf::spawn(Bar)))` _looks and reads_ like Foo is a child of Bar. `ChildOf` is in Foo's "component position" but it is not a component on Foo. This is a huge problem. Now that `Bundle::Effect` exists, we should be _very_ principled about keeping the "weird and unintuitive behavior" to a minimum. Things that read like components _should be the components they appear to be". ## Remaining Work * The macros are currently trivially implemented using macro_rules and are currently limited to the max tuple length. They will require a proc_macro implementation to work around the tuple length limit. ## Next Steps * Port the remaining examples to use `children!` where possible and raw `Spawn` / `SpawnIter` / `SpawnWith` where the flexibility of the raw API is required. ## Migration Guide Existing spawn patterns will continue to work as expected. Manual Bundle implementations now require a `BundleEffect` associated type. Exisiting bundles would have no bundle effect, so use `()`. Additionally `Bundle::from_components` has been moved to the new `BundleFromComponents` trait. ```rust // Before unsafe impl Bundle for X { unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self { } /* remaining bundle impl here */ } // After unsafe impl Bundle for X { type Effect = (); /* remaining bundle impl here */ } unsafe impl BundleFromComponents for X { unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self { } } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Emerson Coskey <emerson@coskey.dev> |
||
![]() |
c679b861d8
|
adds example for local defaults (#17751)
# Objective Solves https://github.com/bevyengine/bevy/issues/17747. ## Solution - Adds an example for creating a default value for Local. ## Testing - Example code compiles and passes assertions. |
||
![]() |
1b7db895b7
|
Harden proc macro path resolution and add integration tests. (#17330)
This pr uses the `extern crate self as` trick to make proc macros behave the same way inside and outside bevy. # Objective - Removes noise introduced by `crate as` in the whole bevy repo. - Fixes #17004. - Hardens proc macro path resolution. ## TODO - [x] `BevyManifest` needs cleanup. - [x] Cleanup remaining `crate as`. - [x] Add proper integration tests to the ci. ## Notes - `cargo-manifest-proc-macros` is written by me and based/inspired by the old `BevyManifest` implementation and [`bkchr/proc-macro-crate`](https://github.com/bkchr/proc-macro-crate). - What do you think about the new integration test machinery I added to the `ci`? More and better integration tests can be added at a later stage. The goal of these integration tests is to simulate an actual separate crate that uses bevy. Ideally they would lightly touch all bevy crates. ## Testing - Needs RA test - Needs testing from other users - Others need to run at least `cargo run -p ci integration-test` and verify that they work. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
3c8fae2390
|
Improved Entity Mapping and Cloning (#17687)
Fixes #17535 Bevy's approach to handling "entity mapping" during spawning and cloning needs some work. The addition of [Relations](https://github.com/bevyengine/bevy/pull/17398) both [introduced a new "duplicate entities" bug when spawning scenes in the scene system](#17535) and made the weaknesses of the current mapping system exceedingly clear: 1. Entity mapping requires _a ton_ of boilerplate (implement or derive VisitEntities and VisitEntitesMut, then register / reflect MapEntities). Knowing the incantation is challenging and if you forget to do it in part or in whole, spawning subtly breaks. 2. Entity mapping a spawned component in scenes incurs unnecessary overhead: look up ReflectMapEntities, create a _brand new temporary instance_ of the component using FromReflect, map the entities in that instance, and then apply that on top of the actual component using reflection. We can do much better. Additionally, while our new [Entity cloning system](https://github.com/bevyengine/bevy/pull/16132) is already pretty great, it has some areas we can make better: * It doesn't expose semantic info about the clone (ex: ignore or "clone empty"), meaning we can't key off of that in places where it would be useful, such as scene spawning. Rather than duplicating this info across contexts, I think it makes more sense to add that info to the clone system, especially given that we'd like to use cloning code in some of our spawning scenarios. * EntityCloner is currently built in a way that prioritizes a single entity clone * EntityCloner's recursive cloning is built to be done "inside out" in a parallel context (queue commands that each have a clone of EntityCloner). By making EntityCloner the orchestrator of the clone we can remove internal arcs, improve the clarity of the code, make EntityCloner mutable again, and simplify the builder code. * EntityCloner does not currently take into account entity mapping. This is necessary to do true "bullet proof" cloning, would allow us to unify the per-component scene spawning and cloning UX, and ultimately would allow us to use EntityCloner in place of raw reflection for scenes like `Scene(World)` (which would give us a nice performance boost: fewer archetype moves, less reflection overhead). ## Solution ### Improved Entity Mapping First, components now have first-class "entity visiting and mapping" behavior: ```rust #[derive(Component, Reflect)] #[reflect(Component)] struct Inventory { size: usize, #[entities] items: Vec<Entity>, } ``` Any field with the `#[entities]` annotation will be viewable and mappable when cloning and spawning scenes. Compare that to what was required before! ```rust #[derive(Component, Reflect, VisitEntities, VisitEntitiesMut)] #[reflect(Component, MapEntities)] struct Inventory { #[visit_entities(ignore)] size: usize, items: Vec<Entity>, } ``` Additionally, for relationships `#[entities]` is implied, meaning this "just works" in scenes and cloning: ```rust #[derive(Component, Reflect)] #[relationship(relationship_target = Children)] #[reflect(Component)] struct ChildOf(pub Entity); ``` Note that Component _does not_ implement `VisitEntities` directly. Instead, it has `Component::visit_entities` and `Component::visit_entities_mut` methods. This is for a few reasons: 1. We cannot implement `VisitEntities for C: Component` because that would conflict with our impl of VisitEntities for anything that implements `IntoIterator<Item=Entity>`. Preserving that impl is more important from a UX perspective. 2. We should not implement `Component: VisitEntities` VisitEntities in the Component derive, as that would increase the burden of manual Component trait implementors. 3. Making VisitEntitiesMut directly callable for components would make it easy to invalidate invariants defined by a component author. By putting it in the `Component` impl, we can make it harder to call naturally / unavailable to autocomplete using `fn visit_entities_mut(this: &mut Self, ...)`. `ReflectComponent::apply_or_insert` is now `ReflectComponent::apply_or_insert_mapped`. By moving mapping inside this impl, we remove the need to go through the reflection system to do entity mapping, meaning we no longer need to create a clone of the target component, map the entities in that component, and patch those values on top. This will make spawning mapped entities _much_ faster (The default `Component::visit_entities_mut` impl is an inlined empty function, so it will incur no overhead for unmapped entities). ### The Bug Fix To solve #17535, spawning code now skips entities with the new `ComponentCloneBehavior::Ignore` and `ComponentCloneBehavior::RelationshipTarget` variants (note RelationshipTarget is a temporary "workaround" variant that allows scenes to skip these components. This is a temporary workaround that can be removed as these cases should _really_ be using EntityCloner logic, which should be done in a followup PR. When that is done, `ComponentCloneBehavior::RelationshipTarget` can be merged into the normal `ComponentCloneBehavior::Custom`). ### Improved Cloning * `Option<ComponentCloneHandler>` has been replaced by `ComponentCloneBehavior`, which encodes additional intent and context (ex: `Default`, `Ignore`, `Custom`, `RelationshipTarget` (this last one is temporary)). * Global per-world entity cloning configuration has been removed. This felt overly complicated, increased our API surface, and felt too generic. Each clone context can have different requirements (ex: what a user wants in a specific system, what a scene spawner wants, etc). I'd prefer to see how far context-specific EntityCloners get us first. * EntityCloner's internals have been reworked to remove Arcs and make it mutable. * EntityCloner is now directly stored on EntityClonerBuilder, simplifying the code somewhat * EntityCloner's "bundle scratch" pattern has been moved into the new BundleScratch type, improving its usability and making it usable in other contexts (such as future cross-world cloning code). Currently this is still private, but with some higher level safe APIs it could be used externally for making dynamic bundles * EntityCloner's recursive cloning behavior has been "externalized". It is now responsible for orchestrating recursive clones, meaning it no longer needs to be sharable/clone-able across threads / read-only. * EntityCloner now does entity mapping during clones, like scenes do. This gives behavior parity and also makes it more generically useful. * `RelatonshipTarget::RECURSIVE_SPAWN` is now `RelationshipTarget::LINKED_SPAWN`, and this field is used when cloning relationship targets to determine if cloning should happen recursively. The new `LINKED_SPAWN` term was picked to make it more generically applicable across spawning and cloning scenarios. ## Next Steps * I think we should adapt EntityCloner to support cross world cloning. I think this PR helps set the stage for that by making the internals slightly more generalized. We could have a CrossWorldEntityCloner that reuses a lot of this infrastructure. * Once we support cross world cloning, we should use EntityCloner to spawn `Scene(World)` scenes. This would yield significant performance benefits (no archetype moves, less reflection overhead). --------- Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
6f39e44c48
|
Introduce methods on QueryState to obtain a Query (#15858)
# Objective Simplify and expand the API for `QueryState`. `QueryState` has a lot of methods that mirror those on `Query`. These are then multiplied by variants that take `&World`, `&mut World`, and `UnsafeWorldCell`. In addition, many of them have `_manual` variants that take `&QueryState` and avoid calling `update_archetypes()`. Not all of the combinations exist, however, so some operations are not possible. ## Solution Introduce methods to get a `Query` from a `QueryState`. That will reduce duplication between the types, and ensure that the full `Query` API is always available for `QueryState`. Introduce methods on `Query` that consume the query to return types with the full `'w` lifetime. This avoids issues with borrowing where things like `query_state.query(&world).get(entity)` don't work because they borrow from the temporary `Query`. Finally, implement `Copy` for read-only `Query`s. `get_inner` and `iter_inner` currently take `&self`, so changing them to consume `self` would be a breaking change. By making `Query: Copy`, they can consume a copy of `self` and continue to work. The consuming methods also let us simplify the implementation of methods on `Query`, by doing `fn foo(&self) { self.as_readonly().foo_inner() }` and `fn foo_mut(&mut self) { self.reborrow().foo_inner() }`. That structure makes it more difficult to accidentally extend lifetimes, since the safe `as_readonly()` and `reborrow()` methods shrink them appropriately. The optimizer is able to see that they are both identity functions and inline them, so there should be no performance cost. Note that this change would conflict with #15848. If `QueryState` is stored as a `Cow`, then the consuming methods cannot be implemented, and `Copy` cannot be implemented. ## Future Work The next step is to mark the methods on `QueryState` as `#[deprecated]`, and move the implementations into `Query`. ## Migration Guide `Query::to_readonly` has been renamed to `Query::as_readonly`. |
||
![]() |
0ca9d6968a
|
Improve docs for WorldQuery (#17654)
# Objective While working on #17649, I found the docs for `WorldQuery` and the related traits frustratingly vague. ## Solution Clarify them and add some more tangible advice. Also fix a copy-pasted typo in related comments. --------- Co-authored-by: James O'Brien <james.obrien@drafly.net> |