bevyengine / bevyengine/bevy

Add events that can be consumed, or can only be read once

Open
#20,171 0 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Feature S-Needs-Design-Doc X-Needs-SME
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

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

I recently ran into a use case where I wanted an observer to "consume" an event because the underlying data is big and/or not cloneable. I can guarantee that this is the only observer that will ever listen to this event.

So I made a generic solution that I'm calling `SingleEvent` (or maybe `UniqueEvent`? `ExclusiveEvent`?).

## What solution would you like?

I propose a new `SingleEvent` trait, along with `SingleTrigger` and `add_single_observer`:
```rust
trait SingleEvent: 'static + Send + Sync { }
```
A "single" observer takes `SingleTrigger` as its trigger type:
```rust
impl SingleEvent for Foo {}

fn my_single_observer(trigger: SingleTrigger) {
let mut data = trigger.event().consume().unwrap();
// Data is mine now! :D
}
```
Single observers are registered via `add_single_observer`:
```rust
app.add_single_observer(my_single_observer)
```
This function should panic or raise errors if another observer is already registered for this event.

User invokes the event using `trigger_single`:
```rust
commands.trigger_single(Foo);
```

A `SingleTrigger` is just a type alias:
```rust
type SingleTrigger = Trigger>
```

And `OnSingleEvent` is just a `Mutex`:
```rust
#[derive(Event)]
pub struct OnSingleEvent(Mutex>);
```

I've released the full implementation of my proposed solution under my own public crate:
https://github.com/Zeenobit/moonshine_util/blob/main/src/event.rs

However, I believe it'd be better if this was part of Bevy proper:
- We could enforce "single" observer registration more robustly
- I'm using a dummy plugin as a "registry" in my solution, and the user could easily bypass this check.
- We could maybe unify the API a bit to maybe use the same `trigger()` method to trigger single and regular events alike.
- We could extend the feature to support single triggers on entities too (my solution only support world events).
- It's a powerful tool for library authors since it allows them to "consume" user data from observers without relying on third party crates

## What alternative(s) have you considered?

N/A

## Additional context

See here for a usage of this pattern in `moonshine-save`:
https://github.com/Zeenobit/moonshine_save/blob/9f7f533086cae8bf6851ff8acfd142018b7f966e/src/save.rs#L206

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.