use crate::prelude::SystemCommand;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use std::any::type_name;
#[derive(Resource)]
pub(crate) struct EventAccessTracker
{
currently_reacting: bool,
data_entity: Entity,
prepared: Vec<(SystemCommand, Entity)>,
}
impl EventAccessTracker
{
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 event reaction 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 EventAccessTracker
{
fn default() -> Self
{
Self{
currently_reacting: false,
data_entity: Entity::from_raw(0u32),
prepared: Vec::default(),
}
}
}
#[derive(Component)]
pub(crate) struct BroadcastEventData<T: Send + Sync + 'static>
{
data: T,
}
impl<T: Send + Sync + 'static> BroadcastEventData<T>
{
pub(crate) fn new(data: T) -> Self
{
Self{ data }
}
fn read(&self) -> &T
{
&self.data
}
}
#[derive(Component)]
pub(crate) struct EntityEventData<T: Send + Sync + 'static>
{
entity: Entity,
data: T,
}
impl<T: Send + Sync + 'static> EntityEventData<T>
{
pub(crate) fn new(target_entity: Entity, data: T) -> Self
{
Self{ entity: target_entity, data }
}
fn read(&self) -> (Entity, &T)
{
(self.entity, &self.data)
}
}
#[derive(SystemParam)]
pub struct BroadcastEvent<'w, 's, T: Send + Sync + 'static>
{
tracker: Res<'w, EventAccessTracker>,
data: Query<'w, 's, &'static BroadcastEventData<T>>,
}
impl<'w, 's, T: Send + Sync + 'static> BroadcastEvent<'w, 's, T>
{
pub fn read(&self) -> &T
{
self.try_read()
.unwrap_or_else(|| panic!("failed reading broadcast event for {}, there is no event", type_name::<T>()))
}
pub fn try_read(&self) -> Option<&T>
{
if !self.tracker.is_reacting() { return None; }
let Ok(data) = self.data.get(self.tracker.data_entity()) else { return None; };
Some(data.read())
}
pub fn is_empty(&self) -> bool
{
self.try_read().is_none()
}
}
#[derive(SystemParam)]
pub struct EntityEvent<'w, 's, T: Send + Sync + 'static>
{
tracker: Res<'w, EventAccessTracker>,
data: Query<'w, 's, &'static EntityEventData<T>>,
}
impl<'w, 's, T: Send + Sync + 'static> EntityEvent<'w, 's, T>
{
pub fn read(&self) -> (Entity, &T)
{
self.try_read()
.unwrap_or_else(|| panic!("failed reading entity event for {}, there is no event", type_name::<T>()))
}
pub fn try_read(&self) -> Option<(Entity, &T)>
{
if !self.tracker.is_reacting() { return None; }
let Ok(data) = self.data.get(self.tracker.data_entity()) else { return None; };
Some(data.read())
}
pub fn entity(&self) -> Entity
{
self.read().0
}
pub fn get_entity(&self) -> Option<Entity>
{
self.try_read().map(|(e, _)| e)
}
pub fn is_empty(&self) -> bool
{
self.try_read().is_none()
}
}