use crate::prelude::SystemCommand;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
#[derive(Resource)]
pub(crate) struct SystemEventAccessTracker
{
currently_reacting: bool,
data_entity: Entity,
prepared: Vec<(SystemCommand, Entity)>,
}
impl SystemEventAccessTracker
{
pub(crate) fn prepare(&mut self, system: SystemCommand, data_entity: Entity)
{
self.prepared.push((system, data_entity));
}
pub(crate) fn start(&mut self, reactor: SystemCommand)
{
let Some(pos) = self.prepared.iter().position(|(s, _)| *s == reactor) else {
tracing::error!("prepared system event is missing {:?}", reactor);
debug_assert!(false);
return;
};
let (_, data_entity) = self.prepared.swap_remove(pos);
debug_assert!(!self.currently_reacting);
self.currently_reacting = true;
self.data_entity = data_entity;
}
pub(crate) fn end(&mut self) -> Entity
{
self.currently_reacting = false;
self.data_entity
}
fn is_reacting(&self) -> bool
{
self.currently_reacting
}
fn data_entity(&self) -> Entity
{
self.data_entity
}
}
impl Default for SystemEventAccessTracker
{
fn default() -> Self
{
Self{
currently_reacting: false,
data_entity: Entity::from_raw(0u32),
prepared: Vec::default(),
}
}
}
#[derive(Component)]
pub(crate) struct SystemEventData<T: Send + Sync + 'static>
{
data: Option<T>,
}
impl<T: Send + Sync + 'static> SystemEventData<T>
{
pub(crate) fn new(data: T) -> Self
{
Self{ data: Some(data) }
}
fn take(&mut self) -> Option<T>
{
self.data.take()
}
}
#[derive(SystemParam)]
pub struct SystemEvent<'w, 's, T: Send + Sync + 'static>
{
tracker: Res<'w, SystemEventAccessTracker>,
data: Query<'w, 's, &'static mut SystemEventData<T>>,
}
impl<'w, 's, T: Send + Sync + 'static> SystemEvent<'w, 's, T>
{
pub fn take(&mut self) -> Option<T>
{
if !self.tracker.is_reacting() { return None; }
let Ok(mut data) = self.data.get_mut(self.tracker.data_entity()) else { return None; };
data.take()
}
}