Skip to content

Observers that can observe multiple different event types #14649

Open
@ItsDoot

Description

@ItsDoot

What problem does this solve or what need does it fill?

Being able to trigger the same observer with multiple different event types would be useful for code reuse and centralizing related logic. This is different from enum Event types, which don't allow you to listen to individual variants for separate observers.

What solution would you like?

#[derive(Event)]
struct FooEvent { foo: i32 }
#[derive(Event)]
struct BarEvent { bar: bool }

// I imagine `Or<(A, B)>` would deref into some `enum Or2<A, B> { A(A), B(B) }`
// `Or<(A, B, C)>` would deref into some `enum Or3<A, B, C> { A(A), B(B), C(C) }`
// etc
// Also, I imagine we'll need a separate `world.observe_any_of()`-style method to correctly handle the internal details
world.observe(|trigger: Trigger<Or<(FooEvent, BarEvent)>>| {
    match trigger.event() {
        Or2::A(FooEvent { foo }) => { /* do something with foo... */ },
        Or2::B(BarEvent { bar }) => { /* do something with bar... */ },
    }
});

What alternative(s) have you considered?

Use an enum:

#[derive(Event)]
enum MyEvent {
    Foo { foo: i32 },
    Bar { bar: bool },
}

world.observe(|trigger: Trigger<MyEvent>| {
    match trigger.event() {
        MyEvent::Foo { foo } => { /* ... */ },
        MyEvent::Bar { bar } => { /* ... */ },
    }
});

However, that prevents us from listening to only 1 of the event types, or a subset of them.

Additional context

This was mentioned on discord as currently possible, albeit with unsafe APIs, so we should introduce a safe wrapper:

Diddykonga — Today at 8:41 PM
Can observers 'observe' multiple events? if not, then the query would need to be split from the observer at some point.
James 🦃 — Today at 9:02 PM
Observers can absolutely observe multiple events
(unsafely)
nth — Today at 9:03 PM
What does the trigger return for the event?
James 🦃 — Today at 9:04 PM
You have to use unsafe APIs
So you can make the trigger return any type and you just have to promise it's safe
The observer for a query should set the ObserverRunner manually to not pay for system overhead
Diddykonga — Today at 9:06 PM
Right since you cant carry that information via generics, unsafe is the only way (variadics when?)
James 🦃 — Today at 9:06 PM
And the ObserverRunner API just gets a pointer and deferred world
(similar to a hook)
doot — Today at 9:13 PM
I wonder if you could wrap that safely in a Trigger<Either<A, B>> type api
actually probably something like a Trigger<Or<(A, B, C, ...)>>

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-FeatureA new feature, making something new possibleD-ComplexQuite challenging from either a design or technical perspective. Ask for help!D-UnsafeTouches with unsafe code in some wayS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!

    Type

    No type

    Projects

    Status

    Observer overhaul

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions