cdcb773e9b
160 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
cdcb773e9b
|
Add EntityWorldMut::reborrow_scope() (#18730)
# Objective Allow `EntityCommand` implementors to delegate to other entity commands easily: ```rs impl EntityCommand for Foo { fn apply(self, mut entity: EntityWorldMut) { entity.reborrow_scope(|e| StepOne.apply(e)); entity.reborrow_scope(|e| StepTwo.apply(e)); } } ``` |
||
![]() |
f6543502b4
|
Add BundleRemover (#18521)
# Objective It has long been a todo item in the ecs to create a `BundleRemover` alongside the inserter, spawner, etc. This is an uncontroversial first step of #18514. ## Solution Move existing code from complex helper functions to one generalized `BundleRemover`. ## Testing Existing tests. |
||
![]() |
bea0a0a9bc
|
Let FilteredEntity(Ref|Mut) receive access when nested. (#18236)
# Objective Let `FilteredEntityRef` and `FilteredEntityMut` receive access when nested inside tuples or `#[derive(QueryData)]` types. Make sure to exclude any access that would conflict with other subqueries! Fixes #14349 ## Solution Replace `WorldQuery::set_access(state, access)` with a new method, `QueryData::provide_extra_access(state, access, available_access)`, that passes both the total available access and the currently used access. This is called after `WorldQuery::update_component_access()`, so any access used by ordinary subqueries will be known. `FilteredEntityRef` and `FilteredEntityMut` can use the combination to determine how much access they can safely take, while tuples can safely pass those parameters directly to their subqueries. This requires a new `Access::remove_conflicting_access()` method that can be used to remove any access that would conflict with existing access. Implementing this method was easier by first factoring some common set manipulation code out of `Access::extend`. I can extract that refactoring to a separate PR if desired. Have `FilteredEntity(Ref|Mut)` store `Access` instead of `FilteredAccess` because they do not need to keep track of the filter. This was necessary in an early draft but no longer is. I left it in because it's small and I'm touching that code anyway, but I can extract it to a separate PR if desired. |
||
![]() |
5f936aefc8
|
Prevent exclusive systems from being used as observers (#19033)
# Objective Prevent using exclusive systems as observers. Allowing them is unsound, because observers are only expected to have `DeferredWorld` access, and the observer infrastructure will keep pointers that are invalidated by the creation of `&mut World`. See https://github.com/bevyengine/bevy/actions/runs/14778342801/job/41491517847?pr=19011 for a MIRI failure in a recent PR caused by an exclusive system being used as an observer in a test. ## Solution Have `Observer::new` panic if `System::is_exclusive()` is true. Document that method, and methods that call it, as panicking. (It should be possible to express this in the type system so that the calls won't even compile, but I did not want to attempt that.) ## Testing Added a unit test that calls `World::add_observer` with an exclusive system. |
||
![]() |
e9a0ef49f9
|
Rename bevy_platform_support to bevy_platform (#18813)
# Objective The goal of `bevy_platform_support` is to provide a set of platform agnostic APIs, alongside platform-specific functionality. This is a high traffic crate (providing things like HashMap and Instant). Especially in light of https://github.com/bevyengine/bevy/discussions/18799, it deserves a friendlier / shorter name. Given that it hasn't had a full release yet, getting this change in before Bevy 0.16 makes sense. ## Solution - Rename `bevy_platform_support` to `bevy_platform`. |
||
![]() |
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`. |
||
![]() |
538afe2330
|
Improved Require Syntax (#18555)
# Objective Requires are currently more verbose than they need to be. People would like to define inline component values. Additionally, the current `#[require(Foo(custom_constructor))]` and `#[require(Foo(|| Foo(10))]` syntax doesn't really make sense within the context of the Rust type system. #18309 was an attempt to improve ergonomics for some cases, but it came at the cost of even more weirdness / unintuitive behavior. Our approach as a whole needs a rethink. ## Solution Rework the `#[require()]` syntax to make more sense. This is a breaking change, but I think it will make the system easier to learn, while also improving ergonomics substantially: ```rust #[derive(Component)] #[require( A, // this will use A::default() B(1), // inline tuple-struct value C { value: 1 }, // inline named-struct value D::Variant, // inline enum variant E::SOME_CONST, // inline associated const F::new(1), // inline constructor G = returns_g(), // an expression that returns G H = SomethingElse::new(), // expression returns SomethingElse, where SomethingElse: Into<H> )] struct Foo; ``` ## Migration Guide Custom-constructor requires should use the new expression-style syntax: ```rust // before #[derive(Component)] #[require(A(returns_a))] struct Foo; // after #[derive(Component)] #[require(A = returns_a())] struct Foo; ``` Inline-closure-constructor requires should use the inline value syntax where possible: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A(10)] struct Foo; ``` In cases where that is not possible, use the expression-style syntax: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A = A(10)] struct Foo; ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
921ff6701f
|
Add methods to work with dynamic immutable components (#18532)
# Objective - Fixes #16861 ## Solution - Added: - `UnsafeEntityCell::get_mut_assume_mutable_by_id` - `EntityMut::get_mut_assume_mutable_by_id` - `EntityMut::get_mut_assume_mutable_by_id_unchecked` - `EntityWorldMut::into_mut_assume_mutable_by_id` - `EntityWorldMut::into_mut_assume_mutable` - `EntityWorldMut::get_mut_assume_mutable_by_id` - `EntityWorldMut::into_mut_assume_mutable_by_id` - `EntityWorldMut::modify_component_by_id` - `World::modify_component_by_id` - `DeferredWorld::modify_component_by_id` - Added `fetch_mut_assume_mutable` to `DynamicComponentFetch` trait (this is a breaking change) ## Testing - CI --- ## Migration Guide If you had previously implemented `DynamicComponentFetch` you must now include a definition for `fetch_mut_assume_mutable`. In general this will be identical to `fetch_mut` using the relevant alternatives for actually getting a component. --- ## Notes All of the added methods are minor variations on existing functions and should therefore be of low risk for inclusion during the RC process. |
||
![]() |
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. |
||
![]() |
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> |
||
![]() |
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> |
||
![]() |
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. |
||
![]() |
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. |
||
![]() |
bb09751cf0
|
Fix observer/hook OnReplace and OnRemove triggering when removing a bundle even when the component is not present on the entity (#17942)
# Objective - Fixes #17897. ## Solution - When removing components, we filter the list of components in the removed bundle based on whether they are actually in the archetype. ## Testing - Added a test. |
||
![]() |
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> |
||
![]() |
267a0d003c
|
Add ComponentId-taking functions to Entity{Ref,Mut}Except to mirror FilteredEntity{Ref,Mut} (#17800)
# Objective Related to #17784. The ticket is actually about just getting rid of `Entity{Ref,Mut}Except` in favor of `FilteredEntity{Ref,Mut}`, but I got told the unification of Entity types is a bigger endeavor that has been going on for a while now (as the "Pointing Fingers" working group) and I should just add the functions I actually need in the meantime. ## Solution This PR adds all of the functions necessary to access components by TypeId or ComponentId instead of static types. ## Testing > Did you test these changes? If so, how? Haven't tested it yet, but the changes are mostly copy/paste from other implementations in the same file, since there is a lot of duplicated functionality there. ## Not a Migration Guide There shouldn't be any breaking changes, it's just a few new functions on existing types. I had to shuffle around the lifetimes in `From<&EntityMutExcept<'a, B>> for EntityRefExcept<'a, B>` (originally it was `From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'_, B>`) to make the borrow checker happy, but I don't think that this should have an impact on user code (correct me if I'm wrong). |
||
![]() |
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> |
||
![]() |
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> |
||
![]() |
1b2cf7d6cd
|
Isolate component registration (#17671)
# Objective Progresses #17569. The end goal here is to synchronize component registration. See the other PR for details for the motivation behind that. For this PR specifically, the objective is to decouple `Components` from `Storages`. What components are registered etc should have nothing to do with what Storages looks like. Storages should only care about what entity archetypes have been spawned. ## Solution Previously, this was used to create sparse sets for relevant components when those components were registered. Now, we do that when the component is inserted/spawned. This PR proposes doing that in `BundleInfo::new`, but there may be a better place. ## Testing In theory, this shouldn't have changed any functionality, so no new tests were created. I'm not aware of any examples that make heavy use of sparse set components either. ## Migration Guide - Remove storages from functions where it is no longer needed. - Note that SparseSets are no longer present for all registered sparse set components, only those that have been spawned. --------- Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
7d68ac029e
|
Use the provided caller instead of Location::caller() in despawn_with_caller() (#17598)
# Objective Pass the correct location to triggers when despawning entities. `EntityWorldMut::despawn_with_caller()` currently passes `Location::caller()` to some triggers instead of the `caller` parameter it was passed. As `despawn_with_caller()` is not `#[track_caller]`, this means the location will always be reported as `despawn_with_caller()` itself. ## Solution Pass `caller` instead of `Location::caller()`. |
||
![]() |
9bc0ae33c3
|
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective - Contributes to #16877 ## Solution - Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils` and into `bevy_platform_support` - Refactored the above to match the layout of these types in `std`. - Updated crates as required. ## Testing - CI --- ## Migration Guide - The following items were moved out of `bevy_utils` and into `bevy_platform_support::hash`: - `FixedState` - `DefaultHasher` - `RandomState` - `FixedHasher` - `Hashed` - `PassHash` - `PassHasher` - `NoOpHash` - The following items were moved out of `bevy_utils` and into `bevy_platform_support::collections`: - `HashMap` - `HashSet` - `bevy_utils::hashbrown` has been removed. Instead, import from `bevy_platform_support::collections` _or_ take a dependency on `hashbrown` directly. - `bevy_utils::Entry` has been removed. Instead, import from `bevy_platform_support::collections::hash_map` or `bevy_platform_support::collections::hash_set` as appropriate. - All of the above equally apply to `bevy::utils` and `bevy::platform_support`. ## Notes - I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils` as they might be candidates for micro-crating. They can always be moved into `bevy_platform_support` at a later date if desired. |
||
![]() |
41e79ae826
|
Refactored ComponentHook Parameters into HookContext (#17503)
# Objective - Make the function signature for `ComponentHook` less verbose ## Solution - Refactored `Entity`, `ComponentId`, and `Option<&Location>` into a new `HookContext` struct. ## Testing - CI --- ## Migration Guide Update the function signatures for your component hooks to only take 2 arguments, `world` and `context`. Note that because `HookContext` is plain data with all members public, you can use de-structuring to simplify migration. ```rust // Before fn my_hook( mut world: DeferredWorld, entity: Entity, component_id: ComponentId, ) { ... } // After fn my_hook( mut world: DeferredWorld, HookContext { entity, component_id, caller }: HookContext, ) { ... } ``` Likewise, if you were discarding certain parameters, you can use `..` in the de-structuring: ```rust // Before fn my_hook( mut world: DeferredWorld, entity: Entity, _: ComponentId, ) { ... } // After fn my_hook( mut world: DeferredWorld, HookContext { entity, .. }: HookContext, ) { ... } ``` |
||
![]() |
f32a6fb205
|
Track callsite for observers & hooks (#15607)
# Objective Fixes #14708 Also fixes some commands not updating tracked location. ## Solution `ObserverTrigger` has a new `caller` field with the `track_change_detection` feature; hooks take an additional caller parameter (which is `Some(…)` or `None` depending on the feature). ## Testing See the new tests in `src/observer/mod.rs` --- ## Showcase Observers now know from where they were triggered (if `track_change_detection` is enabled): ```rust world.observe(move |trigger: Trigger<OnAdd, Foo>| { println!("Added Foo from {}", trigger.caller()); }); ``` ## Migration - hooks now take an additional `Option<&'static Location>` argument --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
44ad3bf62b
|
Move Resource trait to its own file (#17469)
# Objective `bevy_ecs`'s `system` module is something of a grab bag, and *very* large. This is particularly true for the `system_param` module, which is more than 2k lines long! While it could be defensible to put `Res` and `ResMut` there (lol no they're in change_detection.rs, obviously), it doesn't make any sense to put the `Resource` trait there. This is confusing to navigate (and painful to work on and review). ## Solution - Create a root level `bevy_ecs/resource.rs` module to mirror `bevy_ecs/component.rs` - move the `Resource` trait to that module - move the `Resource` derive macro to that module as well (Rust really likes when you pun on the names of the derive macro and trait and put them in the same path) - fix all of the imports ## Notes to reviewers - We could probably move more stuff into here, but I wanted to keep this PR as small as possible given the absurd level of import changes. - This PR is ground work for my upcoming attempts to store resource data on components (resources-as-entities). Splitting this code out will make the work and review a bit easier, and is the sort of overdue refactor that's good to do as part of more meaningful work. ## Testing cargo build works! ## Migration Guide `bevy_ecs::system::Resource` has been moved to `bevy_ecs::resource::Resource`. |
||
![]() |
21f1e3045c
|
Relationships (non-fragmenting, one-to-many) (#17398)
This adds support for one-to-many non-fragmenting relationships (with planned paths for fragmenting and non-fragmenting many-to-many relationships). "Non-fragmenting" means that entities with the same relationship type, but different relationship targets, are not forced into separate tables (which would cause "table fragmentation"). Functionally, this fills a similar niche as the current Parent/Children system. The biggest differences are: 1. Relationships have simpler internals and significantly improved performance and UX. Commands and specialized APIs are no longer necessary to keep everything in sync. Just spawn entities with the relationship components you want and everything "just works". 2. Relationships are generalized. Bevy can provide additional built in relationships, and users can define their own. **REQUEST TO REVIEWERS**: _please don't leave top level comments and instead comment on specific lines of code. That way we can take advantage of threaded discussions. Also dont leave comments simply pointing out CI failures as I can read those just fine._ ## Built on top of what we have Relationships are implemented on top of the Bevy ECS features we already have: components, immutability, and hooks. This makes them immediately compatible with all of our existing (and future) APIs for querying, spawning, removing, scenes, reflection, etc. The fewer specialized APIs we need to build, maintain, and teach, the better. ## Why focus on one-to-many non-fragmenting first? 1. This allows us to improve Parent/Children relationships immediately, in a way that is reasonably uncontroversial. Switching our hierarchy to fragmenting relationships would have significant performance implications. ~~Flecs is heavily considering a switch to non-fragmenting relations after careful considerations of the performance tradeoffs.~~ _(Correction from @SanderMertens: Flecs is implementing non-fragmenting storage specialized for asset hierarchies, where asset hierarchies are many instances of small trees that have a well defined structure)_ 2. Adding generalized one-to-many relationships is currently a priority for the [Next Generation Scene / UI effort](https://github.com/bevyengine/bevy/discussions/14437). Specifically, we're interested in building reactions and observers on top. ## The changes This PR does the following: 1. Adds a generic one-to-many Relationship system 3. Ports the existing Parent/Children system to Relationships, which now lives in `bevy_ecs::hierarchy`. The old `bevy_hierarchy` crate has been removed. 4. Adds on_despawn component hooks 5. Relationships can opt-in to "despawn descendants" behavior, meaning that the entire relationship hierarchy is despawned when `entity.despawn()` is called. The built in Parent/Children hierarchies enable this behavior, and `entity.despawn_recursive()` has been removed. 6. `world.spawn` now applies commands after spawning. This ensures that relationship bookkeeping happens immediately and removes the need to manually flush. This is in line with the equivalent behaviors recently added to the other APIs (ex: insert). 7. Removes the ValidParentCheckPlugin (system-driven / poll based) in favor of a `validate_parent_has_component` hook. ## Using Relationships The `Relationship` trait looks like this: ```rust pub trait Relationship: Component + Sized { type RelationshipSources: RelationshipSources<Relationship = Self>; fn get(&self) -> Entity; fn from(entity: Entity) -> Self; } ``` A relationship is a component that: 1. Is a simple wrapper over a "target" Entity. 2. Has a corresponding `RelationshipSources` component, which is a simple wrapper over a collection of entities. Every "target entity" targeted by a "source entity" with a `Relationship` has a `RelationshipSources` component, which contains every "source entity" that targets it. For example, the `Parent` component (as it currently exists in Bevy) is the `Relationship` component and the entity containing the Parent is the "source entity". The entity _inside_ the `Parent(Entity)` component is the "target entity". And that target entity has a `Children` component (which implements `RelationshipSources`). In practice, the Parent/Children relationship looks like this: ```rust #[derive(Relationship)] #[relationship(relationship_sources = Children)] pub struct Parent(pub Entity); #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent)] pub struct Children(Vec<Entity>); ``` The Relationship and RelationshipSources derives automatically implement Component with the relevant configuration (namely, the hooks necessary to keep everything in sync). The most direct way to add relationships is to spawn entities with relationship components: ```rust let a = world.spawn_empty().id(); let b = world.spawn(Parent(a)).id(); assert_eq!(world.entity(a).get::<Children>().unwrap(), &[b]); ``` There are also convenience APIs for spawning more than one entity with the same relationship: ```rust world.spawn_empty().with_related::<Children>(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` The existing `with_children` API is now a simpler wrapper over `with_related`. This makes this change largely non-breaking for existing spawn patterns. ```rust world.spawn_empty().with_children(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` There are also other relationship APIs, such as `add_related` and `despawn_related`. ## Automatic recursive despawn via the new on_despawn hook `RelationshipSources` can opt-in to "despawn descendants" behavior, which will despawn all related entities in the relationship hierarchy: ```rust #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent, despawn_descendants)] pub struct Children(Vec<Entity>); ``` This means that `entity.despawn_recursive()` is no longer required. Instead, just use `entity.despawn()` and the relevant related entities will also be despawned. To despawn an entity _without_ despawning its parent/child descendants, you should remove the `Children` component first, which will also remove the related `Parent` components: ```rust entity .remove::<Children>() .despawn() ``` This builds on the on_despawn hook introduced in this PR, which is fired when an entity is despawned (before other hooks). ## Relationships are the source of truth `Relationship` is the _single_ source of truth component. `RelationshipSources` is merely a reflection of what all the `Relationship` components say. By embracing this, we are able to significantly improve the performance of the system as a whole. We can rely on component lifecycles to protect us against duplicates, rather than needing to scan at runtime to ensure entities don't already exist (which results in quadratic runtime). A single source of truth gives us constant-time inserts. This does mean that we cannot directly spawn populated `Children` components (or directly add or remove entities from those components). I personally think this is a worthwhile tradeoff, both because it makes the performance much better _and_ because it means theres exactly one way to do things (which is a philosophy we try to employ for Bevy APIs). As an aside: treating both sides of the relationship as "equivalent source of truth relations" does enable building simple and flexible many-to-many relationships. But this introduces an _inherent_ need to scan (or hash) to protect against duplicates. [`evergreen_relations`](https://github.com/EvergreenNest/evergreen_relations) has a very nice implementation of the "symmetrical many-to-many" approach. Unfortunately I think the performance issues inherent to that approach make it a poor choice for Bevy's default relationship system. ## Followup Work * Discuss renaming `Parent` to `ChildOf`. I refrained from doing that in this PR to keep the diff reasonable, but I'm personally biased toward this change (and using that naming pattern generally for relationships). * [Improved spawning ergonomics](https://github.com/bevyengine/bevy/discussions/16920) * Consider adding relationship observers/triggers for "relationship targets" whenever a source is added or removed. This would replace the current "hierarchy events" system, which is unused upstream but may have existing users downstream. I think triggers are the better fit for this than a buffered event queue, and would prefer not to add that back. * Fragmenting relations: My current idea hinges on the introduction of "value components" (aka: components whose type _and_ value determines their ComponentId, via something like Hashing / PartialEq). By labeling a Relationship component such as `ChildOf(Entity)` as a "value component", `ChildOf(e1)` and `ChildOf(e2)` would be considered "different components". This makes the transition between fragmenting and non-fragmenting a single flag, and everything else continues to work as expected. * Many-to-many support * Non-fragmenting: We can expand Relationship to be a list of entities instead of a single entity. I have largely already written the code for this. * Fragmenting: With the "value component" impl mentioned above, we get many-to-many support "for free", as it would allow inserting multiple copies of a Relationship component with different target entities. Fixes #3742 (If this PR is merged, I think we should open more targeted followup issues for the work above, with a fresh tracking issue free of the large amount of less-directed historical context) Fixes #17301 Fixes #12235 Fixes #15299 Fixes #15308 ## Migration Guide * Replace `ChildBuilder` with `ChildSpawnerCommands`. * Replace calls to `.set_parent(parent_id)` with `.insert(Parent(parent_id))`. * Replace calls to `.replace_children()` with `.remove::<Children>()` followed by `.add_children()`. Note that you'll need to manually despawn any children that are not carried over. * Replace calls to `.despawn_recursive()` with `.despawn()`. * Replace calls to `.despawn_descendants()` with `.despawn_related::<Children>()`. * If you have any calls to `.despawn()` which depend on the children being preserved, you'll need to remove the `Children` component first. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
17c46f4add
|
bevy_ecs: Apply #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] (#17335)
# Objective - https://github.com/bevyengine/bevy/issues/17111 ## Solution Set the `clippy::allow_attributes` and `clippy::allow_attributes_without_reason` lints to `warn`, and bring `bevy_ecs` in line with the new restrictions. ## Testing This PR is a WIP; testing will happen after it's finished. |
||
![]() |
f5d38f30cc
|
Fix entity does not exist message on index reuse (#17264)
# Objective With the `track_location` feature, the error message of trying to acquire an entity that was despawned pointed to the wrong line if the entity index has been reused. ## Showcase ```rust use bevy_ecs::prelude::*; fn main() { let mut world = World::new(); let e = world.spawn_empty().id(); world.despawn(e); world.flush(); let _ = world.spawn_empty(); world.entity(e); } ``` Old message: ``` Entity 0v1 was despawned by src/main.rs:8:19 ``` New message: ``` Entity 0v1 does not exist (its index has been reused) ``` |
||
![]() |
4bca7f1b6d
|
Improved Command Errors (#17215)
# Objective Rework / build on #17043 to simplify the implementation. #17043 should be merged first, and the diff from this PR will get much nicer after it is merged (this PR is net negative LOC). ## Solution 1. Command and EntityCommand have been vastly simplified. No more marker components. Just one function. 2. Command and EntityCommand are now generic on the return type. This enables result-less commands to exist, and allows us to statically distinguish between fallible and infallible commands, which allows us to skip the "error handling overhead" for cases that don't need it. 3. There are now only two command queue variants: `queue` and `queue_fallible`. `queue` accepts commands with no return type. `queue_fallible` accepts commands that return a Result (specifically, one that returns an error that can convert to `bevy_ecs::result::Error`). 4. I've added the concept of the "default error handler", which is used by `queue_fallible`. This is a simple direct call to the `panic()` error handler by default. Users that want to override this can enable the `configurable_error_handler` cargo feature, then initialize the GLOBAL_ERROR_HANDLER value on startup. This is behind a flag because there might be minor overhead with `OnceLock` and I'm guessing this will be a niche feature. We can also do perf testing with OnceLock if someone really wants it to be used unconditionally, but I don't personally feel the need to do that. 5. I removed the "temporary error handler" on Commands (and all code associated with it). It added more branching, made Commands bigger / more expensive to initialize (note that we construct it at high frequencies / treat it like a pointer type), made the code harder to follow, and introduced a bunch of additional functions. We instead rely on the new default error handler used in `queue_fallible` for most things. In the event that a custom handler is required, `handle_error_with` can be used. 6. EntityCommand now _only_ supports functions that take `EntityWorldMut` (and all existing entity commands have been ported). Removing the marker component from EntityCommand hinged on this change, but I strongly believe this is for the best anyway, as this sets the stage for more efficient batched entity commands. 7. I added `EntityWorldMut::resource` and the other variants for more ergonomic resource access on `EntityWorldMut` (removes the need for entity.world_scope, which also incurs entity-lookup overhead). ## Open Questions 1. I believe we could merge `queue` and `queue_fallible` into a single `queue` which accepts both fallible and infallible commands (via the introduction of a `QueueCommand` trait). Is this desirable? |
||
![]() |
3742e621ef
|
Allow clippy::too_many_arguments to lint without warnings (#17249)
# Objective Many instances of `clippy::too_many_arguments` linting happen to be on systems - functions which we don't call manually, and thus there's not much reason to worry about the argument count. ## Solution Allow `clippy::too_many_arguments` globally, and remove all lint attributes related to it. |
||
![]() |
f64f3ac997
|
Cleanup entity reference types (#17149)
# Objective Cleanup `EntityRef`, `EntityMut`, and `EntityWorldMut` in preparation for my "Scoped Entity References" PR. ## Solution - Switched `EntityRef`/`EntityMut` from tuple structs to normal ones. - Ensured all conversion trait impls use the same `entity` argument name. - Replaced some `unsafe` with delegated calls from `EntityMut` to `EntityRef` - Added `EntityMut::into_readonly` to make the replacements clearer - Replaced some `unsafe` with delegated calls from `EntityWorldMut` to `EntityMut` and `EntityRef` - Added `EntityWorldMut::into_readonly`, `::as_readonly`, `::into_mutable`, `::as_mutable` to make the replacements clearer ## Testing Reusing current tests. |
||
![]() |
0403948aa2
|
Remove Implicit std Prelude from no_std Crates (#17086)
# Background In `no_std` compatible crates, there is often an `std` feature which will allow access to the standard library. Currently, with the `std` feature _enabled_, the [`std::prelude`](https://doc.rust-lang.org/std/prelude/index.html) is implicitly imported in all modules. With the feature _disabled_, instead the [`core::prelude`](https://doc.rust-lang.org/core/prelude/index.html) is implicitly imported. This creates a subtle and pervasive issue where `alloc` items _may_ be implicitly included (if `std` is enabled), or must be explicitly included (if `std` is not enabled). # Objective - Make the implicit imports for `no_std` crates consistent regardless of what features are/not enabled. ## Solution - Replace the `cfg_attr` "double negative" `no_std` attribute with conditional compilation to _include_ `std` as an external crate. ```rust // Before #![cfg_attr(not(feature = "std"), no_std)] // After #![no_std] #[cfg(feature = "std")] extern crate std; ``` - Fix imports that are currently broken but are only now visible with the above fix. ## Testing - CI ## Notes I had previously used the "double negative" version of `no_std` based on general consensus that it was "cleaner" within the Rust embedded community. However, this implicit prelude issue likely was considered when forming this consensus. I believe the reason why is the items most affected by this issue are provided by the `alloc` crate, which is rarely used within embedded but extensively used within Bevy. |
||
![]() |
294e0db719
|
Rename track_change_detection flag to track_location (#17075)
# Objective - As stated in the related issue, this PR is to better align the feature flag name with what it actually does and the plans for the future. - Fixes #16852 ## Solution - Simple find / replace ## Testing - Local run of `cargo run -p ci` ## Migration Guide The `track_change_detection` feature flag has been renamed to `track_location` to better reflect its extended capabilities. |
||
![]() |
3f19997096
|
Added modify_component to EntityWorldMut , DeferredWorld , and World (#16668)
# Objective - Make working with immutable components more ergonomic - Assist #16662 ## Solution Added `modify_component` to `World` and `EntityWorldMut`. This method "removes" a component from an entity, gives a mutable reference to it to a provided closure, and then "re-inserts" the component back onto the entity. This replacement triggers the `OnReplace` and `OnInsert` hooks, but does _not_ cause an archetype move, as the removal is purely simulated. ## Testing - Added doc-tests and a unit test. --- ## Showcase ```rust use bevy_ecs::prelude::*; /// An immutable component. #[derive(Component, PartialEq, Eq, Debug)] #[component(immutable)] struct Foo(bool); let mut world = World::default(); let mut entity = world.spawn(Foo(false)); assert_eq!(entity.get::<Foo>(), Some(&Foo(false))); // Before the closure is executed, the `OnReplace` hooks/observers are triggered entity.modify_component(|foo: &mut Foo| { foo.0 = true; }); // After the closure is executed, `OnInsert` hooks/observers are triggered assert_eq!(entity.get::<Foo>(), Some(&Foo(true))); ``` ## Notes - If the component is not available on the entity, the closure and hooks aren't executed, and `None` is returned. I chose this as an alternative to returning an error or panicking, but I'm open to changing that based on feedback. - This relies on `unsafe`, in particular for accessing the `Archetype` to trigger hooks. All the unsafe operations are contained within `DeferredWorld::modify_component`, and I would appreciate that this function is given special attention to ensure soundness. - The `OnAdd` hook can never be triggered by this method, since the component must already be inserted. I have chosen to not trigger `OnRemove`, as I believe it makes sense that this method is purely a replacement operation, not an actual removal/insertion. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Malek <50841145+MalekiRe@users.noreply.github.com> |
||
![]() |
b09bbfa905
|
Remove unsound Clone impl for EntityMutExcept (#17032)
# Objective `EntityMutExcept` can currently be cloned, which can easily violate aliasing rules. ## Solution - Remove the `Clone` impl for `EntityMutExcept` - Also manually derived `Clone` impl for `EntityRefExcept` so that `B: Clone` isn't required, and also impl'd `Copy` ## Testing Compile failure tests would be good for this, but I'm not exactly sure how to set that up. ## Migration Guide - `EntityMutExcept` can no-longer be cloned, as this violates Rust's memory safety rules. |
||
![]() |
0f2b2de333
|
Move some structs that impl Command to methods on World and EntityWorldMut (#16999)
## Objective Commands were previously limited to structs that implemented `Command`. Now there are blanket implementations for closures, which (in my opinion) are generally preferable. Internal commands within `commands/mod.rs` have been switched from structs to closures, but there are a number of internal commands in other areas of the engine that still use structs. I'd like to tidy these up by moving their implementations to methods on `World`/`EntityWorldMut` and changing `Commands` to use those methods through closures. This PR handles the following: - `TriggerEvent` and `EmitDynamicTrigger` double as commands and helper structs, and can just be moved to `World` methods. - Four structs that enabled insertion/removal of components via reflection. This functionality shouldn't be exclusive to commands, and can be added to `EntityWorldMut`. - Five structs that mostly just wrapped `World` methods, and can be replaced with closures that do the same thing. ## Solution - __Observer Triggers__ (`observer/trigger_event.rs` and `observer/mod.rs`) - Moved the internals of `TriggerEvent` to the `World` methods that used it. - Replaced `EmitDynamicTrigger` with two `World` methods: - `trigger_targets_dynamic` - `trigger_targets_dynamic_ref` - `TriggerTargets` was now the only thing in `observer/trigger_event.rs`, so it's been moved to `observer/mod.rs` and `trigger_event.rs` was deleted. - __Reflection Insert/Remove__ (`reflect/entity_commands.rs`) - Replaced the following `Command` impls with equivalent methods on `EntityWorldMut`: - `InsertReflect` -> `insert_reflect` - `InsertReflectWithRegistry` -> `insert_reflect_with_registry` - `RemoveReflect` -> `remove_reflect` - `RemoveReflectWithRegistry` -> `remove_reflect_with_registry` - __System Registration__ (`system/system_registry.rs`) - The following `Command` impls just wrapped a `World` method and have been replaced with closures: - `RunSystemWith` - `UnregisterSystem` - `RunSystemCachedWith` - `UnregisterSystemCached` - `RegisterSystem` called a helper function that basically worked as a constructor for `RegisteredSystem` and made sure it came with a marker component. That helper function has been replaced with `RegisteredSystem::new` and a `#[require]`. ## Possible Addition The extension trait that adds the reflection commands, `ReflectCommandExt`, isn't strictly necessary; we could just `impl EntityCommands`. We could even move them to the same files as the main impls and put it behind a `#[cfg]`. The PR that added it [had a similar conversation](https://github.com/bevyengine/bevy/pull/8895#discussion_r1234713671) and decided to stick with the trait, but we could revisit it here if so desired. |
||
![]() |
5b899dcc3a
|
impl EntityBorrow for more types (#16917)
# Objective Some types like `RenderEntity` and `MainEntity` are just wrappers around `Entity`, so they should be able to implement `EntityBorrow`/`TrustedEntityBorrow`. This allows using them with `EntitySet` functionality. The `EntityRef` family are more than direct wrappers around `Entity`, but can still benefit from being unique in a collection. ## Solution Implement `EntityBorrow` and `TrustedEntityBorrow` for simple `Entity` newtypes and `EntityRef` types. These impls are an explicit decision to have the `EntityRef` types compare like just `Entity`. `EntityWorldMut` is omitted from this impl, because it explicitly contains a `&mut World` as well, and we do not ever use more than one at a time. Add `EntityBorrow` to the `bevy_ecs` prelude. ## Migration Guide `NormalizedWindowRef::entity` has been replaced with an `EntityBorrow::entity` impl. |
||
![]() |
1f2d0e6308
|
Add no_std support to bevy_ecs (#16758)
# Objective - Contributes to #15460 ## Solution - Added the following features: - `std` (default) - `async_executor` (default) - `edge_executor` - `critical-section` - `portable-atomic` - Gated `tracing` in `bevy_utils` to allow compilation on certain platforms - Switched from `tracing` to `log` for simple message logging within `bevy_ecs`. Note that `tracing` supports capturing from `log` so this should be an uncontroversial change. - Fixed imports and added feature gates as required - Made `bevy_tasks` optional within `bevy_ecs`. Turns out it's only needed for parallel operations which are already gated behind `multi_threaded` anyway. ## Testing - Added to `compile-check-no-std` CI command - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section,portable-atomic --target thumbv6m-none-eabi` - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section` - `cargo check -p bevy_ecs --no-default-features` ## Draft Release Notes Bevy's core ECS now supports `no_std` platforms. In prior versions of Bevy, it was not possible to work with embedded or niche platforms due to our reliance on the standard library, `std`. This has blocked a number of novel use-cases for Bevy, such as an embedded database for IoT devices, or for creating games on retro consoles. With this release, `bevy_ecs` no longer requires `std`. To use Bevy on a `no_std` platform, you must disable default features and enable the new `edge_executor` and `critical-section` features. You may also need to enable `portable-atomic` and `critical-section` if your platform does not natively support all atomic types and operations used by Bevy. ```toml [dependencies] bevy_ecs = { version = "0.16", default-features = false, features = [ # Required for platforms with incomplete atomics (e.g., Raspberry Pi Pico) "portable-atomic", "critical-section", # Optional "bevy_reflect", "serialize", "bevy_debug_stepping", "edge_executor" ] } ``` Currently, this has been tested on bare-metal x86 and the Raspberry Pi Pico. If you have trouble using `bevy_ecs` on a particular platform, please reach out either through a GitHub issue or in the `no_std` working group on the Bevy Discord server. Keep an eye out for future `no_std` updates as we continue to improve the parity between `std` and `no_std`. We look forward to seeing what kinds of applications are now possible with Bevy! ## Notes - Creating PR in draft to ensure CI is passing before requesting reviews. - This implementation has no support for multithreading in `no_std`, especially due to `NonSend` being unsound if allowed in multithreading. The reason is we cannot check the `ThreadId` in `no_std`, so we have no mechanism to at-runtime determine if access is sound. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Vic <59878206+Victoronz@users.noreply.github.com> |
||
![]() |
21195a75e6
|
track_change_detection: Also track spawns/despawns (#16047)
# Objective Expand `track_change_detection` feature to also track entity spawns and despawns. Use this to create better error messages. # Solution Adds `Entities::entity_get_spawned_or_despawned_by` as well as `{all entity reference types}::spawned_by`. This also removes the deprecated `get_many_entities_mut` & co (and therefore can't land in 0.15) because we don't yet have no Polonius. ## Testing Added a test that checks that the locations get updated and these updates are ordered correctly vs hooks & observers. --- ## Showcase Access location: ```rust let mut world = World::new(); let entity = world.spawn_empty().id(); println!("spawned by: {}", world.entity(entity).spawned_by()); ``` ``` spawned by: src/main.rs:5:24 ``` Error message (with `track_change_detection`): ```rust world.despawn(entity); world.entity(entity); ``` ``` thread 'main' panicked at src/main.rs:11:11: Entity 0v1#4294967296 was despawned by src/main.rs:10:11 ``` and without: ``` thread 'main' panicked at src/main.rs:11:11: Entity 0v1#4294967296 does not exist (enable `track_change_detection` feature for more details) ``` Similar error messages now also exists for `Query::get`, `World::entity_mut`, `EntityCommands` creation and everything that causes `B0003`, e.g. ``` error[B0003]: Could not insert a bundle (of type `MaterialMeshBundle<StandardMaterial>`) for entity Entity { index: 7, generation: 1 }, which was despawned by src/main.rs:10:11. See: https://bevyengine.org/learn/errors/#b0003 ``` --------- Co-authored-by: kurk070ff <108901106+kurk070ff@users.noreply.github.com> Co-authored-by: Freya Pines <freya@MacBookAir.lan> Co-authored-by: Freya Pines <freya@Freyas-MacBook-Air.local> Co-authored-by: Matty Weatherley <weatherleymatthew@gmail.com> |
||
![]() |
5a94beb239
|
Extend cloning functionality and add convenience methods to EntityWorldMut and EntityCommands (#16826)
## Objective Thanks to @eugineerd's work on entity cloning (#16132), we now have a robust way to copy components between entities. We can extend this to implement some useful functionality that would have been more complicated before. Closes #15350. ## Solution `EntityCloneBuilder` now automatically includes required components alongside any component added/removed from the component filter. Added the following methods to `EntityCloneBuilder`: - `move_components` - `without_required_components` Added the following methods to `EntityWorldMut` and `EntityCommands`: - `clone_with` - `clone_components` - `move_components` Also added `clone_and_spawn` and `clone_and_spawn_with` to `EntityWorldMut` (`EntityCommands` already had them). ## Showcase ``` assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), None); world.entity_mut(entity_a).clone_components::<B>(entity_b); assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_a).get::<C>(), Some(&C(5))); assert_eq!(world.entity(entity_b).get::<C>(), None); world.entity_mut(entity_a).move_components::<C>(entity_b); assert_eq!(world.entity(entity_a).get::<C>(), None); assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5))); ``` |
||
![]() |
d132239bb1
|
Misc. docs and renames for niche ECS internals (#16786)
## Objective Some structs and methods in the ECS internals have names that don't describe their purpose very well, and sometimes don't have docs either. Also, the function `remove_bundle_from_archetype` is a counterpart to `BundleInfo::add_bundle_to_archetype`, but isn't a method and is in a different file. ## Solution - Renamed the following structs and added docs: | Before | After | |----------------------|------------------------------| | `AddBundle` | `ArchetypeAfterBundleInsert` | | `InsertBundleResult` | `ArchetypeMoveType` | - Renamed the following methods: | Before | After | |---------------------------------------|----------------------------------------------| | `Edges::get_add_bundle` | `Edges::get_archetype_after_bundle_insert` | | `Edges::insert_add_bundle` | `Edges::cache_archetype_after_bundle_insert` | | `Edges::get_remove_bundle` | `Edges::get_archetype_after_bundle_remove` | | `Edges::insert_remove_bundle` | `Edges::cache_archetype_after_bundle_remove` | | `Edges::get_take_bundle` | `Edges::get_archetype_after_bundle_take` | | `Edges::insert_take_bundle` | `Edges::cache_archetype_after_bundle_take` | - Moved `remove_bundle_from_archetype` from `world/entity_ref.rs` to `BundleInfo`. I left the function in entity_ref in the first commit for comparison, look there for the diff of comments and whatnot. - Tidied up docs: - General grammar and spacing. - Made the usage of "insert" and "add" more consistent. - Removed references to information that isn't there. - Renamed `BundleInfo::add_bundle_to_archetype` to `BundleInfo::insert_bundle_into_archetype` for consistency. |
||
![]() |
711246aa34
|
Update hashbrown to 0.15 (#15801)
Updating dependencies; adopted version of #15696. (Supercedes #15696.) Long answer: hashbrown is no longer using ahash by default, meaning that we can't use the default-hasher methods with ahasher. So, we have to use the longer-winded versions instead. This takes the opportunity to also switch our default hasher as well, but without actually enabling the default-hasher feature for hashbrown, meaning that we'll be able to change our hasher more easily at the cost of all of these method calls being obnoxious forever. One large change from 0.15 is that `insert_unique_unchecked` is now `unsafe`, and for cases where unsafe code was denied at the crate level, I replaced it with `insert`. ## Migration Guide `bevy_utils` has updated its version of `hashbrown` to 0.15 and now defaults to `foldhash` instead of `ahash`. This means that if you've hard-coded your hasher to `bevy_utils::AHasher` or separately used the `ahash` crate in your code, you may need to switch to `foldhash` to ensure that everything works like it does in Bevy. |
||
![]() |
61b98ec80f
|
Rename trigger.entity() to trigger.target() (#16716)
# Objective - A `Trigger` has multiple associated `Entity`s - the entity observing the event, and the entity that was targeted by the event. - The field `entity: Entity` encodes no semantic information about what the entity is used for, you can already tell that it's an `Entity` by the type signature! ## Solution - Rename `trigger.entity()` to `trigger.target()` --- ## Changelog - `Trigger`s are associated with multiple entities. `Trigger::entity()` has been renamed to `Trigger::target()` to reflect the semantics of the entity being returned. ## Migration Guide - Rename `Trigger::entity()` to `Trigger::target()`. - Rename `ObserverTrigger::entity` to `ObserverTrigger::target` |
||
![]() |
a6adced9ed
|
Deny derive_more error feature and replace it with thiserror (#16684)
# Objective - Remove `derive_more`'s error derivation and replace it with `thiserror` ## Solution - Added `derive_more`'s `error` feature to `deny.toml` to prevent it sneaking back in. - Reverted to `thiserror` error derivation ## Notes Merge conflicts were too numerous to revert the individual changes, so this reversion was done manually. Please scrutinise carefully during review. |
||
![]() |
912da04699
|
Run observers before hooks for on_replace and on_remove (#16499)
# Objective - Fixes #16498 ## Solution - Trivially swaps ordering of hooks and observers for all call sites where they are triggered for `on_replace` or `on_remove` ## Testing - Just CI --- ## Migration Guide The order of hooks and observers for `on_replace` and `on_remove` has been swapped. Observers are now run before hooks. This is a more natural ordering where the removal ordering is inverted compared to the insertion ordering. |
||
![]() |
76d610d465
|
Flush commands after every mutation in WorldEntityMut (#16219)
# Objective - Currently adding observers spawns an entity which implicitly flushes the command queue, which can cause undefined behaviour if the `WorldEntityMut` is used after this - The reason `WorldEntityMut` attempted to (unsuccessfully) avoid flushing commands until finished was that such commands may move or despawn the entity being referenced, invalidating the cached location. - With the introduction of hooks and observers, this isn't sensible anymore as running the commands generated by hooks immediately is required to maintain correct ordering of operations and to not expose the world in an inconsistent state - Objective is to make command flushing deterministic and fix the related issues - Fixes #16212 - Fixes #14621 - Fixes #16034 ## Solution - Allow `WorldEntityMut` to exist even when it refers to a despawned entity by allowing `EntityLocation` to be marked invalid - Add checks to all methods to panic if trying to access a despawned entity - Flush command queue after every operation that might trigger hooks or observers - Update entity location always after flushing command queue ## Testing - Added test cases for currently broken behaviour - Added test cases that flushes happen in all operations - Added test cases to ensure hooks and commands are run exactly in correct order when nested --- Todo: - [x] Write migration guide - [x] Add tests that using `EntityWorldMut` on a despawned entity panics - [x] Add tests that commands are flushed after every operation that is supposed to flush them - [x] Add tests that hooks, observers and their spawned commands are run in the correct order when nested --- ## Migration Guide Previously `EntityWorldMut` triggered command queue flushes in unpredictable places, which could interfere with hooks and observers. Now the command queue is flushed always immediately after any call in `EntityWorldMut` that spawns or despawns an entity, or adds, removes or replaces a component. This means hooks and observers will run their commands in the correct order. As a side effect, there is a possibility that a hook or observer could despawn the entity that is being referred to by `EntityWorldMut`. This could already currently happen if an observer was added while keeping an `EntityWorldMut` referece and would cause unsound behaviour. If the entity has been despawned, calling any methods which require the entity location will panic. This matches the behaviour that `Commands` will panic if called on an already despawned entity. In the extremely rare case where taking a new `EntityWorldMut` reference or otherwise restructuring the code so that this case does not happen is not possible, there's a new `is_despawned` method that can be used to check if the referred entity has been despawned. |
||
![]() |
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> |
||
![]() |
d3e9ecbb8c
|
Add missing exports in bevy_ecs (#16415)
# Objective Seemed to have missed the export of `DynamicComponentFetch` from #15593. `TryFromFilteredError` which is returned by `impl TryFrom<FiliteredEntityMut/Ref> for EntityRef/Mut` also seemed to have been missing. ## Solution Export both of them. |
||
![]() |
b83c0e106e
|
Add EntityMut::get_mut_by_id_unchecked (#16210)
# Objective - Fixes: #15603 ## Solution - Add an unsafe `get_mut_by_id_unchecked` to `EntityMut` that borrows &self instead of &mut self, thereby allowing access to multiple components simultaneously. ## Testing - a unit test function `get_mut_by_id_unchecked` was added. --------- Co-authored-by: Mike <mike.hsu@gmail.com> |