bevy_extern_events 0.1.0

Bevy plugin for generic external events to be send to bevy EventReaders
use std::{marker::PhantomData, sync::Mutex};

use bevy::prelude::*;
use generic_global_variables::*;
use once_cell::sync::OnceCell;

#[derive(Event, Default)]
pub struct ExternEvent<T: Send + Sync + Default>(pub T);

#[derive(Resource)]
pub struct ExternEventQueueResource<T: Send + Sync + Default>(T);

#[derive(Default)]
pub struct ExternEventsPlugin<T>(PhantomData<T>);

impl<T: Send + Sync + Default> Plugin for ExternEventsPlugin<T>
where
    ExternEvent<T>: Event,
{
    fn build(&self, app: &mut App) {
        app.add_event::<ExternEvent<T>>()
            .add_systems(PreUpdate, poll_events);
    }
}

fn get_global<T: Send + Sync>(f: impl FnOnce() -> T) -> Entry<T> {
    static GLOBALS: OnceCell<GenericGlobal> = OnceCell::new();

    let globals = GLOBALS.get_or_init(GenericGlobal::new);
    globals.get_or_init(f)
}

pub fn queue_event<T: 'static + Send + Sync>(event: T) {
    let arc = get_global(Mutex::<Vec<T>>::default);
    arc.lock().unwrap().push(event);
}

fn poll_events<T: 'static + Send + Sync + Default>(mut writer: EventWriter<ExternEvent<T>>) {
    let arc = get_global(Mutex::<Vec<T>>::default);
    while let Some(e) = arc.lock().unwrap().pop() {
        info!("poll event");
        writer.send(ExternEvent(e));
    }
}

#[cfg(test)]
mod tests {
    use bevy::prelude::*;

    use crate::{queue_event, ExternEvent, ExternEventsPlugin};

    #[derive(Default)]
    pub struct MyEvent;

    #[derive(Resource, Reflect, Default)]
    pub struct MyEventResource(i32);

    pub fn event_system(
        mut res: ResMut<MyEventResource>,
        mut native_events: EventReader<ExternEvent<MyEvent>>,
    ) {
        for _e in native_events.read() {
            res.0 += 1;
        }
    }

    #[test]
    fn smoke() {
        let mut app = App::new();
        app.init_resource::<MyEventResource>()
            .add_plugins(ExternEventsPlugin::<MyEvent>::default())
            .add_systems(Update, event_system);

        queue_event(MyEvent::default());

        app.update();

        assert_eq!(app.world.resource::<MyEventResource>().0, 1);
    }
}