use std::{fmt::Debug, sync::Mutex, vec};
use bevy::ecs::{
bundle::Bundle,
entity::Entity,
system::{Commands, In, IntoSystem, System},
world::World,
};
use crate::node::prelude::*;
pub mod prelude {
pub use super::{
TaskBridge, TaskChecker, TaskDefinition, TaskEvent, TaskEventListener, TaskStatus,
insert_while_running,
};
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskStatus {
Running,
Complete(NodeResult),
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(NodeState, Debug)]
struct TaskState;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskEvent {
Enter,
Exit,
Success,
Failure,
}
pub type TaskChecker = dyn System<In = In<Entity>, Out = TaskStatus>;
pub type TaskEventListener = dyn System<In = In<Entity>, Out = ()>;
#[cfg_attr(feature = "serde", typetag::serde(tag = "type"))]
pub trait TaskDefinition: 'static + Debug + Send + Sync {
fn build_checker(&self) -> Box<TaskChecker>;
fn build_event_listeners(&self) -> Vec<(TaskEvent, Box<TaskEventListener>)>;
}
pub fn insert_while_running<T: Bundle + 'static + Clone>(
bundle: T,
) -> Vec<(TaskEvent, Box<TaskEventListener>)> {
vec![
(
TaskEvent::Enter,
Box::new(IntoSystem::into_system(
move |In(entity), mut commands: Commands| {
commands.entity(entity).insert(bundle.clone());
},
)),
),
(
TaskEvent::Exit,
Box::new(IntoSystem::into_system(
|In(entity), mut commands: Commands| {
commands.entity(entity).remove::<T>();
},
)),
),
]
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug)]
#[with_state(TaskState)]
pub struct TaskBridge {
definition: Box<dyn TaskDefinition>,
#[cfg_attr(feature = "serde", serde(skip))]
checker: Mutex<Option<Box<TaskChecker>>>,
#[cfg_attr(feature = "serde", serde(skip))]
event_listeners: Mutex<Vec<(TaskEvent, Box<TaskEventListener>)>>,
}
impl TaskBridge {
pub fn new(definition: Box<dyn TaskDefinition>) -> Self {
Self {
definition,
checker: Mutex::new(None),
event_listeners: Mutex::new(vec![]),
}
}
fn check(&self, world: &mut World, entity: Entity) -> TaskStatus {
let mut checker = self.checker.lock().expect("Failed to lock.");
if checker.is_none() {
let mut built_checker = self.definition.build_checker();
built_checker.initialize(world);
*checker = Some(built_checker);
}
checker
.as_mut()
.expect("Checker should be some here.")
.run(entity, world)
.expect("Failed to run checker system.")
}
fn trigger_event(&self, world: &mut World, entity: Entity, event: TaskEvent) {
let mut listeners = self.event_listeners.lock().expect("Failed to lock.");
if listeners.is_empty() {
let mut built_listeners = self.definition.build_event_listeners();
built_listeners.iter_mut().for_each(|(_, sys)| {
sys.initialize(world);
});
*listeners = built_listeners;
}
listeners
.iter_mut()
.filter(|(ev, _)| *ev == event)
.for_each(|(_, sys)| {
sys.run(entity, world).expect("Failed to run event system.");
sys.apply_deferred(world);
});
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl Node for TaskBridge {
fn begin(&self, world: &mut World, entity: Entity) -> NodeStatus {
self.trigger_event(world, entity, TaskEvent::Enter);
self.resume(world, entity, Box::new(TaskState))
}
fn resume(&self, world: &mut World, entity: Entity, state: Box<dyn NodeState>) -> NodeStatus {
let state = Self::downcast(state).expect("Invalid state.");
let status = self.check(world, entity);
match status {
TaskStatus::Running => NodeStatus::Pending(Box::new(state)),
TaskStatus::Complete(result) => {
match result {
NodeResult::Success => self.trigger_event(world, entity, TaskEvent::Success),
NodeResult::Failure => self.trigger_event(world, entity, TaskEvent::Failure),
}
self.trigger_event(world, entity, TaskEvent::Exit);
NodeStatus::Complete(result)
}
}
}
fn force_exit(&self, world: &mut World, entity: Entity, state: Box<dyn NodeState>) {
let _state = Self::downcast(state).expect("Invalid state.");
self.trigger_event(world, entity, TaskEvent::Exit);
}
}