3c8fae2390
155 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
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> |
||
![]() |
62285a47ba
|
Add simple Disabled marker (#17514)
# Objective We have default query filters now, but there is no first-party marker for entity disabling yet Fixes #17458 ## Solution Add the marker, cool recursive features and/or potential hook changes should be follow up work ## Testing Added a unit test to check that the new marker is enabled by default |
||
![]() |
b58eda01e2
|
feat(ecs): add EntityEntryCommands::entity() method chaining (#17580)
This allows you to continue chaining method calls after calling `EntityCommands::entry`: ```rust commands .entity(player.entity) .entry::<Level>() // Modify the component if it exists .and_modify(|mut lvl| lvl.0 += 1) // Otherwise insert a default value .or_insert(Level(0)) // Return the EntityCommands for the entity .entity() // And continue chaining method calls .insert(Name::new("Player")); ``` --------- Signed-off-by: Jean Mertz <git@jeanmertz.com> |
||
![]() |
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> |
||
![]() |
fe24652cc0
|
Change World::try_despawn and World::try_insert_batch to return Result (#17376)
## Objective Most `try` methods on `World` return a `Result`, but `try_despawn` and `try_insert_batch` don't. Since Bevy's error handling is advancing, these should be brought in line. ## Solution - Added `TryDespawnError` and `TryInsertBatchError`. - `try_despawn`, `try_insert_batch`, and `try_insert_batch_if_new` now return their respective errors. - Fixed slightly incorrect behavior in `try_insert_batch_with_caller`. - The method was always meant to continue with the rest of the batch if an entity was missing, but that only worked after the first entity; if the first entity was missing, the method would exit early. This has been resolved. ## Migration Guide - `World::try_despawn` now returns a `Result` rather than a `bool`. - `World::try_insert_batch` and `World::try_insert_batch_if_new` now return a `Result` where they previously returned nothing. |
||
![]() |
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? |
||
![]() |
ee4414159b
|
Add Result handling to Commands and EntityCommands (#17043)
## Objective Fixes #2004 Fixes #3845 Fixes #7118 Fixes #10166 ## Solution - The crux of this PR is the new `Command::with_error_handling` method. This wraps the relevant command in another command that, when applied, will apply the original command and handle any resulting errors. - To enable this, `Command::apply` and `EntityCommand::apply` now return `Result`. - `Command::with_error_handling` takes as a parameter an error handler of the form `fn(&mut World, CommandError)`, which it passes the error to. - `CommandError` is an enum that can be either `NoSuchEntity(Entity)` or `CommandFailed(Box<dyn Error>)`. ### Closures - Closure commands can now optionally return `Result`, which will be passed to `with_error_handling`. ### Commands - Fallible commands can be queued with `Commands::queue_fallible` and `Commands::queue_fallible_with`, which call `with_error_handling` before queuing them (using `Commands::queue` will queue them without error handling). - `Commands::queue_fallible_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `command` submodule provides unqueued forms of built-in fallible commands so that you can use them with `queue_fallible_with`. - There is also an `error_handler` submodule that provides simple error handlers for convenience. ### Entity Commands - `EntityCommand` now automatically checks if the entity exists before executing the command, and returns `NoSuchEntity` if it doesn't. - Since all entity commands might need to return an error, they are always queued with error handling. - `EntityCommands::queue_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `entity_command` submodule provides unqueued forms of built-in entity commands so that you can use them with `queue_with`. ### Defaults - In the future, commands should all fail according to the global error handling setting. That doesn't exist yet though. - For this PR, commands all fail the way they do on `main`. - Both now and in the future, the defaults can be overridden by `Commands::override_error_handler` (or equivalent methods on `EntityCommands` and `EntityEntryCommands`). - `override_error_handler` takes an error handler (`fn(&mut World, CommandError)`) and passes it to every subsequent command queued with `Commands::queue_fallible` or `EntityCommands::queue`. - The `_with` variants of the queue methods will still provide an error handler directly to the command. - An override can be reset with `reset_error_handler`. ## Future Work - After a universal error handling mode is added, we can change all commands to fail that way by default. - Once we have all commands failing the same way (which would require either the full removal of `try` variants or just making them useless while they're deprecated), `queue_fallible_with_default` could be removed, since its only purpose is to enable commands having different defaults. |
||
![]() |
7112d5594e
|
Remove all deprecated code (#16338)
# Objective Release cycle things ## Solution Delete items deprecated in 0.15 and migrate bevy itself. ## Testing CI |
||
![]() |
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. |
||
![]() |
9ac7e17f2e
|
Refactor hierarchy-related commands to remove structs (#17029)
## Objective Continuation of #16999. This PR handles the following: - Many hierarchy-related commands are wrappers around `World` and `EntityWorldMut` methods and can be moved to closures: - `AddChild` - `InsertChildren` - `AddChildren` - `RemoveChildren` - `ClearChildren` - `ReplaceChildren` - `RemoveParent` - `DespawnRecursive` - `DespawnChildrenRecursive` - `AddChildInPlace` - `RemoveParentInPlace` - `SendEvent` is a wrapper around `World` methods and can be moved to a closure (and its file deleted). ## Migration Guide If you were queuing the structs of hierarchy-related commands or `SendEvent` directly, you will need to change them to the methods implemented on `EntityCommands` (or `Commands` for `SendEvent`): | Struct | Method | |--------------------------------------------------------------------|---------------------------------------------------------------------------------------------| | `commands.queue(AddChild { child, parent });` | `commands.entity(parent).add_child(child);` OR `commands.entity(child).set_parent(parent);` | | `commands.queue(AddChildren { children, parent });` | `commands.entity(parent).add_children(children);` | | `commands.queue(InsertChildren { children, parent });` | `commands.entity(parent).insert_children(children);` | | `commands.queue(RemoveChildren { children, parent });` | `commands.entity(parent).remove_children(children);` | | `commands.queue(ReplaceChildren { children, parent });` | `commands.entity(parent).replace_children(children);` | | `commands.queue(ClearChildren { parent });` | `commands.entity(parent).clear_children();` | | `commands.queue(RemoveParent { child });` | `commands.entity(child).remove_parent()` | | `commands.queue(DespawnRecursive { entity, warn: true });` | `commands.entity(entity).despawn_recursive();` | | `commands.queue(DespawnRecursive { entity, warn: false });` | `commands.entity(entity).try_despawn_recursive();` | | `commands.queue(DespawnChildrenRecursive { entity, warn: true });` | `commands.entity(entity).despawn_descendants();` | | `commands.queue(DespawnChildrenRecursive { entity, warn: false});` | `commands.entity(entity).try_despawn_descendants();` | | `commands.queue(SendEvent { event });` | `commands.send_event(event);` | |
||
![]() |
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. |
||
![]() |
64efd08e13
|
Prefer Display over Debug (#16112)
# Objective Fixes #16104 ## Solution I removed all instances of `:?` and put them back one by one where it caused an error. I removed some bevy_utils helper functions that were only used in 2 places and don't add value. See: #11478 ## Testing CI should catch the mistakes ## Migration Guide `bevy::utils::{dbg,info,warn,error}` were removed. Use `bevy::utils::tracing::{debug,info,warn,error}` instead. --------- Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net> |
||
![]() |
1669ca676a
|
Remove vestigial helper functions for Commands and EntityCommands (#16936)
## Objective I believe these started as structs, back when that was how commands had to be implemented. Now they just hide implementation details. ## Solution Remove the helper functions and move each implementation into its respective method, except for the ones that actually reduce code duplication. |
||
![]() |
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))); ``` |
||
![]() |
b2d3371814
|
Event source location tracking (#16778)
# Objective Fixes #16776 ## Solution - reflect `&'static Location` as an opaque type - I've added this to `impls/std.rs` because other core types are there too. Maybe they should be split out into a `core.rs` in another PR. - add source location to `EventId` (behind the `tracking_change_detection` feature flag) ## Testing --- ## Showcase ```rust fn apply_damage_to_health( mut dmg_events: EventReader<DealDamage>, ) { for (event, event_id) in dmg_events.read_with_id() { info!( "Applying {} damage, triggered by {}", event.amount, event_id.caller ); … ``` ``` 2024-12-12T01:21:50.126827Z INFO event: Applying 9 damage, triggered by examples/ecs/event.rs:47:16 ``` ## Migration Guide - If you manually construct a `SendEvent`, use `SendEvent::new()` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
854934c380
|
one shot system cleanup (#16516)
# Objective - Fixes #16497 - This is my first PR, so I'm still learning to contribute to the project ## Solution - Added struct `UnregisterSystemCached` and function `unregister_system_cached` - renamed `World::run_system_with_input` to `run_system_with` - reordered input parameters for `World::run_system_once_with` ## Testing - Added a crude test which registers a system via `World::register_system_cached`, and removes it via `Command::unregister_system_cached`. ## Migration Guide - Change all occurrences of `World::run_system_with_input` to `World::run_system_with`. - swap the order of input parameters for `World::run_system_once_with` such that the system comes before the input. --------- Co-authored-by: Paul Mattern <mail@paulmattern.dev> |
||
![]() |
db4c468fe2
|
Rename EntityCommands::clone to clone_and_spawn (#16696)
## Objective Follow-up to #16672. `EntityCommands::clone` looks the same as the `Clone` trait, which could be confusing. A discord discussion has made me realize that's probably a bigger problem than I thought. Oops :P ## Solution Renamed `EntityCommands::clone` to `EntityCommands::clone_and_spawn`, renamed `EntityCommands::clone_with` to `EntityCommands::clone_and_spawn_with`. Also added some docs explaining the commands' relation to `Clone` (components need to implement it (or `Reflect`)). ## Showcase ``` // Create a new entity and keep its EntityCommands let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); // Create a clone of the first entity let mut entity_clone = entity.clone_and_spawn(); ``` ## The Bikeshed - `clone_and_spawn` (Alice's suggestion) - `spawn_clone` (benfrankel's suggestion) - `spawn_cloned` (rparrett's suggestion) |
||
![]() |
d0afdc6b45
|
Move clone_entity commands to EntityCommands (#16672)
## Objective I was resolving a conflict between #16132 and my PR #15929 and thought the `clone_entity` commands made more sense in `EntityCommands`. ## Solution Moved `Commands::clone_entity` to `EntityCommands::clone`, moved `Commands::clone_entity_with` to `EntityCommands::clone_with`. ## Testing Ran the two tests that used the old methods. ## Showcase ``` // Create a new entity and keep its EntityCommands. let mut entity = commands.spawn((ComponentA(10), ComponentB(20))); // Create a clone of the first entity let mut entity_clone = entity.clone(); ``` The only potential downside is that the method name is now the same as the one from the `Clone` trait. `EntityCommands` doesn't implement `Clone` though, so there's no actual conflict. Maybe I'm biased because this'll work better with my PR, but I think the UX is nicer regardless. |
||
![]() |
f87b9fe20c
|
Turn apply_deferred into a ZST System (#16642)
# Objective - Required by #16622 due to differing implementations of `System` by `FunctionSystem` and `ExclusiveFunctionSystem`. - Optimize the memory usage of instances of `apply_deferred` in system schedules. ## Solution By changing `apply_deferred` from being an ordinary system that ends up as an `ExclusiveFunctionSystem`, and instead into a ZST struct that implements `System` manually, we save ~320 bytes per instance of `apply_deferred` in any schedule. ## Testing - All current tests pass. --- ## Migration Guide - If you were previously calling the special `apply_deferred` system via `apply_deferred(world)`, don't. |
||
![]() |
a35811d088
|
Add Immutable Component Support (#16372)
# Objective - Fixes #16208 ## Solution - Added an associated type to `Component`, `Mutability`, which flags whether a component is mutable, or immutable. If `Mutability= Mutable`, the component is mutable. If `Mutability= Immutable`, the component is immutable. - Updated `derive_component` to default to mutable unless an `#[component(immutable)]` attribute is added. - Updated `ReflectComponent` to check if a component is mutable and, if not, panic when attempting to mutate. ## Testing - CI - `immutable_components` example. --- ## Showcase Users can now mark a component as `#[component(immutable)]` to prevent safe mutation of a component while it is attached to an entity: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` This prevents creating an exclusive reference to the component while it is attached to an entity. This is particularly powerful when combined with component hooks, as you can now fully track a component's value, ensuring whatever invariants you desire are upheld. Before this would be done my making a component private, and manually creating a `QueryData` implementation which only permitted read access. <details> <summary>Using immutable components as an index</summary> ```rust /// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)] #[component( immutable, on_insert = on_insert_name, on_replace = on_replace_name, )] pub struct Name(pub &'static str); /// This index allows for O(1) lookups of an [`Entity`] by its [`Name`]. #[derive(Resource, Default)] struct NameIndex { name_to_entity: HashMap<Name, Entity>, } impl NameIndex { fn get_entity(&self, name: &'static str) -> Option<Entity> { self.name_to_entity.get(&Name(name)).copied() } } fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.insert(name, entity); } fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.remove(&name); } // Setup our name index world.init_resource::<NameIndex>(); // Spawn some entities! let alyssa = world.spawn(Name("Alyssa")).id(); let javier = world.spawn(Name("Javier")).id(); // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Alyssa"), Some(alyssa)); assert_eq!(index.get_entity("Javier"), Some(javier)); // Changing the name of an entity is also fully capture by our index world.entity_mut(javier).insert(Name("Steven")); // Javier changed their name to Steven let steven = javier; // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Javier"), None); assert_eq!(index.get_entity("Steven"), Some(steven)); ``` </details> Additionally, users can use `Component<Mutability = ...>` in trait bounds to enforce that a component _is_ mutable or _is_ immutable. When using `Component` as a trait bound without specifying `Mutability`, any component is applicable. However, methods which only work on mutable or immutable components are unavailable, since the compiler must be pessimistic about the type. ## Migration Guide - When implementing `Component` manually, you must now provide a type for `Mutability`. The type `Mutable` provides equivalent behaviour to earlier versions of `Component`: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` - When working with generic components, you may need to specify that your generic parameter implements `Component<Mutability = Mutable>` rather than `Component` if you require mutable access to said component. - The entity entry API has had to have some changes made to minimise friction when working with immutable components. Methods which previously returned a `Mut<T>` will now typically return an `OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get the `Mut<T>` item again. ## Draft Release Notes Components can now be made immutable while stored within the ECS. Components are the fundamental unit of data within an ECS, and Bevy provides a number of ways to work with them that align with Rust's rules around ownership and borrowing. One part of this is hooks, which allow for defining custom behavior at key points in a component's lifecycle, such as addition and removal. However, there is currently no way to respond to _mutation_ of a component using hooks. The reasons for this are quite technical, but to summarize, their addition poses a significant challenge to Bevy's core promises around performance. Without mutation hooks, it's relatively trivial to modify a component in such a way that breaks invariants it intends to uphold. For example, you can use `core::mem::swap` to swap the components of two entities, bypassing the insertion and removal hooks. This means the only way to react to this modification is via change detection in a system, which then begs the question of what happens _between_ that alteration and the next run of that system? Alternatively, you could make your component private to prevent mutation, but now you need to provide commands and a custom `QueryData` implementation to allow users to interact with your component at all. Immutable components solve this problem by preventing the creation of an exclusive reference to the component entirely. Without an exclusive reference, the only way to modify an immutable component is via removal or replacement, which is fully captured by component hooks. To make a component immutable, simply add `#[component(immutable)]`: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` When implementing `Component` manually, there is an associated type `Mutability` which controls this behavior: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` Note that this means when working with generic components, you may need to specify that a component is mutable to gain access to certain methods: ```rust // Before fn bar<C: Component>() { // ... } // After fn bar<C: Component<Mutability = Mutable>>() { // ... } ``` With this new tool, creating index components, or caching data on an entity should be more user friendly, allowing libraries to provide APIs relying on components and hooks to uphold their invariants. ## Notes - ~~I've done my best to implement this feature, but I'm not happy with how reflection has turned out. If any reflection SMEs know a way to improve this situation I'd greatly appreciate it.~~ There is an outstanding issue around the fallibility of mutable methods on `ReflectComponent`, but the DX is largely unchanged from `main` now. - I've attempted to prevent all safe mutable access to a component that does not implement `Component<Mutability = Mutable>`, but there may still be some methods I have missed. Please indicate so and I will address them, as they are bugs. - Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever you do with unsafe is between you and your compiler. - I am marking this PR as ready, but I suspect it will undergo fairly major revisions based on SME feedback. - I've marked this PR as _Uncontroversial_ based on the feature, not the implementation. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Nuutti Kotivuori <naked@iki.fi> |
||
![]() |
d92fc1e456
|
Move required components doc to type doc (#16575)
# Objective Make documentation of a component's required components more visible by moving it to the type's docs ## Solution Change `#[require]` from a derive macro helper to an attribute macro. Disadvantages: - this silences any unused code warnings on the component, as it is used by the macro! - need to import `require` if not using the ecs prelude (I have not included this in the migration guilde as Rust tooling already suggests the fix) --- ## Showcase  --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> |
||
![]() |
2e267bba5a
|
Entity cloning (#16132)
## Objective Fixes #1515 This PR implements a flexible entity cloning system. The primary use case for it is to clone dynamically-generated entities. Example: ```rs #[derive(Component, Clone)] pub struct Projectile; #[derive(Component, Clone)] pub struct Damage { value: f32, } fn player_input( mut commands: Commands, projectiles: Query<Entity, With<Projectile>>, input: Res<ButtonInput<KeyCode>>, ) { // Fire a projectile if input.just_pressed(KeyCode::KeyF) { commands.spawn((Projectile, Damage { value: 10.0 })); } // Triplicate all active projectiles if input.just_pressed(KeyCode::KeyT) { for projectile in projectiles.iter() { // To triplicate a projectile we need to create 2 more clones for _ in 0..2{ commands.clone_entity(projectile) } } } } ``` ## Solution ### Commands Add a `clone_entity` command to create a clone of an entity with all components that can be cloned. Components that can't be cloned will be ignored. ```rs commands.clone_entity(entity) ``` If there is a need to configure the cloning process (like set to clone recursively), there is a second command: ```rs commands.clone_entity_with(entity, |builder| { builder.recursive(true) }); ``` Both of these commands return `EntityCommands` of the cloned entity, so the copy can be modified afterwards. ### Builder All these commands use `EntityCloneBuilder` internally. If there is a need to clone an entity using `World` instead, it is also possible: ```rs let entity = world.spawn(Component).id(); let entity_clone = world.spawn_empty().id(); EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone); ``` Builder has methods to `allow` or `deny` certain components during cloning if required and can be extended by implementing traits on it. This PR includes two `EntityCloneBuilder` extensions: `CloneEntityWithObserversExt` to configure adding cloned entity to observers of the original entity, and `CloneEntityRecursiveExt` to configure cloning an entity recursively. ### Clone implementations By default, all components that implement either `Clone` or `Reflect` will be cloned (with `Clone`-based implementation preferred in case component implements both). This can be overriden on a per-component basis: ```rs impl Component for SomeComponent { const STORAGE_TYPE: StorageType = StorageType::Table; fn get_component_clone_handler() -> ComponentCloneHandler { // Don't clone this component ComponentCloneHandler::Ignore } } ``` ### `ComponentCloneHandlers` Clone implementation specified in `get_component_clone_handler` will get registered in `ComponentCloneHandlers` (stored in `bevy_ecs::component::Components`) at component registration time. The clone handler implementation provided by a component can be overriden after registration like so: ```rs let component_id = world.components().component_id::<Component>().unwrap() world.get_component_clone_handlers_mut() .set_component_handler(component_id, ComponentCloneHandler::Custom(component_clone_custom)) ``` The default clone handler for all components that do not explicitly define one (or don't derive `Component`) is `component_clone_via_reflect` if `bevy_reflect` feature is enabled, and `component_clone_ignore` (noop) otherwise. Default handler can be overriden using `ComponentCloneHandlers::set_default_handler` ### Handlers Component clone handlers can be used to modify component cloning behavior. The general signature for a handler that can be used in `ComponentCloneHandler::Custom` is as follows: ```rs pub fn component_clone_custom( world: &mut DeferredWorld, entity_cloner: &EntityCloner, ) { // implementation } ``` The `EntityCloner` implementation (used internally by `EntityCloneBuilder`) assumes that after calling this custom handler, the `target` entity has the desired version of the component from the `source` entity. ### Builder handler overrides Besides component-defined and world-overriden handlers, `EntityCloneBuilder` also has a way to override handlers locally. It is mainly used to allow configuration methods like `recursive` and `add_observers`. ```rs // From observer clone handler implementation impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> { fn add_observers(&mut self, add_observers: bool) -> &mut Self { if add_observers { self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom( component_clone_observed_by, )) } else { self.remove_component_clone_handler_override::<ObservedBy>() } } } ``` ## Testing Includes some basic functionality tests and doctests. Performance-wise this feature is the same as calling `clone` followed by `insert` for every entity component. There is also some inherent overhead due to every component clone handler having to access component data through `World`, but this can be reduced without breaking current public API in a later PR. |
||
![]() |
c02696b609
|
Add Commands::run_schedule (#16537)
# Objective - Fixes #16495 ## Solution - Added `Commands::run_schedule`, which internally calls `World::try_run_schedule`, logging any issues. ## Testing - Added doctest - Ran CI ## Showcase Instead of writing: ```rust commands.queue(|world: &mut World| world.run_schedule(FooSchedule)); ``` You can now write: ```rust commands.run_schedule(FooSchedule); ``` |
||
![]() |
a8c610a52d
|
Add unregister_system command (#16340)
# Objective Fixes #16266 ## Solution Added an `UnregisterSystem` command struct and `Commands::unregister_system`. Also renamed `World::remove_system` and `World::remove_system_cached` to `World::unregister_*` ## Testing It's a fairly simple change, but I tested locally to ensure it actually works. --------- Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> |
||
![]() |
30d84519a2
|
Use en-us locale for typos (#16037)
# Objective Bevy seems to want to standardize on "American English" spellings. Not sure if this is laid out anywhere in writing, but see also #15947. While perusing the docs for `typos`, I noticed that it has a `locale` config option and tried it out. ## Solution Switch to `en-us` locale in the `typos` config and run `typos -w` ## Migration Guide The following methods or fields have been renamed from `*dependants*` to `*dependents*`. - `ProcessorAssetInfo::dependants` - `ProcessorAssetInfos::add_dependant` - `ProcessorAssetInfos::non_existent_dependants` - `AssetInfo::dependants_waiting_on_load` - `AssetInfo::dependants_waiting_on_recursive_dep_load` - `AssetInfos::loader_dependants` - `AssetInfos::remove_dependants_and_labels` |
||
![]() |
da5d2fccf5
|
Fix some duplicate words in docs/comments (#15980)
# Objective Stumbled upon one of these, and set off in search of more, armed with my trusty `\b(\w+)\s+\1\b`. ## Solution Remove ~one~ one of them. |
||
![]() |
e79bc7811d
|
Fix *most* clippy lints (#15906)
# Objective Another clippy-lint fix: the goal is so that `ci lints` actually displays the problems that a contributor caused, and not a bunch of existing stuff in the repo. (when run on nightly) ## Solution This fixes all but the `clippy::needless_lifetimes` lint, which will result in substantially more fixes and be in other PR(s). I also explicitly allow `non_local_definitions` since it is [not working correctly, but will be fixed](https://github.com/rust-lang/rust/issues/131643). A few things were manually fixed: for example, some places had an explicitly defined `div_ceil` function that was used, which is no longer needed since this function is stable on unsigned integers. Also, empty lines in doc comments were handled individually. ## Testing I ran `cargo clippy --workspace --all-targets --all-features --fix --allow-staged` with the `clippy::needless_lifetimes` lint marked as `allow` in `Cargo.toml` to avoid fixing that too. It now passes with all but the listed lint. |
||
![]() |
3d6b24880e
|
Add insert_batch and variations (#15702)
# Objective `insert_or_spawn_batch` exists, but a version for just inserting doesn't - Closes #2693 - Closes #8384 - Adopts/supersedes #8600 ## Solution Add `insert_batch`, along with the most common `insert` variations: - `World::insert_batch` - `World::insert_batch_if_new` - `World::try_insert_batch` - `World::try_insert_batch_if_new` - `Commands::insert_batch` - `Commands::insert_batch_if_new` - `Commands::try_insert_batch` - `Commands::try_insert_batch_if_new` ## Testing Added tests, and added a benchmark for `insert_batch`. Performance is slightly better than `insert_or_spawn_batch` when only inserting:  <details> <summary>old benchmark</summary> This was before reworking it to remove the `UnsafeWorldCell`:  </details> --- ## Showcase Usage is the same as `insert_or_spawn_batch`: ``` use bevy_ecs::{entity::Entity, world::World, component::Component}; #[derive(Component)] struct A(&'static str); #[derive(Component, PartialEq, Debug)] struct B(f32); let mut world = World::new(); let entity_a = world.spawn_empty().id(); let entity_b = world.spawn_empty().id(); world.insert_batch([ (entity_a, (A("a"), B(0.0))), (entity_b, (A("b"), B(1.0))), ]); assert_eq!(world.get::<B>(entity_a), Some(&B(0.0))); ``` |
||
![]() |
c2c19e5ae4
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.** # Objective - Implement https://github.com/bevyengine/bevy/discussions/15014 ## Solution This implements [cart's proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459) faithfully except for one change. I separated `TextSpan` from `TextSpan2d` because `TextSpan` needs to require the `GhostNode` component, which is a `bevy_ui` component only usable by UI. Extra changes: - Added `EntityCommands::commands_mut` that returns a mutable reference. This is a blocker for extension methods that return something other than `self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable reference for this reason. ## Testing - [x] Text examples all work. --- ## Showcase TODO: showcase-worthy ## Migration Guide TODO: very breaking ### Accessing text spans by index Text sections are now text sections on different entities in a hierarchy, Use the new `TextReader` and `TextWriter` system parameters to access spans by index. Before: ```rust fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) { let text = query.single_mut(); text.sections[1].value = format_time(time.elapsed()); } ``` After: ```rust fn refresh_text( query: Query<Entity, With<TimeText>>, mut writer: UiTextWriter, time: Res<Time> ) { let entity = query.single(); *writer.text(entity, 1) = format_time(time.elapsed()); } ``` ### Iterating text spans Text spans are now entities in a hierarchy, so the new `UiTextReader` and `UiTextWriter` system parameters provide ways to iterate that hierarchy. The `UiTextReader::iter` method will give you a normal iterator over spans, and `UiTextWriter::for_each` lets you visit each of the spans. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
219b5930f1
|
Rename App/World::observe to add_observer , EntityWorldMut::observe_entity to observe . (#15754)
# Objective - Closes #15752 Calling the functions `App::observe` and `World::observe` doesn't make sense because you're not "observing" the `App` or `World`, you're adding an observer that listens for an event that occurs *within* the `World`. We should rename them to better fit this. ## Solution Renames: - `App::observe` -> `App::add_observer` - `World::observe` -> `World::add_observer` - `Commands::observe` -> `Commands::add_observer` - `EntityWorldMut::observe_entity` -> `EntityWorldMut::observe` (Note this isn't a breaking change as the original rename was introduced earlier this cycle.) ## Testing Reusing current tests. |
||
![]() |
d1bd46d45e
|
Deprecate get_or_spawn (#15652)
# Objective After merging retained rendering world #15320, we now have a good way of creating a link between worlds (*HIYAA intensifies*). This means that `get_or_spawn` is no longer necessary for that function. Entity should be opaque as the warning above `get_or_spawn` says. This is also part of #15459. I'm deprecating `get_or_spawn_batch` in a different PR in order to keep the PR small in size. ## Solution Deprecate `get_or_spawn` and replace it with `get_entity` in most contexts. If it's possible to query `&RenderEntity`, then the entity is synced and `render_entity.id()` is initialized in the render world. ## Migration Guide If you are given an `Entity` and you want to do something with it, use `Commands.entity(...)` or `World.entity(...)`. If instead you want to spawn something use `Commands.spawn(...)` or `World.spawn(...)`. If you are not sure if an entity exists, you can always use `get_entity` and match on the `Option<...>` that is returned. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
584d14808a
|
Allow World::entity family of functions to take multiple entities and get multiple references back (#15614)
# Objective Following the pattern established in #15593, we can reduce the API surface of `World` by providing a single function to grab both a singular entity reference, or multiple entity references. ## Solution The following functions can now also take multiple entity IDs and will return multiple entity references back: - `World::entity` - `World::get_entity` - `World::entity_mut` - `World::get_entity_mut` - `DeferredWorld::entity_mut` - `DeferredWorld::get_entity_mut` If you pass in X, you receive Y: - give a single `Entity`, receive a single `EntityRef`/`EntityWorldMut` (matches current behavior) - give a `[Entity; N]`/`&[Entity; N]` (array), receive an equally-sized `[EntityRef; N]`/`[EntityMut; N]` - give a `&[Entity]` (slice), receive a `Vec<EntityRef>`/`Vec<EntityMut>` - give a `&EntityHashSet`, receive a `EntityHashMap<EntityRef>`/`EntityHashMap<EntityMut>` Note that `EntityWorldMut` is only returned in the single-entity case, because having multiple at the same time would lead to UB. Also, `DeferredWorld` receives an `EntityMut` in the single-entity case because it does not allow structural access. ## Testing - Added doc-tests on `World::entity`, `World::entity_mut`, and `DeferredWorld::entity_mut` - Added tests for aliased mutability and entity existence --- ## Showcase <details> <summary>Click to view showcase</summary> The APIs for fetching `EntityRef`s and `EntityMut`s from the `World` have been unified. ```rust // This code will be referred to by subsequent code blocks. let world = World::new(); let e1 = world.spawn_empty().id(); let e2 = world.spawn_empty().id(); let e3 = world.spawn_empty().id(); ``` Querying for a single entity remains mostly the same: ```rust // 0.14 let eref: EntityRef = world.entity(e1); let emut: EntityWorldMut = world.entity_mut(e1); let eref: Option<EntityRef> = world.get_entity(e1); let emut: Option<EntityWorldMut> = world.get_entity_mut(e1); // 0.15 let eref: EntityRef = world.entity(e1); let emut: EntityWorldMut = world.entity_mut(e1); let eref: Result<EntityRef, Entity> = world.get_entity(e1); let emut: Result<EntityWorldMut, Entity> = world.get_entity_mut(e1); ``` Querying for multiple entities with an array has changed: ```rust // 0.14 let erefs: [EntityRef; 2] = world.many_entities([e1, e2]); let emuts: [EntityMut; 2] = world.many_entities_mut([e1, e2]); let erefs: Result<[EntityRef; 2], Entity> = world.get_many_entities([e1, e2]); let emuts: Result<[EntityMut; 2], QueryEntityError> = world.get_many_entities_mut([e1, e2]); // 0.15 let erefs: [EntityRef; 2] = world.entity([e1, e2]); let emuts: [EntityMut; 2] = world.entity_mut([e1, e2]); let erefs: Result<[EntityRef; 2], Entity> = world.get_entity([e1, e2]); let emuts: Result<[EntityMut; 2], EntityFetchError> = world.get_entity_mut([e1, e2]); ``` Querying for multiple entities with a slice has changed: ```rust let ids = vec![e1, e2, e3]); // 0.14 let erefs: Result<Vec<EntityRef>, Entity> = world.get_many_entities_dynamic(&ids[..]); let emuts: Result<Vec<EntityMut>, QueryEntityError> = world.get_many_entities_dynamic_mut(&ids[..]); // 0.15 let erefs: Result<Vec<EntityRef>, Entity> = world.get_entity(&ids[..]); let emuts: Result<Vec<EntityMut>, EntityFetchError> = world.get_entity_mut(&ids[..]); let erefs: Vec<EntityRef> = world.entity(&ids[..]); // Newly possible! let emuts: Vec<EntityMut> = world.entity_mut(&ids[..]); // Newly possible! ``` Querying for multiple entities with an `EntityHashSet` has changed: ```rust let set = EntityHashSet::from_iter([e1, e2, e3]); // 0.14 let emuts: Result<Vec<EntityMut>, QueryEntityError> = world.get_many_entities_from_set_mut(&set); // 0.15 let emuts: Result<EntityHashMap<EntityMut>, EntityFetchError> = world.get_entity_mut(&set); let erefs: Result<EntityHashMap<EntityRef>, EntityFetchError> = world.get_entity(&set); // Newly possible! let emuts: EntityHashMap<EntityMut> = world.entity_mut(&set); // Newly possible! let erefs: EntityHashMap<EntityRef> = world.entity(&set); // Newly possible! ``` </details> ## Migration Guide - `World::get_entity` now returns `Result<_, Entity>` instead of `Option<_>`. - Use `world.get_entity(..).ok()` to return to the previous behavior. - `World::get_entity_mut` and `DeferredWorld::get_entity_mut` now return `Result<_, EntityFetchError>` instead of `Option<_>`. - Use `world.get_entity_mut(..).ok()` to return to the previous behavior. - Type inference for `World::entity`, `World::entity_mut`, `World::get_entity`, `World::get_entity_mut`, `DeferredWorld::entity_mut`, and `DeferredWorld::get_entity_mut` has changed, and might now require the input argument's type to be explicitly written when inside closures. - The following functions have been deprecated, and should be replaced as such: - `World::many_entities` -> `World::entity::<[Entity; N]>` - `World::many_entities_mut` -> `World::entity_mut::<[Entity; N]>` - `World::get_many_entities` -> `World::get_entity::<[Entity; N]>` - `World::get_many_entities_dynamic` -> `World::get_entity::<&[Entity]>` - `World::get_many_entities_mut` -> `World::get_entity_mut::<[Entity; N]>` - The equivalent return type has changed from `Result<_, QueryEntityError>` to `Result<_, EntityFetchError>` - `World::get_many_entities_dynamic_mut` -> `World::get_entity_mut::<&[Entity]>1 - The equivalent return type has changed from `Result<_, QueryEntityError>` to `Result<_, EntityFetchError>` - `World::get_many_entities_from_set_mut` -> `World::get_entity_mut::<&EntityHashSet>` - The equivalent return type has changed from `Result<Vec<EntityMut>, QueryEntityError>` to `Result<EntityHashMap<EntityMut>, EntityFetchError>`. If necessary, you can still convert the `EntityHashMap` into a `Vec`. |
||
![]() |
8bf5d99d86
|
Add method to remove component and all required components for removed component (#15026)
## Objective The new Required Components feature (#14791) in Bevy allows spawning a fixed set of components with a single method with cool require macro. However, there's currently no corresponding method to remove all those components together. This makes it challenging to keep insertion and removal code in sync, especially for simple using cases. ```rust #[derive(Component)] #[require(Y)] struct X; #[derive(Component, Default)] struct Y; world.entity_mut(e).insert(X); // Spawns both X and Y world.entity_mut(e).remove::<X>(); world.entity_mut(e).remove::<Y>(); // We need to manually remove dependencies without any sync with the `require` macro ``` ## Solution Simplifies component management by providing operations for removal required components. This PR introduces simple 'footgun' methods to removes all components of this bundle and its required components. Two new methods are introduced: For Commands: ```rust commands.entity(e).remove_with_requires::<B>(); ``` For World: ```rust world.entity_mut(e).remove_with_requires::<B>(); ``` For performance I created new field in Bundels struct. This new field "contributed_bundle_ids" contains cached ids for dynamic bundles constructed from bundle_info.cintributed_components() ## Testing The PR includes three test cases: 1. Removing a single component with requirements using World. 2. Removing a bundle with requirements using World. 3. Removing a single component with requirements using Commands. 4. Removing a single component with **runtime** requirements using Commands These tests ensure the feature works as expected across different scenarios. ## Showcase Example: ```rust use bevy_ecs::prelude::*; #[derive(Component)] #[require(Y)] struct X; #[derive(Component, Default)] #[require(Z)] struct Y; #[derive(Component, Default)] struct Z; #[derive(Component)] struct W; let mut world = World::new(); // Spawn an entity with X, Y, Z, and W components let entity = world.spawn((X, W)).id(); assert!(world.entity(entity).contains::<X>()); assert!(world.entity(entity).contains::<Y>()); assert!(world.entity(entity).contains::<Z>()); assert!(world.entity(entity).contains::<W>()); // Remove X and required components Y, Z world.entity_mut(entity).remove_with_requires::<X>(); assert!(!world.entity(entity).contains::<X>()); assert!(!world.entity(entity).contains::<Y>()); assert!(!world.entity(entity).contains::<Z>()); assert!(world.entity(entity).contains::<W>()); ``` ## Motivation for PR #15580 ## Performance I made simple benchmark ```rust let mut world = World::default(); let entity = world.spawn_empty().id(); let steps = 100_000_000; let start = std::time::Instant::now(); for _ in 0..steps { world.entity_mut(entity).insert(X); world.entity_mut(entity).remove::<(X, Y, Z, W)>(); } let end = std::time::Instant::now(); println!("normal remove: {:?} ", (end - start).as_secs_f32()); println!("one remove: {:?} micros", (end - start).as_secs_f64() / steps as f64 * 1_000_000.0); let start = std::time::Instant::now(); for _ in 0..steps { world.entity_mut(entity).insert(X); world.entity_mut(entity).remove_with_requires::<X>(); } let end = std::time::Instant::now(); println!("remove_with_requires: {:?} ", (end - start).as_secs_f32()); println!("one remove_with_requires: {:?} micros", (end - start).as_secs_f64() / steps as f64 * 1_000_000.0); ``` Output: CPU: Amd Ryzen 7 2700x ```bash normal remove: 17.36135 one remove: 0.17361348299999999 micros remove_with_requires: 17.534006 one remove_with_requires: 0.17534005400000002 micros ``` NOTE: I didn't find any tests or mechanism in the repository to update BundleInfo after creating new runtime requirements with an existing BundleInfo. So this PR also does not contain such logic. ## Future work (outside this PR) Create cache system for fast removing components in "safe" mode, where "safe" mode is remove only required components that will be no longer required after removing root component. --------- Co-authored-by: a.yamaev <a.yamaev@smartengines.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
336c23c1aa
|
Rename observe to observe_entity on EntityWorldMut (#15616)
# Objective The current observers have some unfortunate footguns where you can end up confused about what is actually being observed. For apps you can chain observe like `app.observe(..).observe(..)` which works like you would expect, but if you try the same with world the first `observe()` will return the `EntityWorldMut` for the created observer, and the second `observe()` will only observe on the observer entity. It took several hours for multiple people on discord to figure this out, which is not a great experience. ## Solution Rename `observe` on entities to `observe_entity`. It's slightly more verbose when you know you have an entity, but it feels right to me that observers for specific things have more specific naming, and it prevents this issue completely. Another possible solution would be to unify `observe` on `App` and `World` to have the same kind of return type, but I'm not sure exactly what that would look like. ## Testing Simple name change, so only concern is docs really. --- ## Migration Guide The `observe()` method on entities has been renamed to `observe_entity()` to prevent confusion about what is being observed in some cases. |
||
![]() |
2da8d17a44
|
Add try_despawn methods to World/Commands (#15480)
# Objective Fixes #14511. `despawn` allows you to remove entities from the world. However, if the entity does not exist, it emits a warning. This may not be intended behavior for many users who have use cases where they need to call `despawn` regardless of if the entity actually exists (see the issue), or don't care in general if the entity already doesn't exist. (Also trying to gauge interest on if this feature makes sense, I'd personally love to have it, but I could see arguments that this might be a footgun. Just trying to help here 😄 If there's no contention I could also implement this for `despawn_recursive` and `despawn_descendants` in the same PR) ## Solution Add `try_despawn`, `try_despawn_recursive` and `try_despawn_descendants`. Modify `World::despawn_with_caller` to also take in a `warn` boolean argument, which is then considered when logging the warning. Set `log_warning` to `true` in the case of `despawn`, and `false` in the case of `try_despawn`. ## Testing Ran `cargo run -p ci` on macOS, it seemed fine. |
||
![]() |
461305b3d7
|
Revert "Have EntityCommands methods consume self for easier chaining" (#15523)
As discussed in #15521 - Partial revert of #14897, reverting the change to the methods to consume `self` - The `insert_if` method is kept The migration guide of #14897 should be removed Closes #15521 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
29edad4690
|
Improve unclear docs about spawn(_batch) and ParallelCommands (#15491)
> [!NOTE] > This is my first PR, so if something is incorrect > or missing, please let me know :3 # Objective - Clarifies `spawn`, `spawn_batch` and `ParallelCommands` docs about performance and use cases - Fixes #15472 ## Solution Add comments to `spawn`, `spawn_batch` and `ParallelCommands` to clarify the intended use case and link to other/better ways of doing spawning things for certain use cases. |
||
![]() |
d70595b667
|
Add core and alloc over std Lints (#15281)
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
fb9aaa1527
|
Follow up to cached run_system (#15410)
# Objective - Fixes #15373 - Fixes https://github.com/bevyengine/bevy/pull/14920#issuecomment-2370428013 ## Solution - Make `IntoSystem::pipe` and `IntoSystem::map` return two new (possibly-ZST) types that implement `IntoSystem` and whose `into_system` method return the systems that were previously being returned by `IntoSystem::pipe` and `IntoSystem::map` - Don't eagerly call `IntoSystem::into_system` on the argument given to `RunSystemCachedWith::new` to avoid losing its ZST-ness ## Testing - Added a regression test for each issue ## Migration Guide - `IntoSystem::pipe` and `IntoSystem::map` now return `IntoPipeSystem` and `IntoAdapterSystem` instead of `PipeSystem` and `AdapterSystem`. Most notably these types don't implement `System` but rather only `IntoSystem`. |
||
![]() |
efda7f3f9c
|
Simpler lint fixes: makes ci lints work but disables a lint for now (#15376)
Takes the first two commits from #15375 and adds suggestions from this comment: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366968300 See #15375 for more reasoning/motivation. ## Rebasing (rerunning) ```rust git switch simpler-lint-fixes git reset --hard main cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "rustfmt" cargo clippy --workspace --all-targets --all-features --fix cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "clippy" git cherry-pick e6c0b94f6795222310fb812fa5c4512661fc7887 ``` |
||
![]() |
c7ec456e50
|
Support systems that take references as input (#15184)
# Objective - Fixes #14924 - Closes #9584 ## Solution - We introduce a new trait, `SystemInput`, that serves as a type function from the `'static` form of the input, to its lifetime'd version, similarly to `SystemParam` or `WorldQuery`. - System functions now take the lifetime'd wrapped version, `SystemInput::Param<'_>`, which prevents the issue presented in #14924 (i.e. `InRef<T>`). - Functions for running systems now take the lifetime'd unwrapped version, `SystemInput::Inner<'_>` (i.e. `&T`). - Due to the above change, system piping had to be re-implemented as a standalone type, rather than `CombinatorSystem` as it was previously. - Removes the `Trigger<'static, E, B>` transmute in observer runner code. ## Testing - All current tests pass. - Added additional tests and doc-tests. --- ## Showcase ```rust let mut world = World::new(); let mut value = 2; // Currently possible: fn square(In(input): In<usize>) -> usize { input * input } value = world.run_system_once_with(value, square); // Now possible: fn square_mut(InMut(input): InMut<usize>) { *input *= *input; } world.run_system_once_with(&mut value, square_mut); // Or: fn square_ref(InRef(input): InRef<usize>) -> usize { *input * *input } value = world.run_system_once_with(&value, square_ref); ``` ## Migration Guide - All current explicit usages of the following types must be changed in the way specified: - `SystemId<I, O>` to `SystemId<In<I>, O>` - `System<In = T>` to `System<In = In<T>>` - `IntoSystem<I, O, M>` to `IntoSystem<In<I>, O, M>` - `Condition<M, T>` to `Condition<M, In<T>>` - `In<Trigger<E, B>>` is no longer a valid input parameter type. Use `Trigger<E, B>` directly, instead. --------- Co-authored-by: Giacomo Stevanato <giaco.stevanato@gmail.com> |
||
![]() |
e312da8c52
|
Reduce runtime panics through SystemParam validation (#15276)
# Objective The goal of this PR is to introduce `SystemParam` validation in order to reduce runtime panics. Fixes #15265 ## Solution `SystemParam` now has a new method `validate_param(...) -> bool`, which takes immutable variants of `get_param` arguments. The returned value indicates whether the parameter can be acquired from the world. If parameters cannot be acquired for a system, it won't be executed, similarly to run conditions. This reduces panics when using params like `Res`, `ResMut`, etc. as well as allows for new, ergonomic params like #15264 or #15302. Param validation happens at the level of executors. All validation happens directly before executing a system, in case of normal systems they are skipped, in case of conditions they return false. Warning about system skipping is primitive and subject to change in subsequent PRs. ## Testing Two executor tests check that all executors: - skip systems which have invalid parameters: - piped systems get skipped together, - dependent systems still run correctly, - skip systems with invalid run conditions: - system conditions have invalid parameters, - system set conditions have invalid parameters. |
||
![]() |
f78856b3bd
|
Add cached run_system API (#14920)
# Objective
Working with `World` is painful due to lifetime issues and a lack of
ergonomics, so you may want to delegate to the system API. Your current
options are:
- `world.run_system_once`, which initializes the system each time it's
called (performance cost) and doesn't support `Local`. The docs
recommend users not use this method outside of diagnostic use cases like
unit tests.
- `world.run_system`, which requires you to register the system and
store the `SystemId` somewhere (made easier by implementing `FromWorld`
for a newtyped `Local`, unless you're in e.g. a custom `Command` impl).
These options work, but you're choosing between a performance cost and
an ergonomic challenge.
## Solution
Provide a cached `run_system` API that accepts an `S: IntoSystem` and
checks for a `CachedSystemId<S::System>(SystemId)` resource. If it
doesn't exist, it will register the system and save its `SystemId` in
that resource.
In other words, it hides the "save the `SystemId` in a `Local` or
`Resource`" pattern as an implementation detail.
Prior work: https://github.com/bevyengine/bevy/pull/10469.
## Testing
This approach worked in a proof-of-concept:
|