use crate::prelude::*;
use bevy::prelude::*;
use std::hash::Hash;
#[derive(Component)]
struct SpawnedSystem<I, O>
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
{
system: Option<CallbackSystem<I, O>>,
}
impl<I, O> SpawnedSystem<I, O>
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
{
fn new(system: CallbackSystem<I,O>) -> Self
{
Self{ system: Some(system) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysId(Entity);
impl SysId
{
pub fn new(entity: Entity) -> Self { Self(entity) }
pub fn entity(&self) -> Entity
{
self.0
}
}
pub fn spawn_system<I, O, S, Marker>(world: &mut World, system: S) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static,
{
spawn_system_from(world, CallbackSystem::new(system))
}
pub fn spawn_system_from<I, O>(world: &mut World, system: CallbackSystem<I, O>) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
{
SysId::new(world.spawn(SpawnedSystem::new(system)).id())
}
pub fn spawn_rc_system<I, O, S, Marker>(world: &mut World, system: S) -> AutoDespawnSignal
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static,
{
spawn_rc_system_from(world, CallbackSystem::new(system))
}
pub fn spawn_rc_system_from<I, O>(world: &mut World, system: CallbackSystem<I, O>) -> AutoDespawnSignal
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
{
let sys_id = spawn_system_from(world, system);
world.resource::<AutoDespawner>().prepare(sys_id.0)
}
pub fn spawned_syscall<I, O>(world: &mut World, sys_id: SysId, input: <I as SystemInput>::Inner<'_>) -> Result<O, ()>
where
I: Send + Sync + SystemInput + 'static, <I as SystemInput>::Inner<'static>: Send,
O: Send + Sync + 'static,
{
let Ok(mut entity_mut) = world.get_entity_mut(sys_id.0) else { return Err(()); };
let Some(mut spawned_system) = entity_mut.get_mut::<SpawnedSystem<I, O>>()
else { tracing::error!(?sys_id, "spawned system component is missing"); return Err(()); };
let Some(mut callback) = spawned_system.system.take()
else { tracing::warn!(?sys_id, "recursive spawned system call detected"); return Err(()); };
let result = callback.run(world, input).ok_or(())?;
let Ok(mut entity_mut) = world.get_entity_mut(sys_id.0) else { return Ok(result); };
let Some(mut spawned_system) = entity_mut.get_mut::<SpawnedSystem<I, O>>()
else { tracing::error!(?sys_id, "spawned system component is missing"); return Ok(result); };
spawned_system.system = Some(callback);
Ok(result)
}
pub trait SpawnedSyscallCommandsExt
{
fn spawn_system<I, O, S, Marker>(&mut self, system: S) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static;
fn spawn_system_from<I, O>(&mut self, system: CallbackSystem<I, O>) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static;
fn insert_system<I, O, S, Marker>(&mut self, entity: Entity, system: S) -> Result<(), ()>
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static;
fn spawned_syscall<I>(&mut self, sys_id: SysId, input: <I as bevy::prelude::SystemInput>::Inner<'static>)
where
I: Send + Sync + SystemInput + 'static, <I as SystemInput>::Inner<'static>: Send;
}
impl<'w, 's> SpawnedSyscallCommandsExt for Commands<'w, 's>
{
fn spawn_system<I, O, S, Marker>(&mut self, system: S) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static
{
self.spawn_system_from(CallbackSystem::new(system))
}
fn spawn_system_from<I, O>(&mut self, system: CallbackSystem<I, O>) -> SysId
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static
{
SysId::new(self.spawn(SpawnedSystem::new(system)).id())
}
fn insert_system<I, O, S, Marker>(&mut self, entity: Entity, system: S) -> Result<(), ()>
where
I: Send + Sync + SystemInput + 'static,
O: Send + Sync + 'static,
S: IntoSystem<I, O, Marker> + Send + Sync + 'static
{
let Some(mut entity) = self.get_entity(entity) else { return Err(()); };
entity.insert(SpawnedSystem::new(CallbackSystem::new(system)));
Ok(())
}
fn spawned_syscall<I>(&mut self, sys_id: SysId, input: <I as SystemInput>::Inner<'static>)
where
I: Send + Sync + SystemInput + 'static, <I as SystemInput>::Inner<'static>: Send
{
self.queue(
move |world: &mut World|
{
if let Err(_) = spawned_syscall::<I, ()>(world, sys_id, input.into())
{
tracing::warn!(?sys_id, "spawned syscall failed");
}
}
);
}
}