Commit Graph

48 Commits

Author SHA1 Message Date
Tim Blackbird
c697e92439 Adjust derives 2025-07-12 15:28:32 +02:00
Tim Blackbird
2721e7f1de Split BufferedEvent from Event 2025-07-12 15:28:18 +02:00
AlephCubed
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>
2025-07-07 22:05:16 +00:00
Daniel Skates
560429ebd9
Observer trigger refactor (#19935)
# Objective

- The usage of ComponentId is quite confusing: events are not
components. By newtyping this, we can prevent stupid mistakes, avoid
leaking internal details and make the code clearer for users and engine
devs reading it.
- Adopts https://github.com/bevyengine/bevy/pull/19755

---------

Co-authored-by: oscar-benderstone <oscarbenderstone@gmail.com>
Co-authored-by: Oscar Bender-Stone <88625129+oscar-benderstone@users.noreply.github.com>
2025-07-04 16:27:21 +00:00
Chris Russell
6e918f56d8
Have System::run_unsafe return Result. (#19145)
# Objective

Allow combinator and pipe systems to delay validation of the second
system, while still allowing the second system to be skipped.

Fixes #18796

Allow fallible systems to be used as one-shot systems, reporting errors
to the error handler when used through commands.

Fixes #19722

Allow fallible systems to be used as run conditions, including when used
with combinators. Alternative to #19580.

Always validate parameters when calling the safe
`run_without_applying_deferred`, `run`, and `run_readonly` methods on a
`System`.

## Solution

Have `System::run_unsafe` return a `Result`.  

We want pipe systems to run the first system before validating the
second, since the first system may affect whether the second system has
valid parameters. But if the second system skips then we have no output
value to return! So, pipe systems must return a `Result` that indicates
whether the second system ran.

But if we just make pipe systems have `Out = Result<B::Out>`, then
chaining `a.pipe(b).pipe(c)` becomes difficult. `c` would need to accept
the `Result` from `a.pipe(b)`, which means it would likely need to
return `Result` itself, giving `Result<Result<Out>>`!

Instead, we make *all* systems return a `Result`! We move the handling
of fallible systems from `IntoScheduleConfigs` and `IntoObserverSystem`
to `SystemParamFunction` and `ExclusiveSystemParamFunction`, so that an
infallible system can be wrapped before being passed to a combinator.

As a side effect, this enables fallible systems to be used as run
conditions and one-shot systems.

Now that the safe `run_without_applying_deferred`, `run`, and
`run_readonly` methods return a `Result`, we can have them perform
parameter validation themselves instead of requiring each caller to
remember to call them. `run_unsafe` will continue to not validate
parameters, since it is used in the multi-threaded executor when we want
to validate and run in separate tasks.

Note that this makes type inference a little more brittle. A function
that returns `Result<T>` can be considered either a fallible system
returning `T` or an infallible system returning `Result<T>` (and this is
important to continue supporting `pipe`-based error handling)! So there
are some cases where the output type of a system can no longer be
inferred. It will work fine when directly adding to a schedule, since
then the output type is fixed to `()` (or `bool` for run conditions).
And it will work fine when `pipe`ing to a system with a typed input
parameter.

I used a dedicated `RunSystemError` for the error type instead of plain
`BevyError` so that skipping a system does not box an error or capture a
backtrace.
2025-07-03 21:48:09 +00:00
Joona Aalto
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.
2025-06-15 16:46:34 +00:00
Joona Aalto
e5dc177b4b
Rename Trigger to On (#19596)
# Objective

Currently, the observer API looks like this:

```rust
app.add_observer(|trigger: Trigger<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

Future plans for observers also include "multi-event observers" with a
trigger that looks like this (see [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)):

```rust
trigger: Trigger<(
    OnAdd<Pressed>,
    OnRemove<Pressed>,
    OnAdd<InteractionDisabled>,
    OnRemove<InteractionDisabled>,
    OnInsert<Hovered>,
)>,
```

In scenarios like this, there is a lot of repetition of `On`. These are
expected to be very high-traffic APIs especially in UI contexts, so
ergonomics and readability are critical.

By renaming `Trigger` to `On`, we can make these APIs read more cleanly
and get rid of the repetition:

```rust
app.add_observer(|trigger: On<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

```rust
trigger: On<(
    Add<Pressed>,
    Remove<Pressed>,
    Add<InteractionDisabled>,
    Remove<InteractionDisabled>,
    Insert<Hovered>,
)>,
```

Names like `On<Add<Pressed>>` emphasize the actual event listener nature
more than `Trigger<OnAdd<Pressed>>`, and look cleaner. This *also* frees
up the `Trigger` name if we want to use it for the observer event type,
splitting them out from buffered events (bikeshedding this is out of
scope for this PR though).

For prior art:
[`bevy_eventlistener`](https://github.com/aevyrie/bevy_eventlistener)
used
[`On`](https://docs.rs/bevy_eventlistener/latest/bevy_eventlistener/event_listener/struct.On.html)
for its event listener type. Though in our case, the observer is the
event listener, and `On` is just a type containing information about the
triggered event.

## Solution

Steal from `bevy_event_listener` by @aevyrie and use `On`.

- Rename `Trigger` to `On`
- Rename `OnAdd` to `Add`
- Rename `OnInsert` to `Insert`
- Rename `OnReplace` to `Replace`
- Rename `OnRemove` to `Remove`
- Rename `OnDespawn` to `Despawn`

## Discussion

### Naming Conflicts??

Using a name like `Add` might initially feel like a very bad idea, since
it risks conflict with `core::ops::Add`. However, I don't expect this to
be a big problem in practice.

- You rarely need to actually implement the `Add` trait, especially in
modules that would use the Bevy ECS.
- In the rare cases where you *do* get a conflict, it is very easy to
fix by just disambiguating, for example using `ops::Add`.
- The `Add` event is a struct while the `Add` trait is a trait (duh), so
the compiler error should be very obvious.

For the record, renaming `OnAdd` to `Add`, I got exactly *zero* errors
or conflicts within Bevy itself. But this is of course not entirely
representative of actual projects *using* Bevy.

You might then wonder, why not use `Added`? This would conflict with the
`Added` query filter, so it wouldn't work. Additionally, the current
naming convention for observer events does not use past tense.

### Documentation

This does make documentation slightly more awkward when referring to
`On` or its methods. Previous docs often referred to `Trigger::target`
or "sends a `Trigger`" (which is... a bit strange anyway), which would
now be `On::target` and "sends an observer `Event`".

You can see the diff in this PR to see some of the effects. I think it
should be fine though, we may just need to reword more documentation to
read better.
2025-06-12 18:22:33 +00:00
Alice Cecile
6ddd0f16a8
Component lifecycle reorganization and documentation (#19543)
# Objective

I set out with one simple goal: clearly document the differences between
each of the component lifecycle events via module docs.

Unfortunately, no such module existed: the various lifecycle code was
scattered to the wind.
Without a unified module, it's very hard to discover the related types,
and there's nowhere good to put my shiny new documentation.

## Solution

1. Unify the assorted types into a single
`bevy_ecs::component_lifecycle` module.
2. Write docs.
3. Write a migration guide.

## Testing

Thanks CI!

## Follow-up

1. The lifecycle event names are pretty confusing, especially
`OnReplace`. We should consider renaming those. No bikeshedding in my PR
though!
2. Observers need real module docs too :(
3. Any additional functional changes should be done elsewhere; this is a
simple docs and re-org PR.

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-10 00:59:16 +00:00
theotherphil
3690ad5b0b
Remove apostrophes in possessive its (#19244)
# Objective

Fix some grammatical errors: it's -> its

Not the most useful commit in the world, but I saw a couple of these and
decided to fix the lot.

## Solution
-

## Testing
-
2025-05-26 19:53:14 +00:00
Zachary Harrold
63e78fe489
Deprecated Begone! 0.16 Cleanup (#19108)
# Objective

A fair few items were deprecated in 0.16. Let's delete them now that
we're in the 0.17 development cycle!

## Solution

- Deleted items marked deprecated in 0.16.

## Testing

- CI

---

## Notes

I'm making the assumption that _everything_ deprecated in 0.16 should be
removed in 0.17. That may be a false assumption in certain cases. Please
check the items to be removed to see if there are any exceptions we
should keep around for another cycle!
2025-05-07 18:17:41 +00:00
Joona Aalto
7b1c9f192e
Adopt consistent FooSystems naming convention for system sets (#18900)
# Objective

Fixes a part of #14274.

Bevy has an incredibly inconsistent naming convention for its system
sets, both internally and across the ecosystem.

<img alt="System sets in Bevy"
src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b"
width="450" />

*Names of public system set types in Bevy*

Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are
also a few `FooSystems` and `FooSet` types. In ecosystem crates on the
other hand, `FooSet` is perhaps the most commonly used name in general.
Conventions being so wildly inconsistent can make it harder for users to
pick names for their own types, to search for system sets on docs.rs, or
to even discern which types *are* system sets.

To reign in the inconsistency a bit and help unify the ecosystem, it
would be good to establish a common recommended naming convention for
system sets in Bevy itself, similar to how plugins are commonly suffixed
with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming
convention in first-party Bevy, we can softly nudge ecosystem crates to
follow suit (for types where it makes sense to do so).

Choosing a naming convention is also relevant now, as the [`bevy_cli`
recently adopted
lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce
naming for plugins and system sets, and the recommended naming used for
system sets is still a bit open.

## Which Name To Use?

Now the contentious part: what naming convention should we actually
adopt?

This was discussed on the Bevy Discord at the end of last year, starting
[here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>).
`FooSet` and `FooSystems` were the clear favorites, with `FooSet` very
narrowly winning an unofficial poll. However, it seems to me like the
consensus was broadly moving towards `FooSystems` at the end and after
the poll, with Cart
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708))
and later Alice
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533))
and also me being in favor of it.

Let's do a quick pros and cons list! Of course these are just what I
thought of, so take it with a grain of salt.

`FooSet`:

- Pro: Nice and short!
- Pro: Used by many ecosystem crates.
- Pro: The `Set` suffix comes directly from the trait name `SystemSet`.
- Pro: Pairs nicely with existing APIs like `in_set` and
`configure_sets`.
- Con: `Set` by itself doesn't actually indicate that it's related to
systems *at all*, apart from the implemented trait. A set of what?
- Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex:
`ContactSet`, `MeshSet`, `EnemySet`...

`FooSystems`:

- Pro: Very clearly indicates that the type represents a collection of
systems. The actual core concept, system(s), is in the name.
- Pro: Parallels nicely with `FooPlugins` for plugin groups.
- Pro: Low risk of conflicts with other names or misunderstandings about
what the type is.
- Pro: In most cases, reads *very* nicely and clearly. Ex:
`PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and
`AnimationSet`.
- Pro: Easy to search for on docs.rs.
- Con: Usually results in longer names.
- Con: Not yet as widely used.

Really the big problem with `FooSet` is that it doesn't actually
describe what it is. It describes what *kind of thing* it is (a set of
something), but not *what it is a set of*, unless you know the type or
check its docs or implemented traits. `FooSystems` on the other hand is
much more self-descriptive in this regard, at the cost of being a bit
longer to type.

Ultimately, in some ways it comes down to preference and how you think
of system sets. Personally, I was originally in favor of `FooSet`, but
have been increasingly on the side of `FooSystems`, especially after
seeing what the new names would actually look like in Avian and now
Bevy. I prefer it because it usually reads better, is much more clearly
related to groups of systems than `FooSet`, and overall *feels* more
correct and natural to me in the long term.

For these reasons, and because Alice and Cart also seemed to share a
preference for it when it was previously being discussed, I propose that
we adopt a `FooSystems` naming convention where applicable.

## Solution

Rename Bevy's system set types to use a consistent `FooSet` naming where
applicable.

- `AccessibilitySystem` → `AccessibilitySystems`
- `GizmoRenderSystem` → `GizmoRenderSystems`
- `PickSet` → `PickingSystems`
- `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems`
- `TransformSystem` → `TransformSystems`
- `RemoteSet` → `RemoteSystems`
- `RenderSet` → `RenderSystems`
- `SpriteSystem` → `SpriteSystems`
- `StateTransitionSteps` → `StateTransitionSystems`
- `RenderUiSystem` → `RenderUiSystems`
- `UiSystem` → `UiSystems`
- `Animation` → `AnimationSystems`
- `AssetEvents` → `AssetEventSystems`
- `TrackAssets` → `AssetTrackingSystems`
- `UpdateGizmoMeshes` → `GizmoMeshSystems`
- `InputSystem` → `InputSystems`
- `InputFocusSet` → `InputFocusSystems`
- `ExtractMaterialsSet` → `MaterialExtractionSystems`
- `ExtractMeshesSet` → `MeshExtractionSystems`
- `RumbleSystem` → `RumbleSystems`
- `CameraUpdateSystem` → `CameraUpdateSystems`
- `ExtractAssetsSet` → `AssetExtractionSystems`
- `Update2dText` → `Text2dUpdateSystems`
- `TimeSystem` → `TimeSystems`
- `AudioPlaySet` → `AudioPlaybackSystems`
- `SendEvents` → `EventSenderSystems`
- `EventUpdates` → `EventUpdateSystems`

A lot of the names got slightly longer, but they are also a lot more
consistent, and in my opinion the majority of them read much better. For
a few of the names I took the liberty of rewording things a bit;
definitely open to any further naming improvements.

There are still also cases where the `FooSystems` naming doesn't really
make sense, and those I left alone. This primarily includes system sets
like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`,
or `TransitionSchedules<S>`, where the type has some special purpose and
semantics.

## Todo

- [x] Should I keep all the old names as deprecated type aliases? I can
do this, but to avoid wasting work I'd prefer to first reach consensus
on whether these renames are even desired.
- [x] Migration guide
- [x] Release notes
2025-05-06 15:18:03 +00:00
Chris Russell
9e240ee99a
Improve error message for missing events (#18683)
# Objective

Improve the parameter validation error message for
`Event(Reader|Writer|Mutator)`.

System parameters defined using `#[derive(SystemParam)]`, including the
parameters for events, currently propagate the validation errors from
their subparameters. The error includes the type of the failing
parameter, so the resulting error includes the type of the failing
subparameter instead of the derived parameter.

In particular, `EventReader<T>` will report an error from a
`Res<Events<T>>`, even though the user has no parameter of that type!

This is a follow-up to #18593.

## Solution

Have `#[derive]`d system parameters map errors during propagation so
that they report the outer parameter type.

To continue to provide context, add a field to
`SystemParamValidationError` that identifies the subparameter by name,
and is empty for non-`#[derive]`d parameters.

Allow them to override the failure message for individual parameters.
Use this to convert "Resource does not exist" to "Event not initialized"
for `Event(Reader|Writer|Mutator)`.

## Showcase

The validation error for a `EventReader<SomeEvent>` parameter when
`add_event` has not been called changes from:

Before: 
```
Parameter `Res<Events<SomeEvent>>` failed validation: Resource does not exist
```

After
```
Parameter `EventReader<SomeEvent>::events` failed validation: Event not initialized
```
2025-04-02 19:25:48 +00:00
Martín Maita
5973ba418f
Bump crate-ci/typos from 1.30.2 to 1.31.0 (#18656)
# Objective

- Fixes #18642

## Solution

- Bumped crate-ci/typos from 1.30.2 to 1.31.0.
- Fixed typos.

## Testing

- Typos were fixed.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 10:59:15 +00:00
Gino Valente
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
```
2025-03-17 18:32:35 +00:00
Tim Overbeek
664000f848
Improve derive(Event) and simplify macro code (#18083)
# Objective

simplify some code and improve Event macro

Closes https://github.com/bevyengine/bevy/issues/14336,


# Showcase

you can now write derive Events like so
```rust
#[derive(event)]
#[event(auto_propagate, traversal = MyType)]
struct MyEvent;
```
2025-03-07 02:01:23 +00:00
Zachary Harrold
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>
2025-02-24 03:54:47 +00:00
AlephCubed
5f86668bbb
Renamed EventWriter::send methods to write. (#17977)
Fixes #17856.

## Migration Guide
- `EventWriter::send` has been renamed to `EventWriter::write`.
- `EventWriter::send_batch` has been renamed to
`EventWriter::write_batch`.
- `EventWriter::send_default` has been renamed to
`EventWriter::write_default`.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-02-23 21:18:52 +00:00
Chris Russell
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.
2025-02-10 21:21:20 +00:00
raldone01
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>
2025-02-09 19:45:45 +00:00
Alice Cecile
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`.
2025-01-21 19:47:08 +00:00
Alice Cecile
237c6b207e
Remove Event: Component trait bound using a wrapper type which impls Component (#17380)
# Objective

As raised in https://github.com/bevyengine/bevy/pull/17317, the `Event:
Component` trait bound is confusing to users.

In general, a type `E` (like `AppExit`) which implements `Event` should
not:

- be stored as a component on an entity
- be a valid option for `Query<&AppExit>`
- require the storage type and other component metadata to be specified

Events are not components (even if they one day use some of the same
internal mechanisms), and this trait bound is confusing to users.

We're also automatically generating `Component` impls with our derive
macro, which should be avoided when possible to improve explicitness and
avoid conflicts with user impls.

Closes #17317, closes #17333

## Solution

- We only care that each unique event type gets a unique `ComponentId`
- dynamic events need their own tools for getting identifiers anyways
- This avoids complicating the internals of `ComponentId` generation.
- Clearly document why this cludge-y solution exists.

In the medium term, I think that either a) properly generalizing
`ComponentId` (and moving it into `bevy_reflect?) or b) using a
new-typed `Entity` as the key for events is more correct. This change is
stupid simple though, and removes the offending trait bound in a way
that doesn't introduce complex tech debt and does not risk changes to
the internals.

This change does not:

- restrict our ability to implement dynamic buffered events (the main
improvement over #17317)
- there's still a fair bit of work to do, but this is a step in the
right direction
- limit our ability to store event metadata on entities in the future
- make it harder for users to work with types that are both events and
components (just add the derive / trait bound)

## Migration Guide

The `Event` trait no longer requires the `Component` trait. If you were
relying on this behavior, change your trait bounds from `Event` to
`Event + Component`. If you also want your `Event` type to implement
`Component`, add a derive.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-01-15 18:42:47 +00:00
MichiRecRoom
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.
2025-01-14 21:37:41 +00:00
Zachary Harrold
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.
2025-01-03 01:58:43 +00:00
Sean Kim
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.
2025-01-01 18:43:47 +00:00
Benjamin Brienen
4460a4d9ed
Use -D warnings in all relevant CI (#17011)
# Objective

Fixes #17009

See:
https://doc.rust-lang.org/stable/clippy/continuous_integration/index.html

## Solution

Add the env

## Testing

CI should start to fail, then I'll fix it.

## Showcase


![image](https://github.com/user-attachments/assets/acd2888f-9fc0-445a-a96a-842ba9f1c6aa)
2024-12-31 00:15:28 +00:00
JaySpruce
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);`
|
2024-12-30 20:58:03 +00:00
Zachary Harrold
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>
2024-12-17 21:40:36 +00:00
Christian Hughes
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`
2024-12-17 05:43:05 +00:00
SpecificProtagonist
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>
2024-12-12 18:12:53 +00:00
Miles Silberling-Cook
09b0b5df91
Window picking (#16103)
# Objective

On the web, it's common to attach observers to windows. As @viridia has
discovered, this can be quite a nice paradigm in bevy as well when
applied to observers. The changes here are intended to make this
possible.
+ Adds a new default picking back-end as part to the core picking plugin
(which can be disabled) that causes pointers on windows to treat the
window entity as the final hit, behind everything else. This means
clicking empty space now dispatches normal picking events to the window,
and is especially nice for drag-and-drop functionality.
+ Adds a new traversal type, specific to picking events, that causes
them to bubble up to the window entity after they reach the root of the
hierarchy.

## Solution

The window picking back-end is extremely simple, but the bubbling
changes are much more complex, since they require doing a different
traversal depending on the picking event.

To achieve this, `Traversal` has been made generic over an associated
sized data type `D`. Observer bounds have been changed such that
`Event::Traversal<D>` is required for `Trigger<D>`. A blanket
implementation has been added for `()` and `Parent` that preserves the
existing functionality. A new `PointerTraversal` traversal has been
implemented, with a blanket implementation for `Traversal<Pointer<E>>`.

It is still possible to use `Parent` as the traversal for any event,
because of the blanket implementation. It is now possible for users to
add other custom traversals, which read event data during traversal.

## Testing

I tested these changes locally on some picking UI prototypes I have been
playing with. I also tested them on the picking examples.

---------

Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com>
2024-12-05 21:14:39 +00:00
Christian Hughes
b4071ca370
Add World::get_resource_or_init as an alternative to World::get_resource_or_insert_with (#15758)
# Objective

If a `Resource` implements `FromWorld` or `Default`, it's nicer to be
able to write:

```rust
let foo = world.get_resource_or_init::<Foo>();
```

Rather than:

```rust
let foo = world.get_resource_or_insert_with(Foo::default);
```

The latter is also not possible if a type implements `FromWorld` only,
and not `Default`.

## Solution

Added:

```rust
impl World {
    pub fn get_resource_or_init<R: Resource + FromWorld>(&mut self) -> Mut<'_, R>;
}
```

Turns out all current in-engine uses of `get_resource_or_insert_with`
are exactly the above, so they've also been replaced.

## Testing

- Added a doc-test.
- Also added a doc-test for `World::get_resource_or_insert_with`.
2024-10-09 20:56:26 +00:00
urben1680
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.
2024-10-05 01:35:44 +00:00
Zachary Harrold
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>
2024-09-27 00:59:59 +00:00
Clar Fon
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
```
2024-09-24 11:42:59 +00:00
Benjamin Brienen
b45d83ebda
Rename Add to Queue for methods with deferred semantics (#15234)
# Objective

- Fixes #15106

## Solution

- Trivial refactor to rename the method. The duplicate method `push` was
removed as well. This will simpify the API and make the semantics more
clear. `Add` implies that the action happens immediately, whereas in
reality, the command is queued to be run eventually.
- `ChildBuilder::add_command` has similarly been renamed to
`queue_command`.

## Testing

Unit tests should suffice for this simple refactor.

---

## Migration Guide

- `Commands::add` and `Commands::push` have been replaced with
`Commnads::queue`.
- `ChildBuilder::add_command` has been renamed to
`ChildBuilder::queue_command`.
2024-09-17 00:17:49 +00:00
Blazepaws
21e39360f7
Example for bevy_ecs::event::Events uses deprecated function get_reader (#15216)
Fixes https://github.com/bevyengine/bevy/issues/15214
2024-09-15 14:24:04 +00:00
Blazepaws
53d2bc9482
Reflect derived traits on all components and resources: bevy_ecs (#15215)
Solves https://github.com/bevyengine/bevy/issues/15187 for bevy_ecs
2024-09-15 14:23:54 +00:00
robtfm
45281e62d7
Commands::send_event (#14933)
# Objective

sending events tends to be low-frequency so ergonomics can be
prioritized over efficiency.
add `Commands::send_event` to send any type of event without needing a
writer in hand.

i don't know how we feel about these kind of ergonomic things, i add
this to all my projects and find it useful. adding `mut
this_particular_event_writer: EventWriter<ThisParticularEvent>` every
time i want to send something is unnecessarily cumbersome.
it also simplifies the "send and receive in the same system" pattern
significantly.

basic example before:
```rs
fn my_func(
    q: Query<(Entity, &State)>,
    mut damage_event_writer: EventWriter<DamageEvent>,
    mut heal_event_writer: EventWriter<HealEvent>,
) {
    for (entity, state) in q.iter() {
        if let Some(damage) = state.get_damage() {
            damage_event_writer.send(DamageEvent { entity, damage });
        }

        if let Some(heal) = state.get_heal() {
            heal_event_writer.send(HealEvent { entity, heal });
        }
    }
}
```

basic example after:
```rs
import bevy::ecs::event::SendEventEx;

fn my_func(
    mut commands: Commands,
    q: Query<(Entity, &State)>,
) {
    for (entity, state) in q.iter() {
        if let Some(damage) = state.get_damage() {
            commands.send_event(DamageEvent { entity, damage });
        }

        if let Some(heal) = state.get_heal() {
            commands.send_event(HealEvent { entity, heal });
        }
    }
}
```

send/receive in the same system before:
```rs
fn send_and_receive_param_set(
    mut param_set: ParamSet<(EventReader<DebugEvent>, EventWriter<DebugEvent>)>,
) {
    // We must collect the events to resend, because we can't access the writer while we're iterating over the reader.
    let mut events_to_resend = Vec::new();

    // This is p0, as the first parameter in the `ParamSet` is the reader.
    for event in param_set.p0().read() {
        if event.resend_from_param_set {
            events_to_resend.push(event.clone());
        }
    }

    // This is p1, as the second parameter in the `ParamSet` is the writer.
    for mut event in events_to_resend {
        event.times_sent += 1;
        param_set.p1().send(event);
    }
}
```

after:
```rs
use bevy::ecs::event::SendEventEx;

fn send_via_commands_and_receive(
    mut reader: EventReader<DebugEvent>,
    mut commands: Commands,
) {
    for event in reader.read() {
        if event.resend_via_commands {
            commands.send_event(DebugEvent {
                times_sent: event.times_sent + 1,
                ..event.clone()
            });
        }
    }
}
```

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2024-08-27 23:43:40 +00:00
Miles Silberling-Cook
ed2b8e0f35
Minimal Bubbling Observers (#13991)
# Objective

Add basic bubbling to observers, modeled off `bevy_eventlistener`.

## Solution

- Introduce a new `Traversal` trait for components which point to other
entities.
- Provide a default `TraverseNone: Traversal` component which cannot be
constructed.
- Implement `Traversal` for `Parent`.
- The `Event` trait now has an associated `Traversal` which defaults to
`TraverseNone`.
- Added a field `bubbling: &mut bool` to `Trigger` which can be used to
instruct the runner to bubble the event to the entity specified by the
event's traversal type.
- Added an associated constant `SHOULD_BUBBLE` to `Event` which
configures the default bubbling state.
- Added logic to wire this all up correctly.

Introducing the new associated information directly on `Event` (instead
of a new `BubblingEvent` trait) lets us dispatch both bubbling and
non-bubbling events through the same api.

## Testing

I have added several unit tests to cover the common bugs I identified
during development. Running the unit tests should be enough to validate
correctness. The changes effect unsafe portions of the code, but should
not change any of the safety assertions.

## Changelog

Observers can now bubble up the entity hierarchy! To create a bubbling
event, change your `Derive(Event)` to something like the following:

```rust
#[derive(Component)]
struct MyEvent;

impl Event for MyEvent {
    type Traverse = Parent; // This event will propagate up from child to parent.
    const AUTO_PROPAGATE: bool = true; // This event will propagate by default.
}
```

You can dispatch a bubbling event using the normal
`world.trigger_targets(MyEvent, entity)`.

Halting an event mid-bubble can be done using
`trigger.propagate(false)`. Events with `AUTO_PROPAGATE = false` will
not propagate by default, but you can enable it using
`trigger.propagate(true)`.

If there are multiple observers attached to a target, they will all be
triggered by bubbling. They all share a bubbling state, which can be
accessed mutably using `trigger.propagation_mut()` (`trigger.propagate`
is just sugar for this).

You can choose to implement `Traversal` for your own types, if you want
to bubble along a different structure than provided by `bevy_hierarchy`.
Implementers must be careful never to produce loops, because this will
cause bevy to hang.

## Migration Guide
+ Manual implementations of `Event` should add associated type `Traverse
= TraverseNone` and associated constant `AUTO_PROPAGATE = false`;
+ `Trigger::new` has new field `propagation: &mut Propagation` which
provides the bubbling state.
+ `ObserverRunner` now takes the same `&mut Propagation` as a final
parameter.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-07-15 13:39:41 +00:00
Giacomo Stevanato
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)
2024-07-11 13:08:31 +00:00
Bob Gardner
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>
2024-07-08 14:53:06 +00:00
Lee-Orr
bd7dcd3f6d
deregister events (#14083)
# Objective

Add ability to de-register events from the EventRegistry (and the
associated World).

The initial reasoning relates to retaining support for Event hot
reloading in `dexterous_developer`.

## Solution

Add a `deregister_events<T: Event>(&mut world)` method to the
`EventRegistry` struct.

## Testing

Added an automated test that verifies the event registry adds and
removes `Events<T>` from the world.

---------

Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
2024-07-01 16:18:14 +00:00
Lura
856b39d821
Apply Clippy lints regarding lazy evaluation and closures (#14015)
# Objective

- Lazily evaluate
[default](https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_or_default)~~/[or](https://rust-lang.github.io/rust-clippy/master/index.html#/or_fun_call)~~
values where it makes sense
  - ~~`unwrap_or(foo())` -> `unwrap_or_else(|| foo())`~~
  - `unwrap_or(Default::default())` -> `unwrap_or_default()`
  - etc.
- Avoid creating [redundant
closures](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure),
even for [method
calls](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure_for_method_calls)
  - `map(|something| something.into())` -> `map(Into:into)`

## Solution

- Apply Clippy lints:
-
~~[or_fun_call](https://rust-lang.github.io/rust-clippy/master/index.html#/or_fun_call)~~
-
[unwrap_or_default](https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_or_default)
-
[redundant_closure_for_method_calls](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure_for_method_calls)
([redundant
closures](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure)
is already enabled)

## Testing

- Tested on Windows 11 (`stable-x86_64-pc-windows-gnu`, 1.79.0)
- Bevy compiles without errors or warnings and examples seem to work as
intended
  - `cargo clippy` 
  - `cargo run -p ci -- compile` 

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-07-01 15:54:40 +00:00
Bob Gardner
4c3b4a445d
Mark events as read during EventReader::par_read (#13836)
# Objective

- Fix issue #13821  

## Solution

- Rewrote the test to ensure that it actually tests the functionality
correctly. Then made the par_read function correctly change the values
of self.reader.last_event_count.

## Testing

- Rewrote the test for par_read to run the system schedule twice,
checking the output each time

---------

Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com>
2024-06-25 15:36:06 +00:00
Jan Hohenheim
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
2024-06-17 17:22:01 +00:00
James O'Brien
eb3c81374a
Generalised ECS reactivity with Observers (#10839)
# Objective

- Provide an expressive way to register dynamic behavior in response to
ECS changes that is consistent with existing bevy types and traits as to
provide a smooth user experience.
- Provide a mechanism for immediate changes in response to events during
command application in order to facilitate improved query caching on the
path to relations.

## Solution

- A new fundamental ECS construct, the `Observer`; inspired by flec's
observers but adapted to better fit bevy's access patterns and rust's
type system.

---

## Examples
There are 3 main ways to register observers. The first is a "component
observer" that looks like this:
```rust
world.observe(|trigger: Trigger<OnAdd, Transform>, query: Query<&Transform>| {
    let transform = query.get(trigger.entity()).unwrap();
});
```
The above code will spawn a new entity representing the observer that
will run it's callback whenever the `Transform` component is added to an
entity. This is a system-like function that supports dependency
injection for all the standard bevy types: `Query`, `Res`, `Commands`
etc. It also has a `Trigger` parameter that provides information about
the trigger such as the target entity, and the event being triggered.
Importantly these systems run during command application which is key
for their future use to keep ECS internals up to date. There are similar
events for `OnInsert` and `OnRemove`, and this will be expanded with
things such as `ArchetypeCreated`, `TableEmpty` etc. in follow up PRs.

Another way to register an observer is an "entity observer" that looks
like this:
```rust
world.entity_mut(entity).observe(|trigger: Trigger<Resize>| {
    // ...
});
```
Entity observers run whenever an event of their type is triggered
targeting that specific entity. This type of observer will de-spawn
itself if the entity (or entities) it is observing is ever de-spawned so
as to not leave dangling observers.

Entity observers can also be spawned from deferred contexts such as
other observers, systems, or hooks using commands:
```rust
commands.entity(entity).observe(|trigger: Trigger<Resize>| {
    // ...
});
```

Observers are not limited to in built event types, they can be used with
any type that implements `Event` (which has been extended to implement
Component). This means events can also carry data:

```rust
#[derive(Event)]
struct Resize { x: u32, y: u32 }

commands.entity(entity).observe(|trigger: Trigger<Resize>, query: Query<&mut Size>| {
    let event = trigger.event();
    // ...
});

// Will trigger the observer when commands are applied.
commands.trigger_targets(Resize { x: 10, y: 10 }, entity);
```

You can also trigger events that target more than one entity at a time:

```rust
commands.trigger_targets(Resize { x: 10, y: 10 }, [e1, e2]);
```

Additionally, Observers don't _need_ entity targets:

```rust
app.observe(|trigger: Trigger<Quit>| {
})

commands.trigger(Quit);
```

In these cases, `trigger.entity()` will be a placeholder.

Observers are actually just normal entities with an `ObserverState` and
`Observer` component! The `observe()` functions above are just shorthand
for:

```rust
world.spawn(Observer::new(|trigger: Trigger<Resize>| {});
```

This will spawn the `Observer` system and use an `on_add` hook to add
the `ObserverState` component.

Dynamic components and trigger types are also fully supported allowing
for runtime defined trigger types.

## Possible Follow-ups
1. Deprecate `RemovedComponents`, observers should fulfill all use cases
while being more flexible and performant.
2. Queries as entities: Swap queries to entities and begin using
observers listening to archetype creation triggers to keep their caches
in sync, this allows unification of `ObserverState` and `QueryState` as
well as unlocking several API improvements for `Query` and the
management of `QueryState`.
3. Trigger bubbling: For some UI use cases in particular users are
likely to want some form of bubbling for entity observers, this is
trivial to implement naively but ideally this includes an acceleration
structure to cache hierarchy traversals.
4. All kinds of other in-built trigger types.
5. Optimization; in order to not bloat the complexity of the PR I have
kept the implementation straightforward, there are several areas where
performance can be improved. The focus for this PR is to get the
behavior implemented and not incur a performance cost for users who
don't use observers.

I am leaving each of these to follow up PR's in order to keep each of
them reviewable as this already includes significant changes.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-06-15 01:33:26 +00:00
Alice Cecile
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>
2024-06-12 14:28:51 +00:00
Bob Gardner
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.
2024-06-10 21:45:01 +00:00