use crate::prelude::*;
use bevy::prelude::*;
use bevy::utils::{HashMap, HashSet};
use crossbeam::channel::{Receiver, Sender};
use core::any::TypeId;
use std::vec::Vec;
struct ComponentReactors
{
insertion_callbacks : Vec<ReactorHandle>,
mutation_callbacks : Vec<ReactorHandle>,
removal_callbacks : Vec<ReactorHandle>,
}
impl ComponentReactors
{
fn is_empty(&self) -> bool
{
self.insertion_callbacks.is_empty() &&
self.mutation_callbacks.is_empty() &&
self.removal_callbacks.is_empty()
}
}
impl Default for ComponentReactors
{
fn default() -> Self
{
Self{
insertion_callbacks : Vec::new(),
mutation_callbacks : Vec::new(),
removal_callbacks : Vec::new(),
}
}
}
fn collect_component_removals<C: ReactComponent>(
In(mut buffer) : In<Vec<Entity>>,
mut removed : RemovedComponents<React<C>>,
) -> Vec<Entity>
{
buffer.clear();
removed.read().for_each(|entity| buffer.push(entity));
buffer
}
struct RemovalChecker
{
component_id : TypeId,
checker : SysCall<(), Vec<Entity>, Vec<Entity>>
}
impl RemovalChecker
{
fn new<C: ReactComponent>() -> Self
{
Self{
component_id : TypeId::of::<C>(),
checker : SysCall::new(|world, buffer| syscall(world, buffer, collect_component_removals::<C>)),
}
}
}
fn schedule_entity_reaction_impl(
buffer : &mut Vec<ReactionCommand>,
reaction_source : Entity,
reaction_type : EntityReactionType,
entity_reactors : &EntityReactors
){
if let EntityReactionType::Event(id) = reaction_type
{ tracing::error!(?id, "tried queuing entity event as entity reaction"); return; }
for reactor in entity_reactors.iter_rtype(reaction_type)
{
buffer.push(
ReactionCommand::EntityReaction{
reaction_source,
reaction_type,
reactor,
}
);
}
}
#[derive(Resource)]
pub(crate) struct ReactCache
{
reaction_commands_buffer: Vec<ReactionCommand>,
component_reactors: HashMap<TypeId, ComponentReactors>,
tracked_removals: HashSet<TypeId>,
removal_checkers: Vec<RemovalChecker>,
removal_buffer: Option<Vec<Entity>>,
despawn_reactors: HashMap<Entity, Vec<ReactorHandle>>,
despawn_sender: Sender<Entity>,
despawn_receiver: Receiver<Entity>,
any_entity_event_reactors: HashMap<TypeId, Vec<ReactorHandle>>,
resource_reactors: HashMap<TypeId, Vec<ReactorHandle>>,
broadcast_reactors: HashMap<TypeId, Vec<ReactorHandle>>,
}
impl ReactCache
{
pub(crate) fn despawn_sender(&self) -> Sender<Entity>
{
self.despawn_sender.clone()
}
pub(crate) fn track_removals<C: ReactComponent>(&mut self)
{
if self.tracked_removals.contains(&TypeId::of::<C>()) { return; };
self.tracked_removals.insert(TypeId::of::<C>());
self.removal_checkers.push(RemovalChecker::new::<C>());
}
pub(crate) fn register_insertion_reactor<C: ReactComponent>(&mut self, handle: ReactorHandle)
{
self.component_reactors
.entry(TypeId::of::<C>())
.or_default()
.insertion_callbacks
.push(handle);
}
pub(crate) fn register_mutation_reactor<C: ReactComponent>(&mut self, handle: ReactorHandle)
{
self.component_reactors
.entry(TypeId::of::<C>())
.or_default()
.mutation_callbacks
.push(handle);
}
pub(crate) fn register_removal_reactor<C: ReactComponent>(&mut self, handle: ReactorHandle)
{
self.component_reactors
.entry(TypeId::of::<C>())
.or_default()
.removal_callbacks
.push(handle);
}
pub(crate) fn register_any_entity_event_reactor<E: 'static>(&mut self, handle: ReactorHandle)
{
self.any_entity_event_reactors
.entry(TypeId::of::<E>())
.or_default()
.push(handle);
}
pub(crate) fn register_resource_mutation_reactor<R: ReactResource>(&mut self, handle: ReactorHandle)
{
self.resource_reactors
.entry(TypeId::of::<R>())
.or_default()
.push(handle);
}
pub(crate) fn register_broadcast_reactor<E: 'static>(&mut self, handle: ReactorHandle)
{
self.broadcast_reactors
.entry(TypeId::of::<E>())
.or_default()
.push(handle);
}
pub(crate) fn register_despawn_reactor(&mut self, entity: Entity, handle: ReactorHandle)
{
self.despawn_reactors
.entry(entity)
.or_default()
.push(handle);
}
pub(crate) fn revoke_component_reactor(&mut self, rtype: EntityReactionType, reactor_id: SystemCommand)
{
let (comp_id, reactors) = match rtype
{
EntityReactionType::Insertion(comp_id) => (comp_id, self.component_reactors.get_mut(&comp_id)),
EntityReactionType::Mutation(comp_id) => (comp_id, self.component_reactors.get_mut(&comp_id)),
EntityReactionType::Removal(comp_id) => (comp_id, self.component_reactors.get_mut(&comp_id)),
EntityReactionType::Event(_) => unreachable!(),
};
let Some(reactors) = reactors else { return; };
let callbacks = match rtype
{
EntityReactionType::Insertion(_) => &mut reactors.insertion_callbacks,
EntityReactionType::Mutation(_) => &mut reactors.mutation_callbacks,
EntityReactionType::Removal(_) => &mut reactors.removal_callbacks,
EntityReactionType::Event(_) => unreachable!(),
};
for (idx, handle) in callbacks.iter().enumerate()
{
if handle.sys_command() != reactor_id { continue; }
let _ = callbacks.remove(idx);
break;
}
if !reactors.is_empty() { return; }
let _ = self.component_reactors.remove(&comp_id);
}
pub(crate) fn revoke_any_entity_event_reactor(&mut self, event_id: TypeId, reactor_id: SystemCommand)
{
let Some(callbacks) = self.any_entity_event_reactors.get_mut(&event_id) else { return; };
for (idx, handle) in callbacks.iter().enumerate()
{
if handle.sys_command() != reactor_id { continue; }
let _ = callbacks.remove(idx);
break;
}
if callbacks.len() > 0 { return; }
let _ = self.any_entity_event_reactors.remove(&event_id);
}
pub(crate) fn revoke_resource_mutation_reactor(&mut self, resource_id: TypeId, reactor_id: SystemCommand)
{
let Some(callbacks) = self.resource_reactors.get_mut(&resource_id) else { return; };
for (idx, handle) in callbacks.iter().enumerate()
{
if handle.sys_command() != reactor_id { continue; }
let _ = callbacks.remove(idx);
break;
}
if callbacks.len() > 0 { return; }
let _ = self.resource_reactors.remove(&resource_id);
}
pub(crate) fn revoke_broadcast_reactor(&mut self, event_id: TypeId, reactor_id: SystemCommand)
{
let Some(callbacks) = self.broadcast_reactors.get_mut(&event_id) else { return; };
for (idx, handle) in callbacks.iter().enumerate()
{
if handle.sys_command() != reactor_id { continue; }
let _ = callbacks.remove(idx);
break;
}
if callbacks.len() > 0 { return; }
let _ = self.broadcast_reactors.remove(&event_id);
}
pub(crate) fn revoke_despawn_reactor(&mut self, entity: Entity, reactor_id: SystemCommand)
{
let Some(callbacks) = self.despawn_reactors.get_mut(&entity) else { return; };
for (idx, handle) in callbacks.iter().enumerate()
{
if handle.sys_command() != reactor_id { continue; }
let _ = callbacks.remove(idx);
break;
}
if callbacks.len() > 0 { return; }
let _ = self.despawn_reactors.remove(&entity);
}
pub(crate) fn schedule_insertion_reaction<C: ReactComponent>(
In(entity) : In<Entity>,
mut cache : ResMut<ReactCache>,
mut commands : Commands,
entity_reactors : Query<&EntityReactors>,
){
let rtype = EntityReactionType::Insertion(TypeId::of::<C>());
if let Ok(entity_reactors) = entity_reactors.get(entity)
{
let _ = schedule_entity_reaction_impl(&mut cache.reaction_commands_buffer, entity, rtype, &entity_reactors);
}
for command in cache.reaction_commands_buffer.drain(..) {
commands.queue(command);
}
if let Some(handlers) = cache.component_reactors.get(&TypeId::of::<C>())
{
for handle in handlers.insertion_callbacks.iter()
{
commands.queue(
ReactionCommand::EntityReaction{
reaction_source : entity,
reaction_type : rtype,
reactor : handle.sys_command(),
}
);
}
}
}
pub(crate) fn schedule_mutation_reaction<C: ReactComponent>(
In(entity) : In<Entity>,
mut cache : ResMut<ReactCache>,
mut commands : Commands,
entity_reactors : Query<&EntityReactors>,
){
let rtype = EntityReactionType::Mutation(TypeId::of::<C>());
if let Ok(entity_reactors) = entity_reactors.get(entity)
{
let _ = schedule_entity_reaction_impl(&mut cache.reaction_commands_buffer, entity, rtype, &entity_reactors);
}
for command in cache.reaction_commands_buffer.drain(..) {
commands.queue(command);
}
if let Some(handlers) = cache.component_reactors.get(&TypeId::of::<C>())
{
for handle in handlers.mutation_callbacks.iter()
{
commands.queue(
ReactionCommand::EntityReaction{
reaction_source : entity,
reaction_type : rtype,
reactor : handle.sys_command(),
}
);
}
}
}
pub(crate) fn schedule_removal_reactions(&mut self, world: &mut World)
{
let mut buffer = self.removal_buffer.take().unwrap_or_else(|| Vec::default());
let mut commands_buff = std::mem::take(&mut self.reaction_commands_buffer);
for checker in &mut self.removal_checkers
{
buffer = checker.checker.call(world, buffer);
if buffer.len() == 0 { continue; }
let rtype = EntityReactionType::Removal(checker.component_id);
for entity in buffer.iter()
{
if let Some(entity_reactors) = world.get_mut::<EntityReactors>(*entity)
{
schedule_entity_reaction_impl(
&mut commands_buff,
*entity,
rtype,
&entity_reactors
);
}
for command in commands_buff.drain(..) {
world.commands().queue(command);
}
let Some(reactors) = self.component_reactors.get(&checker.component_id) else { continue; };
for handle in reactors.removal_callbacks.iter()
{
world.commands().queue(
ReactionCommand::EntityReaction{
reaction_source : *entity,
reaction_type : rtype,
reactor : handle.sys_command(),
}
);
}
}
}
self.removal_buffer = Some(buffer);
self.reaction_commands_buffer = commands_buff;
}
pub(crate) fn schedule_entity_event_reaction<E: Send + Sync + 'static>(
In((target, event)) : In<(Entity, E)>,
mut commands : Commands,
cache : Res<ReactCache>,
entity_reactors : Query<&EntityReactors>,
){
let entity_reactors = entity_reactors.get(target);
let handlers = cache.any_entity_event_reactors.get(&TypeId::of::<E>());
let reaction_type = EntityReactionType::Event(TypeId::of::<E>());
let num = entity_reactors.map(|e| e.count(reaction_type)).unwrap_or_default()
+ handlers.map(|h| h.len()).unwrap_or_default();
if num == 0 { return; }
let data_entity = commands.spawn((DataEntityCounter::new(num), EntityEventData::new(target, event))).id();
if let Ok(entity_reactors) = entity_reactors
{
for reactor in entity_reactors.iter_rtype(reaction_type)
{
commands.queue(
ReactionCommand::EntityEvent{
target,
data_entity,
reactor,
}
);
}
}
if let Some(handlers) = cache.any_entity_event_reactors.get(&TypeId::of::<E>())
{
for handle in handlers.iter()
{
commands.queue(
ReactionCommand::EntityEvent{
target,
data_entity,
reactor: handle.sys_command(),
}
);
}
}
}
pub(crate) fn schedule_despawn_reactions(&mut self, world: &mut World)
{
while let Ok(despawned_entity) = self.despawn_receiver.try_recv()
{
let Some(mut despawn_reactors) = self.despawn_reactors.remove(&despawned_entity) else { continue; };
for handle in despawn_reactors.drain(..)
{
world.commands().queue(
ReactionCommand::Despawn{
reaction_source : despawned_entity,
reactor : handle.sys_command(),
handle,
}
);
}
}
}
pub(crate) fn schedule_resource_mutation_reaction<R: ReactResource>(
cache : Res<ReactCache>,
mut commands : Commands,
){
let Some(handlers) = cache.resource_reactors.get(&TypeId::of::<R>()) else { return; };
for handle in handlers.iter()
{
commands.queue(
ReactionCommand::Resource{ reactor: handle.sys_command() }
);
}
}
pub(crate) fn schedule_broadcast_reaction<E: Send + Sync + 'static>(
In(event) : In<E>,
cache : Res<ReactCache>,
mut commands : Commands,
){
let Some(handlers) = cache.broadcast_reactors.get(&TypeId::of::<E>()) else { return; };
let num = handlers.len();
if num == 0 { return; }
let data_entity = commands.spawn((DataEntityCounter::new(num), BroadcastEventData::new(event))).id();
for handle in handlers.iter()
{
commands.queue(
ReactionCommand::BroadcastEvent{ data_entity, reactor: handle.sys_command() }
);
}
}
}
impl Default for ReactCache
{
fn default() -> Self
{
let (despawn_sender, despawn_receiver) = crossbeam::channel::unbounded();
Self{
reaction_commands_buffer : Vec::default(),
component_reactors : HashMap::default(),
tracked_removals : HashSet::default(),
removal_checkers : Vec::new(),
removal_buffer : None,
despawn_reactors : HashMap::new(),
despawn_sender,
despawn_receiver,
any_entity_event_reactors : HashMap::new(),
resource_reactors : HashMap::new(),
broadcast_reactors : HashMap::new(),
}
}
}