8a918a8915
21 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3aed85a88b
|
Rename send_event and similar methods to write_event (#20017)
Fixes: #18963 Follows up on: #17977 Adopts: #18966 In 0.16, `EventWriter::send` was renamed to `EventWriter::write`, but many methods were missed (sorry about that). This completes that refactor by renaming all `send` methods and internals. | Old | New | |-------------------------------------|--------------------------------------| | `World::send_event` | `World::write_event` | | `World::send_event_default` | `World::write_event_default` | | `World::send_event_batch` | `World::write_event_batch` | | `DeferredWorld::send_event` | `DeferredWorld::write_event` | | `DeferredWorld::send_event_default` | `DeferredWorld::write_event_default` | | `DeferredWorld::send_event_batch` | `DeferredWorld::write_event_batch` | | `Commands::send_event` | `Commmands::write_event` | | `Events::send` | `Events::write` | | `Events::send_default` | `Events::write_default` | | `Events::send_batch` | `Events::write_batch` | | `RemovedComponentEvents::send` | `RemovedComponentEvents::write` | | `command::send_event` | `commmand::write_event` | | `SendBatchIds` | `WriteBatchIds` | --------- Co-authored-by: shwwwa <shwwwa.dev@gmail.com> |
||
|
|
38c3423693
|
Event Split: Event, EntityEvent, and BufferedEvent (#19647)
# Objective Closes #19564. The current `Event` trait looks like this: ```rust pub trait Event: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` The `Event` trait is used by both buffered events (`EventReader`/`EventWriter`) and observer events. If they are observer events, they can optionally be targeted at specific `Entity`s or `ComponentId`s, and can even be propagated to other entities. However, there has long been a desire to split the trait semantically for a variety of reasons, see #14843, #14272, and #16031 for discussion. Some reasons include: - It's very uncommon to use a single event type as both a buffered event and targeted observer event. They are used differently and tend to have distinct semantics. - A common footgun is using buffered events with observers or event readers with observer events, as there is no type-level error that prevents this kind of misuse. - #19440 made `Trigger::target` return an `Option<Entity>`. This *seriously* hurts ergonomics for the general case of entity observers, as you need to `.unwrap()` each time. If we could statically determine whether the event is expected to have an entity target, this would be unnecessary. There's really two main ways that we can categorize events: push vs. pull (i.e. "observer event" vs. "buffered event") and global vs. targeted: | | Push | Pull | | ------------ | --------------- | --------------------------- | | **Global** | Global observer | `EventReader`/`EventWriter` | | **Targeted** | Entity observer | - | There are many ways to approach this, each with their tradeoffs. Ultimately, we kind of want to split events both ways: - A type-level distinction between observer events and buffered events, to prevent people from using the wrong kind of event in APIs - A statically designated entity target for observer events to avoid accidentally using untargeted events for targeted APIs This PR achieves these goals by splitting event traits into `Event`, `EntityEvent`, and `BufferedEvent`, with `Event` being the shared trait implemented by all events. ## `Event`, `EntityEvent`, and `BufferedEvent` `Event` is now a very simple trait shared by all events. ```rust pub trait Event: Send + Sync + 'static { // Required for observer APIs fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` You can call `trigger` for *any* event, and use a global observer for listening to the event. ```rust #[derive(Event)] struct Speak { message: String, } // ... app.add_observer(|trigger: On<Speak>| { println!("{}", trigger.message); }); // ... commands.trigger(Speak { message: "Y'all like these reworked events?".to_string(), }); ``` To allow an event to be targeted at entities and even propagated further, you can additionally implement the `EntityEvent` trait: ```rust pub trait EntityEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This lets you call `trigger_targets`, and to use targeted observer APIs like `EntityCommands::observe`: ```rust #[derive(Event, EntityEvent)] #[entity_event(traversal = &'static ChildOf, auto_propagate)] struct Damage { amount: f32, } // ... let enemy = commands.spawn((Enemy, Health(100.0))).id(); // Spawn some armor as a child of the enemy entity. // When the armor takes damage, it will bubble the event up to the enemy. let armor_piece = commands .spawn((ArmorPiece, Health(25.0), ChildOf(enemy))) .observe(|trigger: On<Damage>, mut query: Query<&mut Health>| { // Note: `On::target` only exists because this is an `EntityEvent`. let mut health = query.get(trigger.target()).unwrap(); health.0 -= trigger.amount(); }); commands.trigger_targets(Damage { amount: 10.0 }, armor_piece); ``` > [!NOTE] > You *can* still also trigger an `EntityEvent` without targets using `trigger`. We probably *could* make this an either-or thing, but I'm not sure that's actually desirable. To allow an event to be used with the buffered API, you can implement `BufferedEvent`: ```rust pub trait BufferedEvent: Event {} ``` The event can then be used with `EventReader`/`EventWriter`: ```rust #[derive(Event, BufferedEvent)] struct Message(String); fn write_hello(mut writer: EventWriter<Message>) { writer.write(Message("I hope these examples are alright".to_string())); } fn read_messages(mut reader: EventReader<Message>) { // Process all buffered events of type `Message`. for Message(message) in reader.read() { println!("{message}"); } } ``` In summary: - Need a basic event you can trigger and observe? Derive `Event`! - Need the event to be targeted at an entity? Derive `EntityEvent`! - Need the event to be buffered and support the `EventReader`/`EventWriter` API? Derive `BufferedEvent`! ## Alternatives I'll now cover some of the alternative approaches I have considered and briefly explored. I made this section collapsible since it ended up being quite long :P <details> <summary>Expand this to see alternatives</summary> ### 1. Unified `Event` Trait One option is not to have *three* separate traits (`Event`, `EntityEvent`, `BufferedEvent`), and to instead just use associated constants on `Event` to determine whether an event supports targeting and buffering or not: ```rust pub trait Event: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; const TARGETED: bool = false; const BUFFERED: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` Methods can then use bounds like `where E: Event<TARGETED = true>` or `where E: Event<BUFFERED = true>` to limit APIs to specific kinds of events. This would keep everything under one `Event` trait, but I don't think it's necessarily a good idea. It makes APIs harder to read, and docs can't easily refer to specific types of events. You can also create weird invariants: what if you specify `TARGETED = false`, but have `Traversal` and/or `AUTO_PROPAGATE` enabled? ### 2. `Event` and `Trigger` Another option is to only split the traits between buffered events and observer events, since that is the main thing people have been asking for, and they have the largest API difference. If we did this, I think we would need to make the terms *clearly* separate. We can't really use `Event` and `BufferedEvent` as the names, since it would be strange that `BufferedEvent` doesn't implement `Event`. Something like `ObserverEvent` and `BufferedEvent` could work, but it'd be more verbose. For this approach, I would instead keep `Event` for the current `EventReader`/`EventWriter` API, and call the observer event a `Trigger`, since the "trigger" terminology is already used in the observer context within Bevy (both as a noun and a verb). This is also what a long [bikeshed on Discord](https://discord.com/channels/691052431525675048/749335865876021248/1298057661878898791) seemed to land on at the end of last year. ```rust // For `EventReader`/`EventWriter` pub trait Event: Send + Sync + 'static {} // For observers pub trait Trigger: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; const TARGETED: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` The problem is that "event" is just a really good term for something that "happens". Observers are rapidly becoming the more prominent API, so it'd be weird to give them the `Trigger` name and leave the good `Event` name for the less common API. So, even though a split like this seems neat on the surface, I think it ultimately wouldn't really work. We want to keep the `Event` name for observer events, and there is no good alternative for the buffered variant. (`Message` was suggested, but saying stuff like "sends a collision message" is weird.) ### 3. `GlobalEvent` + `TargetedEvent` What if instead of focusing on the buffered vs. observed split, we *only* make a distinction between global and targeted events? ```rust // A shared event trait to allow global observers to work pub trait Event: Send + Sync + 'static { fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } // For buffered events and non-targeted observer events pub trait GlobalEvent: Event {} // For targeted observer events pub trait TargetedEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This is actually the first approach I implemented, and it has the neat characteristic that you can only use non-targeted APIs like `trigger` with a `GlobalEvent` and targeted APIs like `trigger_targets` with a `TargetedEvent`. You have full control over whether the entity should or should not have a target, as they are fully distinct at the type-level. However, there's a few problems: - There is no type-level indication of whether a `GlobalEvent` supports buffered events or just non-targeted observer events - An `Event` on its own does literally nothing, it's just a shared trait required to make global observers accept both non-targeted and targeted events - If an event is both a `GlobalEvent` and `TargetedEvent`, global observers again have ambiguity on whether an event has a target or not, undermining some of the benefits - The names are not ideal ### 4. `Event` and `EntityEvent` We can fix some of the problems of Alternative 3 by accepting that targeted events can also be used in non-targeted contexts, and simply having the `Event` and `EntityEvent` traits: ```rust // For buffered events and non-targeted observer events pub trait Event: Send + Sync + 'static { fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } // For targeted observer events pub trait EntityEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This is essentially identical to this PR, just without a dedicated `BufferedEvent`. The remaining major "problem" is that there is still zero type-level indication of whether an `Event` event *actually* supports the buffered API. This leads us to the solution proposed in this PR, using `Event`, `EntityEvent`, and `BufferedEvent`. </details> ## Conclusion The `Event` + `EntityEvent` + `BufferedEvent` split proposed in this PR aims to solve all the common problems with Bevy's current event model while keeping the "weirdness" factor minimal. It splits in terms of both the push vs. pull *and* global vs. targeted aspects, while maintaining a shared concept for an "event". ### Why I Like This - The term "event" remains as a single concept for all the different kinds of events in Bevy. - Despite all event types being "events", they use fundamentally different APIs. Instead of assuming that you can use an event type with any pattern (when only one is typically supported), you explicitly opt in to each one with dedicated traits. - Using separate traits for each type of event helps with documentation and clearer function signatures. - I can safely make assumptions on expected usage. - If I see that an event is an `EntityEvent`, I can assume that I can use `observe` on it and get targeted events. - If I see that an event is a `BufferedEvent`, I can assume that I can use `EventReader` to read events. - If I see both `EntityEvent` and `BufferedEvent`, I can assume that both APIs are supported. In summary: This allows for a unified concept for events, while limiting the different ways to use them with opt-in traits. No more guess-work involved when using APIs. ### Problems? - Because `BufferedEvent` implements `Event` (for more consistent semantics etc.), you can still use all buffered events for non-targeted observers. I think this is fine/good. The important part is that if you see that an event implements `BufferedEvent`, you know that the `EventReader`/`EventWriter` API should be supported. Whether it *also* supports other APIs is secondary. - I currently only support `trigger_targets` for an `EntityEvent`. However, you can technically target components too, without targeting any entities. I consider that such a niche and advanced use case that it's not a huge problem to only support it for `EntityEvent`s, but we could also split `trigger_targets` into `trigger_entities` and `trigger_components` if we wanted to (or implement components as entities :P). - You can still trigger an `EntityEvent` *without* targets. I consider this correct, since `Event` implements the non-targeted behavior, and it'd be weird if implementing another trait *removed* behavior. However, it does mean that global observers for entity events can technically return `Entity::PLACEHOLDER` again (since I got rid of the `Option<Entity>` added in #19440 for ergonomics). I think that's enough of an edge case that it's not a huge problem, but it is worth keeping in mind. - ~~Deriving both `EntityEvent` and `BufferedEvent` for the same type currently duplicates the `Event` implementation, so you instead need to manually implement one of them.~~ Changed to always requiring `Event` to be derived. ## Related Work There are plans to implement multi-event support for observers, especially for UI contexts. [Cart's example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508) API looked like this: ```rust // Truncated for brevity trigger: Trigger<( OnAdd<Pressed>, OnRemove<Pressed>, OnAdd<InteractionDisabled>, OnRemove<InteractionDisabled>, OnInsert<Hovered>, )>, ``` I believe this shouldn't be in conflict with this PR. If anything, this PR might *help* achieve the multi-event pattern for entity observers with fewer footguns: by statically enforcing that all of these events are `EntityEvent`s in the context of `EntityCommands::observe`, we can avoid misuse or weird cases where *some* events inside the trigger are targeted while others are not. |
||
|
|
9b32e09551
|
bevy_reflect: Add clone registrations project-wide (#18307)
# Objective Now that #13432 has been merged, it's important we update our reflected types to properly opt into this feature. If we do not, then this could cause issues for users downstream who want to make use of reflection-based cloning. ## Solution This PR is broken into 4 commits: 1. Add `#[reflect(Clone)]` on all types marked `#[reflect(opaque)]` that are also `Clone`. This is mandatory as these types would otherwise cause the cloning operation to fail for any type that contains it at any depth. 2. Update the reflection example to suggest adding `#[reflect(Clone)]` on opaque types. 3. Add `#[reflect(clone)]` attributes on all fields marked `#[reflect(ignore)]` that are also `Clone`. This prevents the ignored field from causing the cloning operation to fail. Note that some of the types that contain these fields are also `Clone`, and thus can be marked `#[reflect(Clone)]`. This makes the `#[reflect(clone)]` attribute redundant. However, I think it's safer to keep it marked in the case that the `Clone` impl/derive is ever removed. I'm open to removing them, though, if people disagree. 4. Finally, I added `#[reflect(Clone)]` on all types that are also `Clone`. While not strictly necessary, it enables us to reduce the generated output since we can just call `Clone::clone` directly instead of calling `PartialReflect::reflect_clone` on each variant/field. It also means we benefit from any optimizations or customizations made in the `Clone` impl, including directly dereferencing `Copy` values and increasing reference counters. Along with that change I also took the liberty of adding any missing registrations that I saw could be applied to the type as well, such as `Default`, `PartialEq`, and `Hash`. There were hundreds of these to edit, though, so it's possible I missed quite a few. That last commit is **_massive_**. There were nearly 700 types to update. So it's recommended to review the first three before moving onto that last one. Additionally, I can break the last commit off into its own PR or into smaller PRs, but I figured this would be the easiest way of doing it (and in a timely manner since I unfortunately don't have as much time as I used to for code contributions). ## Testing You can test locally with a `cargo check`: ``` cargo check --workspace --all-features ``` |
||
|
|
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> |
||
|
|
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. |
||
|
|
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> |
||
|
|
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`. |
||
|
|
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. |
||
|
|
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> |
||
|
|
cc0f6a8db4
|
Remove deprecated ECS items (#16853)
# Objective - Cleanup deprecated code ## Solution - Removed `#[deprecated]` items which were marked as such in 0.15 or prior versions. ## Migration Guide - The following deprecated items were removed: `Events::get_reader`, `Events::get_reader_current`, `ManualEventReader`, `Condition::and_then`, `Condition::or_else`, `World::,many_entities`, `World::many_entities_mut`, `World::get_many_entities`, `World::get_many_entities_dynamic`, `World::get_many_entities_mut`, `World::get_many_entities_dynamic_mut`, `World::get_many_entities_from_set_mut` |
||
|
|
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> |
||
|
|
2e89e98931
|
Deprecate Events::oldest_id (#15658)
# Objective Fixes #15617 ## Solution The original author confirmed it was not intentional that both these methods exist. They do the same, one has the better implementation and the other the better name. ## Testing I just ran the unit tests of the module. --- ## Migration Guide - Change usages of `Events::oldest_id` to `Events::oldest_event_count` - If `Events::oldest_id` was used to get the actual oldest `EventId::id`, note that the deprecated method never reliably did that in the first place as the buffers may contain no id currently. |
||
|
|
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> |
||
|
|
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 ``` |
||
|
|
21e39360f7
|
Example for bevy_ecs::event::Events uses deprecated function get_reader (#15216)
Fixes https://github.com/bevyengine/bevy/issues/15214 |
||
|
|
53d2bc9482
|
Reflect derived traits on all components and resources: bevy_ecs (#15215)
Solves https://github.com/bevyengine/bevy/issues/15187 for bevy_ecs |
||
|
|
d7080369a7
|
Fix intra-doc links and make CI test them (#14076)
# Objective - Bevy currently has lot of invalid intra-doc links, let's fix them! - Also make CI test them, to avoid future regressions. - Helps with #1983 (but doesn't fix it, as there could still be explicit links to docs.rs that are broken) ## Solution - Make `cargo r -p ci -- doc-check` check fail on warnings (could also be changed to just some specific lints) - Manually fix all the warnings (note that in some cases it was unclear to me what the fix should have been, I'll try to highlight them in a self-review) |
||
|
|
ec1aa48fc6
|
Created an EventMutator for when you want to mutate an event before reading (#13818)
# Objective - Often in games you will want to create chains of systems that modify some event. For example, a chain of damage systems that handle a DamageEvent and modify the underlying value before the health system finally consumes the event. Right now this requires either: * Using a component added to the entity * Consuming and refiring events Neither is ideal when really all we want to do is read the events value, modify it, and write it back. ## Solution - Create an EventMutator class similar to EventReader but with ResMut<T> and iterators that return &mut so that events can be mutated. ## Testing - I replicated all the existing tests for EventReader to make sure behavior was the same (I believe) and added a number of tests specific to testing that 1) events can actually be mutated, and that 2) EventReader sees changes from EventMutator for events it hasn't already seen. ## Migration Guide Users currently using `ManualEventReader` should use `EventCursor` instead. `ManualEventReader` will be removed in Bevy 0.16. Additionally, `Events::get_reader` has been replaced by `Events::get_cursor`. Users currently directly accessing the `Events` resource for mutation should move to `EventMutator` if possible. --------- Co-authored-by: poopy <gonesbird@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
6273227e09
|
Fix lints introduced in Rust beta 1.80 (#13899)
Resolves #13895 Mostly just involves being more explicit about which parts of the docs belong to a list and which begin a new paragraph. - found a few docs that were malformed because of exactly this, so I fixed that by introducing a paragraph - added indentation to nearly all multiline lists - fixed a few minor typos - added `#[allow(dead_code)]` to types that are needed to test annotations but are never constructed ([here](https://github.com/bevyengine/bevy/pull/13899/files#diff-b02b63604e569c8577c491e7a2030d456886d8f6716eeccd46b11df8aac75dafR1514) and [here](https://github.com/bevyengine/bevy/pull/13899/files#diff-b02b63604e569c8577c491e7a2030d456886d8f6716eeccd46b11df8aac75dafR1523)) - verified that `cargo +beta run -p ci -- lints` passes - verified that `cargo +beta run -p ci -- test` passes |
||
|
|
2cffd14923
|
Ensure that events are updated even when using a bare-bones Bevy App (#13808)
# Objective As discovered in https://github.com/Leafwing-Studios/leafwing-input-manager/issues/538, there appears to be some real weirdness going on in how event updates are processed between Bevy 0.13 and Bevy 0.14. To identify the cause and prevent regression, I've added tests to validate the intended behavior. My initial suspicion was that this would be fixed by https://github.com/bevyengine/bevy/pull/13762, but that doesn't seem to be the case. Instead, events appear to never be updated at all when using `bevy_app` by itself. This is part of the problem resolved by https://github.com/bevyengine/bevy/pull/11528, and introduced by https://github.com/bevyengine/bevy/pull/10077. After some investigation, it appears that `signal_event_update_system` is never added using a bare-bones `App`, and so event updates are always skipped. This can be worked around by adding your own copy to a later-in-the-frame schedule, but that's not a very good fix. ## Solution Ensure that if we're not using a `FixedUpdate` schedule, events are always updated every frame. To do this, I've modified the logic of `event_update_condition` and `event_update_system` to clearly and correctly differentiate between the two cases: where we're waiting for a "you should update now" signal and where we simply don't care. To encode this, I've added the `ShouldUpdateEvents` enum, replacing a simple `bool` in `EventRegistry`'s `needs_update` field. Now, both tests pass as expected, without having to manually add a system! ## Testing I've written two parallel unit tests to cover the intended behavior: 1. Test that `iter_current_update_events` works as expected in `bevy_ecs`. 2. Test that `iter_current_update_events` works as expected in `bevy_app` I've also added a test to verify that event updating works correctly in the presence of a fixed main schedule, and a second test to verify that fixed updating works at all to help future authors narrow down failures. ## Outstanding - [x] figure out why the `bevy_app` version of this test fails but the `bevy_ecs` version does not - [x] figure out why `EventRegistry::run_updates` isn't working properly - [x] figure out why `EventRegistry::run_updates` is never getting called - [x] figure out why `event_update_condition` is always returning false - [x] figure out why `EventRegistry::needs_update` is always false - [x] verify that the problem is a missing `signal_events_update_system` --------- Co-authored-by: Mike <mike.hsu@gmail.com> |
||
|
|
2ccdae7489
|
Split event.rs into a full module. (#13801)
# Objective - Split the bevy_ecs::events module so it's easier to work with ## Solution - Split the event.rs file across multiple files, made sure all tests passed, and exports from the module were the same as previous ## Testing - All automated tests pass. |