bevy_channel_trigger/
lib.rs

1use bevy::{ecs::event::Event, prelude::*};
2use crossbeam_channel::{Receiver, Sender, TryRecvError};
3
4/// channel sender to share with multiple producers and offering a simple `send` function
5#[derive(Resource, Clone, Debug)]
6pub struct ChannelSender<T: Event>(Sender<T>);
7
8impl<T: Event> ChannelSender<T> {
9    /// send `event` to our central receiver that forwards them as triggers that can be observed
10    pub fn send(&self, event: impl Into<T>) {
11        let event = event.into();
12        if let Err(err) = self.0.send(event) {
13            error!("sending failed due to receiver being dropped: {err:?}");
14        };
15    }
16}
17
18#[derive(Resource)]
19struct EventReceiver<T: Event>(Receiver<T>);
20
21/// Extension to Bevy `App` that allows ergonomic creation of the channel
22pub trait ChannelTriggerApp {
23    /// Spawns a channel registers the receiver as a resource and returns the `ChannelSender<T>`
24    /// This sender can be used from anywhere to send events into the Bevy world.
25    /// These triggers can be subscribed to via `app.observe`.
26    fn add_channel_trigger<T: Event>(&mut self) -> ChannelSender<T>;
27}
28
29impl ChannelTriggerApp for App {
30    fn add_channel_trigger<T: Event>(&mut self) -> ChannelSender<T> {
31        let (sender, receiver) = crossbeam_channel::unbounded();
32        self.insert_resource(EventReceiver::<T>(receiver));
33        self.add_systems(PreUpdate, process_events::<T>);
34        ChannelSender::<T>(sender)
35    }
36}
37
38fn process_events<T: Event>(receiver: Option<Res<EventReceiver<T>>>, mut commands: Commands) {
39    if let Some(receiver) = receiver.as_ref() {
40        loop {
41            match receiver.0.try_recv() {
42                Ok(msg) => {
43                    commands.trigger(msg);
44                }
45                Err(TryRecvError::Disconnected) => {
46                    error!("sender dropped, removing receiver");
47                    commands.remove_resource::<EventReceiver<T>>();
48                }
49                Err(TryRecvError::Empty) => {
50                    break;
51                }
52            }
53        }
54    }
55}