Observer EventMatchers#24013
Conversation
james-j-obrien
left a comment
There was a problem hiding this comment.
Cool approach! Looks good.
Diddykonga
left a comment
There was a problem hiding this comment.
LGTM
Abstracts the E: Event and B: Bundle from On<E, B> into On<E> where E: EventMatcher
This allows the syntax to be like Add<C>, while the actual semantics use the Event and Components associated types on the EventMatcher. Similiar-ish to an custom WorldQuery.
chescock
left a comment
There was a problem hiding this comment.
Nice, this is an elegant way to solve this! I like how a bunch of B generics disappeared from places that don't care about them.
| > On<'w, 't, E, B> | ||
| impl<'w, 't, const AUTO_PROPAGATE: bool, E, T> On<'w, 't, E> | ||
| where | ||
| E: EntityEvent + for<'a> Event<Trigger<'a> = PropagateEntityTrigger<AUTO_PROPAGATE, E, T>>, |
There was a problem hiding this comment.
Should E be generalized from Event to EventMatcher here, like the other uses of On? Or is the idea that EntityEvents never use components?
| /// **not** an `AND` filter. For example, `Add<(A, B)>` will trigger if either | ||
| /// component `A` or component `B` is added to an entity. | ||
| #[doc(alias = "OnAdd")] | ||
| pub struct Add<B: Bundle>(PhantomData<B>); |
There was a problem hiding this comment.
Making sure I understand: We never actually create values of these types, and they just exist to be used as type parameters. Right?
| M: Send + Sync + 'static, | ||
| I: IntoObserverSystem<E, B, M> + Send + Sync, | ||
| > Bundle for AddObserver<E, B, M, I> | ||
| unsafe impl<E: EntityEvent, M: Send + Sync + 'static, I: IntoObserverSystem<E, M> + Send + Sync> |
There was a problem hiding this comment.
(Same question about EntityEvent for the uses in this file.)
| label = "invalid `EventMatcher`", | ||
| note = "consider annotating `{Self}` with `#[derive(Event)]` or implementing `EventMatcher` manually" | ||
| )] | ||
| pub trait EventMatcher: 'static { |
There was a problem hiding this comment.
Ooh, once we have a separation between the type used to trigger the event and the type used to observe it, I bet we'll be able to use this for other things, like #14649.
| ``` | ||
|
|
||
| For lifecycle observers watching dynamic components, you now need to modify | ||
| `On<Add>` to `On<Add<()>>`: |
There was a problem hiding this comment.
It's also possible to use On<AddEvent>, right? But our recommendation is On<Add<()>> for consistency?
| type Trigger<'a>: Trigger<Self>; | ||
| } | ||
|
|
||
| /// Trait for types that can be 'matched' on by [`Observer`]s to register additional |
There was a problem hiding this comment.
Ah, so the idea behind the "matcher" metaphor is that there is a stream of AddEvents going by and the Add<C> matcher will only match ones with a C? I was a little confused by the name at first. Matcher is an uncommon term, and I was thinking of these more like sets of events than like filters.
Bikeshedding a bit: How about something like EventPattern? These types seem to fill the role of the pattern in a match expression. And a "pattern" also sounds like a way to generate a set of events to observe.
| /// All components specified in the [`Bundle`] are treated as an `OR` filter | ||
| /// **not** an `AND` filter. For example, `Add<(A, B)>` will trigger if either | ||
| /// component `A` or component `B` is added to an entity. | ||
| #[doc(alias = "OnAdd")] |
There was a problem hiding this comment.
Not really related to this PR, but: Are we planning to leave these aliases in place forever? I thought they were mostly to help migrate in the release where we renamed them.
There was a problem hiding this comment.
Normally I would just remove the doc aliases / deprecation after a cycle, but with the rise of the LLM I'm questioning that policy. Lots of reports of "my LLM got confused by a migration" for up to a couple years after it was done.
There was a problem hiding this comment.
Do we need to worry about someones tool breaking on migrations, when that tool hallucinates/breaks on its own?
|
I'm curious about the answers to @chescock's questions :) FYI, I'm planning to hold off merging this until after we cut the 0.19-rc, so we can land this together with a F: QueryFilter generic in 0.20. |
Objective
On<E, B>'sBtype parameter is the single most commonly confusing construct inbevy_ecs. The predominate use case is for filtering lifecycle event observers:For most other use cases, like picking or custom user events, this type parameter is wholly unnecessary.
Therefore, it should be removed from view for the cases where it is not needed.
Solution
Introduce a tiny layer above
EventcalledEventMatcher, and updateOn's bounds:This allows us to lift the
Btype parameter inOn<E, B>into theEtype parameter. To ensure other events like picking continue to work, we introduce a blanket implementation for allEvents:To facilitate lifecycle events, we first rename them as follows:
Add->AddEventInsert->InsertEventDiscard->DiscardEventRemove->RemoveEventDespawn->DespawnEventThen, we introduce new generic types in place of those old identifiers that are
EventMatchers:This results in a slightly modified observer setup:
Testing
No new tests have been added, we have decent coverage of observer tests as is.