use std::{
future::Future,
mem,
sync::{Arc, OnceLock},
};
use super::{
node_builder::{NodeBuilder, TaskHolder, async_world},
raw::utils::remove_system_holder_on_remove,
};
use apply::Apply;
use bevy_ecs::{component::*, error::*, lifecycle::HookContext, prelude::*, system::*, world::*};
use bevy_log::error;
use bevy_tasks::Task;
use bevy_utils::prelude::*;
use enclose::enclose as clone;
use futures_signals::{
signal::{Signal, SignalExt},
signal_vec::{SignalVec, SignalVecExt},
};
pub struct RawHaalkaEl {
node_builder: Option<NodeBuilder>,
deferred_updaters: Vec<Box<dyn FnOnce(RawHaalkaEl) -> RawHaalkaEl + Send + 'static>>,
}
impl From<NodeBuilder> for RawHaalkaEl {
fn from(node_builder: NodeBuilder) -> Self {
Self {
node_builder: Some(node_builder),
..Self::new_dummy()
}
}
}
impl<NodeType: Bundle> From<NodeType> for RawHaalkaEl {
fn from(node_bundle: NodeType) -> Self {
Self {
node_builder: Some(NodeBuilder::from(node_bundle)),
..Self::new_dummy()
}
}
}
impl Default for RawHaalkaEl {
fn default() -> Self {
Self::new()
}
}
#[allow(missing_docs)]
pub enum DeferredUpdaterAppendDirection {
Front,
Back,
}
impl RawHaalkaEl {
fn new_dummy() -> Self {
Self {
node_builder: None,
deferred_updaters: default(),
}
}
#[allow(missing_docs)]
pub fn new() -> Self {
Self {
node_builder: Some(default()),
deferred_updaters: default(),
}
}
pub fn defer_update(
mut self,
append_direction: DeferredUpdaterAppendDirection,
updater: impl FnOnce(RawHaalkaEl) -> RawHaalkaEl + Send + 'static,
) -> Self {
match append_direction {
DeferredUpdaterAppendDirection::Front => self.deferred_updaters.insert(0, Box::new(updater)),
DeferredUpdaterAppendDirection::Back => self.deferred_updaters.push(Box::new(updater)),
}
self
}
pub fn update_node_builder(mut self, updater: impl FnOnce(NodeBuilder) -> NodeBuilder) -> Self {
self.node_builder = Some(updater(self.node_builder.unwrap()));
self
}
pub fn into_node_builder(mut self) -> NodeBuilder {
let deferred_updaters = self.deferred_updaters.drain(..).collect::<Vec<_>>();
let mut self_ = self;
for updater in deferred_updaters {
self_ = updater(self_);
}
self_.node_builder.unwrap()
}
pub fn on_spawn(self, on_spawn: impl FnOnce(&mut World, Entity) + Send + 'static) -> Self {
self.update_node_builder(|node_builder| node_builder.on_spawn(on_spawn))
}
pub fn on_spawn_with_system<T: IntoSystem<In<Entity>, (), Marker> + Send + 'static, Marker>(
self,
system: T,
) -> Self {
self.on_spawn(|world, entity| {
if let Err(error) = world.run_system_once_with(system, entity) {
error!("failed to run system on spawn: {}", error);
}
})
}
pub fn insert<B: Bundle>(self, bundle: B) -> Self {
self.update_node_builder(|node_builder| node_builder.insert(bundle))
}
pub fn insert_forwarded(
self,
forwarder: impl FnOnce(&mut EntityWorldMut) -> Option<Entity> + Send + 'static,
bundle: impl Bundle + 'static,
) -> Self {
self.with_entity_forwarded(forwarder, move |mut entity| {
entity.insert(bundle);
})
}
pub fn with_entity(self, f: impl FnOnce(EntityWorldMut) + Send + 'static) -> Self {
self.update_node_builder(|node_builder| node_builder.with_entity(f))
}
pub fn with_entity_forwarded(
self,
forwarder: impl FnOnce(&mut EntityWorldMut) -> Option<Entity> + Send + 'static,
f: impl FnOnce(EntityWorldMut) + Send + 'static,
) -> Self {
self.with_entity(move |mut entity| {
if let Some(forwardee) = forwarder(&mut entity) {
entity.world_scope(|world| {
if let Ok(forwardee) = world.get_entity_mut(forwardee) {
f(forwardee)
}
})
}
})
}
pub fn with_component<C: Component<Mutability = Mutable>>(self, f: impl FnOnce(Mut<C>) + Send + 'static) -> Self {
self.with_entity(|mut entity| {
if let Some(component) = entity.get_mut::<C>() {
f(component);
}
})
}
pub fn with_component_forwarded<C: Component<Mutability = Mutable>>(
self,
forwarder: impl FnOnce(&mut EntityWorldMut) -> Option<Entity> + Send + 'static,
f: impl FnOnce(Mut<C>) + Send + 'static,
) -> Self {
self.with_entity_forwarded(forwarder, move |mut entity| {
if let Some(component) = entity.get_mut::<C>() {
f(component)
}
})
}
pub fn observe<E: Event, B: Bundle, Marker>(self, observer: impl IntoObserverSystem<E, B, Marker>) -> Self {
self.on_spawn(|world, entity| {
observe(world, entity, observer);
})
}
pub fn hold_tasks(self, tasks: impl IntoIterator<Item = Task<()>> + Send + 'static) -> Self {
self.with_component::<TaskHolder>(|task_holder| {
for task in tasks.into_iter() {
task_holder.hold(task);
}
})
}
pub fn on_remove(self, on_remove: impl FnOnce(&mut DeferredWorld, Entity) + Send + Sync + 'static) -> Self {
self.on_spawn(|world, entity| {
if let Some(mut on_remove_component) = world.entity_mut(entity).get_mut::<OnRemove>() {
on_remove_component.0.push(Box::new(on_remove));
} else {
world.entity_mut(entity).insert(OnRemove(vec![Box::new(on_remove)]));
}
})
}
pub fn on_signal<T, Fut: Future<Output = ()> + Send + 'static>(
self,
signal: impl Signal<Item = T> + Send + 'static,
f: impl FnMut(Entity, T) -> Fut + Send + 'static,
) -> Self {
self.update_node_builder(|node_builder| node_builder.on_signal(signal, f))
}
pub fn on_signal_sync<T>(
self,
signal: impl Signal<Item = T> + Send + 'static,
mut f: impl FnMut(Entity, T) + Send + 'static,
) -> Self {
self.on_signal(signal, move |entity, value| {
f(entity, value);
async {}
})
}
pub fn on_signal_with_system<T: Send + 'static, Marker>(
self,
signal: impl Signal<Item = T> + Send + 'static,
system: impl IntoSystem<In<(Entity, T)>, (), Marker> + Send + 'static,
) -> Self {
let system_holder = Arc::new(OnceLock::new());
self.on_spawn(clone!((system_holder) move |world, _| {
let _ = system_holder.set(register_system(world, system));
}))
.on_signal(
signal,
clone!((system_holder) move |entity, input| {
async_world().apply(run_system_with_entity(entity, system_holder.get().copied().unwrap(), input).handle_error_with(warn))
}),
)
.apply(remove_system_holder_on_remove(system_holder))
}
#[allow(clippy::type_complexity)]
pub fn on_signal_with_system_forwarded<T: Send + 'static, Marker1, Marker2>(
self,
signal: impl Signal<Item = T> + Send + 'static,
forwarder: impl IntoSystem<In<Entity>, Option<Entity>, Marker1> + Send + 'static,
system: impl IntoSystem<In<(Entity, T)>, (), Marker2> + Send + 'static,
) -> Self {
let forwarder_system_holder = Arc::new(OnceLock::new());
let handler_system_holder = Arc::new(OnceLock::new());
self.on_spawn(clone!((forwarder_system_holder, handler_system_holder) move |world, _| {
let _ = forwarder_system_holder.set(register_system(world, forwarder));
let _ = handler_system_holder.set(register_system(world, system));
}))
.on_signal_with_system(
signal,
clone!((forwarder_system_holder, handler_system_holder) move |In((entity, input)): In<(Entity, T)>, mut commands: Commands| {
commands.queue(clone!((forwarder_system_holder, handler_system_holder) move |world: &mut World| {
if let Ok(Some(forwardee)) = world.run_system_with(forwarder_system_holder.get().copied().unwrap(), entity) {
let _ = world.run_system_with(handler_system_holder.get().copied().unwrap(), (forwardee, input));
}
}))
}),
)
.apply(remove_system_holder_on_remove(forwarder_system_holder))
.apply(remove_system_holder_on_remove(handler_system_holder))
}
pub fn on_signal_with_entity<T: Send + 'static>(
self,
signal: impl Signal<Item = T> + Send + 'static,
mut f: impl FnMut(EntityWorldMut, T) + Send + Sync + 'static,
) -> Self {
self.on_signal_with_system(
signal,
move |In((entity, value)): In<(Entity, T)>, world: &mut World| {
if let Ok(entity) = world.get_entity_mut(entity) {
f(entity, value)
}
},
)
}
pub fn on_signal_with_entity_forwarded<T: Send + 'static, Marker>(
self,
signal: impl Signal<Item = T> + Send + 'static,
forwarder: impl IntoSystem<In<Entity>, Option<Entity>, Marker> + Send + 'static,
mut f: impl FnMut(EntityWorldMut, T) + Send + Sync + 'static,
) -> Self {
self.on_signal_with_system_forwarded(
signal,
forwarder,
move |In((entity, value)): In<(Entity, T)>, world: &mut World| {
if let Ok(entity) = world.get_entity_mut(entity) {
f(entity, value)
}
},
)
}
pub fn on_signal_with_component<T: Send + 'static, C: Component<Mutability = Mutable>>(
self,
signal: impl Signal<Item = T> + Send + 'static,
mut f: impl FnMut(Mut<C>, T) + Send + Sync + 'static,
) -> Self {
self.on_signal_with_system(
signal,
move |In((entity, value)): In<(Entity, T)>, mut query: Query<&mut C>| {
if let Ok(component) = query.get_mut(entity) {
f(component, value)
}
},
)
}
pub fn on_signal_with_component_forwarded<T: Send + 'static, C: Component<Mutability = Mutable>, Marker>(
self,
signal: impl Signal<Item = T> + Send + 'static,
forwarder: impl IntoSystem<In<Entity>, Option<Entity>, Marker> + Send + 'static,
mut f: impl FnMut(Mut<C>, T) + Send + Sync + 'static,
) -> Self {
self.on_signal_with_system_forwarded(
signal,
forwarder,
move |In((entity, value)): In<(Entity, T)>, mut query: Query<&mut C>| {
if let Ok(component) = query.get_mut(entity) {
f(component, value)
}
},
)
}
pub fn component_signal<C: Component, S: Signal<Item = impl Into<Option<C>>> + Send + 'static>(
mut self,
component_option_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(component_option_signal) = component_option_signal_option.into() {
self = self.on_signal_with_entity::<Option<C>>(
component_option_signal.map(|into_component_option| into_component_option.into()),
move |mut entity, component_option| {
if let Some(component) = component_option {
entity.insert(component);
} else {
entity.remove::<C>();
}
},
);
}
self
}
pub fn component_signal_forwarded<C: Component, Marker>(
self,
forwarder: impl IntoSystem<In<Entity>, Option<Entity>, Marker> + Send + 'static,
component_option_signal: impl Signal<Item = impl Into<Option<C>>> + Send + 'static,
) -> Self {
self.on_signal_with_entity_forwarded(
component_option_signal.map(|into_component_option| into_component_option.into()),
forwarder,
move |mut entity, component_option| {
if let Some(component) = component_option {
entity.insert(component);
} else {
entity.remove::<C>();
}
},
)
}
pub fn on_signal_send_message<T, E: Message>(
self,
signal: impl Signal<Item = T> + Send + 'static,
mut f: impl FnMut(Entity, T) -> E + Send + 'static,
) -> Self {
self.on_signal(signal, move |entity, value| {
async_world().send_message(f(entity, value))
})
}
pub fn child<IORE: IntoOptionRawElement>(self, child_option: IORE) -> Self {
if let Some(child) = child_option.into_option_element() {
return self.update_node_builder(|node_builder| node_builder.child(child.into_raw().into_node_builder()));
}
self
}
pub fn child_signal<IORE: IntoOptionRawElement>(
self,
child_option_signal: impl Signal<Item = IORE> + Send + 'static,
) -> Self {
self.update_node_builder(|node_builder| {
node_builder.child_signal(child_option_signal.map(|child_option| {
child_option
.into_option_element()
.map(|child| child.into_raw().into_node_builder())
}))
})
}
pub fn children<IORE: IntoOptionRawElement, I: IntoIterator<Item = IORE>>(self, children_options: I) -> Self
where
I::IntoIter: Send + 'static,
{
self.update_node_builder(|node_builder| {
node_builder.children(
children_options
.into_iter()
.filter_map(|child_option| child_option.into_option_element())
.map(|child| child.into_raw().into_node_builder()),
)
})
}
pub fn children_signal_vec<IORE: IntoOptionRawElement>(
self,
children_options_signal_vec: impl SignalVec<Item = IORE> + Send + 'static,
) -> Self {
self.update_node_builder(|node_builder| {
node_builder.children_signal_vec(
children_options_signal_vec
.filter_map(|child_option| child_option.into_option_element())
.map(|child| child.into_raw().into_node_builder()),
)
})
}
}
fn run_system_with_entity<I: Send + 'static>(
entity: Entity,
id: SystemId<In<(Entity, I)>>,
input: I,
) -> impl Command<Result> {
move |world: &mut World| -> Result {
if world.get_entity(entity).is_ok() {
world.run_system_with(id, (entity, input))?;
}
Ok(())
}
}
fn on_remove_on_remove(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
let fs = world
.get_mut::<OnRemove>(entity)
.unwrap()
.0
.drain(..)
.collect::<Vec<_>>();
for f in fs {
f(&mut world, entity);
}
}
#[allow(clippy::type_complexity)]
#[derive(Component)]
#[component(on_remove = on_remove_on_remove)]
struct OnRemove(Vec<Box<dyn FnOnce(&mut DeferredWorld, Entity) + Send + Sync + 'static>>);
#[derive(Component)]
pub struct HaalkaOneShotSystem;
pub(crate) fn register_system<I: SystemInput + 'static, O: 'static, Marker, S: IntoSystem<I, O, Marker> + 'static>(
world: &mut World,
system: S,
) -> SystemId<I, O> {
let system = world.register_system(system);
if let Ok(mut entity) = world.get_entity_mut(system.entity()) {
entity.insert(HaalkaOneShotSystem);
}
system
}
#[derive(Component)]
pub struct HaalkaObserver;
pub(crate) fn observe<E: Event, B: Bundle, Marker>(
world: &mut World,
entity: Entity,
observer: impl IntoObserverSystem<E, B, Marker>,
) -> EntityWorldMut<'_> {
world.spawn((Observer::new(observer).with_entity(entity), HaalkaObserver))
}
pub trait RawElement: Sized {
fn into_raw(self) -> RawHaalkaEl;
}
impl<REW: RawElWrapper> RawElement for REW {
fn into_raw(self) -> RawHaalkaEl {
self.into_raw_el()
}
}
pub trait IntoRawElement {
type EL: RawElement;
fn into_raw_element(self) -> Self::EL;
}
impl<T: RawElement> IntoRawElement for T {
type EL = T;
fn into_raw_element(self) -> Self::EL {
self
}
}
pub trait IntoOptionRawElement {
type EL: RawElement;
fn into_option_element(self) -> Option<Self::EL>;
}
impl<E: RawElement, IE: IntoRawElement<EL = E>> IntoOptionRawElement for Option<IE> {
type EL = E;
fn into_option_element(self) -> Option<Self::EL> {
self.map(|into_element| into_element.into_raw_element())
}
}
impl<E: RawElement, IE: IntoRawElement<EL = E>> IntoOptionRawElement for IE {
type EL = E;
fn into_option_element(self) -> Option<Self::EL> {
Some(self.into_raw_element())
}
}
pub trait RawElWrapper: Sized {
fn raw_el_mut(&mut self) -> &mut RawHaalkaEl;
fn update_raw_el(mut self, updater: impl FnOnce(RawHaalkaEl) -> RawHaalkaEl) -> Self {
let raw_el = mem::replace(self.raw_el_mut(), RawHaalkaEl::new_dummy());
*self.raw_el_mut() = updater(raw_el);
self
}
fn into_raw_el(mut self) -> RawHaalkaEl {
mem::replace(self.raw_el_mut(), RawHaalkaEl::new_dummy())
}
}
impl RawElement for RawHaalkaEl {
fn into_raw(self) -> RawHaalkaEl {
self
}
}
pub trait Spawnable: RawElement
where
Self: Sized,
{
fn spawn(self, world: &mut World) -> Entity {
self.into_raw().into_node_builder().spawn(world)
}
}
impl<REW: RawElement> Spawnable for REW {}
#[allow(missing_docs)]
pub mod utils {
use super::*;
pub fn remove_system_on_remove<I: SystemInput + 'static, O: 'static>(
getter: impl FnOnce() -> Option<SystemId<I, O>> + Send + Sync + 'static,
) -> impl FnOnce(RawHaalkaEl) -> RawHaalkaEl {
|raw_el| {
raw_el.on_remove(move |world, _| {
if let Some(system) = getter() {
world.commands().queue(move |world: &mut World| {
let _ = world.unregister_system(system);
})
}
})
}
}
pub fn remove_system_holder_on_remove<I: SystemInput + 'static, O: 'static>(
system_holder: Arc<OnceLock<SystemId<I, O>>>,
) -> impl FnOnce(RawHaalkaEl) -> RawHaalkaEl {
remove_system_on_remove(move || system_holder.get().copied())
}
pub fn flush_deferred_updaters<T: RawElement>(raw_el: T) -> RawHaalkaEl {
raw_el.into_raw().into_node_builder().apply(RawHaalkaEl::from)
}
}