use crate::prelude::*;
use bevy::prelude::*;
fn cleanup_on_abort(world: &mut World, setup: SystemCommandSetup, cleanup: SystemCommandCleanup)
{
setup.run(world);
cleanup.run(world);
garbage_collect_entities(world);
schedule_removal_and_despawn_reactors(world);
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct SystemCommandSetup
{
reactor: SystemCommand,
setup: fn(&mut World, SystemCommand),
}
impl SystemCommandSetup
{
pub(crate) fn new(reactor: SystemCommand, setup: fn(&mut World, SystemCommand)) -> Self
{
Self { reactor, setup }
}
fn run(self, world: &mut World)
{
(self.setup)(world, self.reactor);
}
}
impl Default for SystemCommandSetup
{
fn default() -> Self
{
Self{
reactor: SystemCommand(Entity::PLACEHOLDER),
setup: |_, _| {}
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct BufferedSyscommand
{
command: SystemCommand,
setup: SystemCommandSetup,
cleanup: SystemCommandCleanup,
}
pub(crate) fn syscommand_runner(
world: &mut World,
command: SystemCommand,
setup: SystemCommandSetup,
cleanup: SystemCommandCleanup,
)
{
let idx = **world.resource::<SyscommandCounter>();
garbage_collect_entities(world);
schedule_removal_and_despawn_reactors(world);
let Ok(mut entity_mut) = world.get_entity_mut(*command)
else
{
cleanup_on_abort(world, setup, cleanup);
return
};
let Some(mut system_command) = entity_mut.get_mut::<SystemCommandStorage>()
else
{
tracing::error!(?command, "system command component is missing on extract");
cleanup_on_abort(world, setup, cleanup);
return
};
let Some(mut callback) = system_command.take()
else
{
if idx == 0 {
tracing::warn!(?command, "system command missing");
cleanup_on_abort(world, setup, cleanup);
} else {
tracing::debug!(?command, "deferring suspected recursive system command");
world.resource_mut::<CobwebCommandQueue<BufferedSyscommand>>().push(
BufferedSyscommand{ command, setup, cleanup }
);
}
return
};
**world.resource_mut::<SyscommandCounter>() += 1;
setup.run(world);
callback.run(world, cleanup);
garbage_collect_entities(world);
if let Ok(mut entity_mut) = world.get_entity_mut(*command)
{
if let Some(mut system_command) = entity_mut.get_mut::<SystemCommandStorage>()
{
system_command.insert(callback);
}
else
{
std::mem::drop(callback);
entity_mut.despawn_recursive();
tracing::error!(?command, "system command component is missing on insert");
garbage_collect_entities(world);
}
}
else
{
std::mem::drop(callback);
garbage_collect_entities(world);
}
schedule_removal_and_despawn_reactors(world);
let mut buffered_syscommands = world.resource_mut::<CobwebCommandQueue<BufferedSyscommand>>().remove();
buffered_syscommands
.retain(
|buffered|
{
if buffered.command == command
{
tracing::debug!(?command, "running reordered recursive system command");
syscommand_runner(world, buffered.command, buffered.setup, buffered.cleanup);
return false;
}
true
}
);
world.resource_mut::<CobwebCommandQueue<BufferedSyscommand>>().append(buffered_syscommands);
if idx == 0
{
while let Some(to_discard) = world.resource_mut::<CobwebCommandQueue<BufferedSyscommand>>().pop_front() {
tracing::warn!(?to_discard.command, "failed to run missing system command");
cleanup_on_abort(world, to_discard.setup, to_discard.cleanup);
}
**world.resource_mut::<SyscommandCounter>() = 0;
}
}