use crate::prelude::*;
use bevy::ecs::world::Command;
use bevy::prelude::*;
use std::any::TypeId;
fn try_cleanup_data_entity(world: &mut World, entity: Entity)
{
let Some(mut counter) = world.get_mut::<DataEntityCounter>(entity) else { return };
counter.decrement();
if counter.is_done() {
world.despawn(entity);
}
}
fn start_system_event(world: &mut World, system: SystemCommand)
{
world.resource_mut::<SystemEventAccessTracker>().start(system);
}
fn end_system_event(world: &mut World)
{
let data_entity = world.resource_mut::<SystemEventAccessTracker>().end();
world.despawn(data_entity);
}
fn start_entity_reaction(world: &mut World, reactor: SystemCommand)
{
world.resource_mut::<EntityReactionAccessTracker>().start(reactor);
}
fn end_entity_reaction(world: &mut World)
{
world.resource_mut::<EntityReactionAccessTracker>().end();
}
fn start_despawn_reaction(world: &mut World, reactor: SystemCommand)
{
world.resource_mut::<DespawnAccessTracker>().start(reactor);
}
fn end_despawn_reaction(world: &mut World)
{
world.resource_mut::<DespawnAccessTracker>().end();
}
fn start_entity_event(world: &mut World, reactor: SystemCommand)
{
start_entity_reaction(world, reactor);
world.resource_mut::<EventAccessTracker>().start(reactor);
}
fn end_entity_event(world: &mut World)
{
end_entity_reaction(world);
let data_entity = world.resource_mut::<EventAccessTracker>().end();
try_cleanup_data_entity(world, data_entity);
}
fn start_broadcast_event(world: &mut World, reactor: SystemCommand)
{
world.resource_mut::<EventAccessTracker>().start(reactor);
}
fn end_broadcast_event(world: &mut World)
{
let data_entity = world.resource_mut::<EventAccessTracker>().end();
try_cleanup_data_entity(world, data_entity);
}
#[derive(Component)]
pub(crate) struct DataEntityCounter
{
count: usize
}
impl DataEntityCounter
{
pub(crate) fn new(count: usize) -> Self
{
Self{ count }
}
fn decrement(&mut self)
{
self.count = self.count.saturating_sub(1);
}
fn is_done(&self) -> bool
{
self.count == 0
}
}
#[derive(Debug, Copy, Clone, Deref, Eq, PartialEq)]
pub struct SystemCommand(pub Entity);
impl Command for SystemCommand
{
fn apply(self, world: &mut World)
{
syscommand_runner(world, self, SystemCommandSetup::default(), SystemCommandCleanup::default());
}
}
impl From<RevokeToken> for SystemCommand
{
fn from(token: RevokeToken) -> Self
{
token.id
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct EventCommand
{
pub(crate) system: SystemCommand,
pub(crate) data_entity: Entity,
}
impl Command for EventCommand
{
fn apply(self, world: &mut World)
{
world.resource_mut::<SystemEventAccessTracker>().prepare(self.system, self.data_entity);
syscommand_runner(
world,
self.system,
SystemCommandSetup::new(self.system, start_system_event),
SystemCommandCleanup::new(end_system_event)
);
}
}
#[derive(Clone)]
pub(crate) enum ReactionCommand
{
Resource
{
reactor: SystemCommand,
},
EntityReaction
{
reaction_source: Entity,
reaction_type: EntityReactionType,
reactor: SystemCommand,
},
Despawn
{
reaction_source: Entity,
reactor: SystemCommand,
handle: ReactorHandle,
},
EntityEvent
{
target: Entity,
data_entity: Entity,
reactor: SystemCommand,
},
BroadcastEvent
{
data_entity: Entity,
reactor: SystemCommand,
},
}
impl Command for ReactionCommand
{
fn apply(self, world: &mut World)
{
match self
{
Self::Resource{ reactor } =>
{
syscommand_runner(world, reactor, SystemCommandSetup::default(), SystemCommandCleanup::default());
}
Self::EntityReaction{ reaction_source, reaction_type, reactor } =>
{
world.resource_mut::<EntityReactionAccessTracker>().prepare(reactor, reaction_source, reaction_type);
syscommand_runner(
world,
reactor,
SystemCommandSetup::new(reactor, start_entity_reaction),
SystemCommandCleanup::new(end_entity_reaction)
);
}
Self::Despawn{ reaction_source, reactor, handle } =>
{
world.resource_mut::<DespawnAccessTracker>().prepare(reactor, reaction_source, handle);
syscommand_runner(
world,
reactor,
SystemCommandSetup::new(reactor, start_despawn_reaction),
SystemCommandCleanup::new(end_despawn_reaction));
}
Self::EntityEvent{ target, data_entity, reactor } =>
{
world.resource_mut::<EntityReactionAccessTracker>().prepare(
reactor,
target,
EntityReactionType::Event(TypeId::of::<()>()),
);
world.resource_mut::<EventAccessTracker>().prepare(reactor, data_entity);
syscommand_runner(world,
reactor,
SystemCommandSetup::new(reactor, start_entity_event),
SystemCommandCleanup::new(end_entity_event)
);
}
Self::BroadcastEvent{ data_entity, reactor } =>
{
world.resource_mut::<EventAccessTracker>().prepare(reactor, data_entity);
syscommand_runner(world,
reactor,
SystemCommandSetup::new(reactor, start_broadcast_event),
SystemCommandCleanup::new(end_broadcast_event)
);
}
}
}
}