From f33315c9b7b769a94baab17e3a9df9f5ebe924d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilbert=20R=C3=B6hrbein?= <payload.git@mailbox.org> Date: Sun, 19 Sep 2021 17:33:32 +0200 Subject: [PATCH] fix(systems): Fix steps, add a test and explicit systems ordering (#27) Fixes: #26 * add test for steps and fixes * define system ordering --- src/actions.rs | 22 ++++---- src/lib.rs | 37 ++++++++---- tests/steps.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 tests/steps.rs diff --git a/src/actions.rs b/src/actions.rs index 5475a54..9a0c09d 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -175,17 +175,16 @@ pub fn steps_system( ) { use ActionState::*; for (seq_ent, Actor(actor), mut steps_action) in steps_q.iter_mut() { - let current_state = states.get_mut(seq_ent).expect("uh oh").clone(); + let active_ent = steps_action.active_ent.0; + let current_state = states.get_mut(seq_ent).unwrap().clone(); match current_state { Requested => { // Begin at the beginning - let mut step_state = states.get_mut(steps_action.active_ent.0).expect("oops"); - *step_state = Requested; - let mut current_state = states.get_mut(seq_ent).expect("uh oh"); - *current_state = Executing; + *states.get_mut(active_ent).unwrap() = Requested; + *states.get_mut(seq_ent).unwrap() = Executing; } Executing => { - let mut step_state = states.get_mut(steps_action.active_ent.0).expect("bug"); + let mut step_state = states.get_mut(active_ent).unwrap(); match *step_state { Init => { // Request it! This... should not really happen? But just in case I'm missing something... :) @@ -216,15 +215,18 @@ pub fn steps_system( let step_builder = steps_action.steps[steps_action.active_step].clone(); let step_ent = step_builder.attach(&mut cmd, *actor); cmd.entity(seq_ent).push_children(&[step_ent]); - let mut step_state = states.get_mut(step_ent).expect("oops"); - *step_state = ActionState::Requested; + steps_action.active_ent.0 = step_ent; } } } Cancelled => { // Cancel current action - let mut step_state = states.get_mut(steps_action.active_ent.0).expect("oops"); - *step_state = ActionState::Cancelled; + let mut step_state = states.get_mut(active_ent).expect("oops"); + if *step_state == Requested || *step_state == Executing { + *step_state = Cancelled; + } else if *step_state == Failure || *step_state == Success { + *states.get_mut(seq_ent).unwrap() = step_state.clone(); + } } Init | Success | Failure => { // Do nothing. diff --git a/src/lib.rs b/src/lib.rs index 3a0c6ed..55aa2d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,16 +193,31 @@ pub struct BigBrainPlugin; impl Plugin for BigBrainPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_system(thinker::thinker_system.system()); - app.add_system(thinker::thinker_component_attach_system.system()); - app.add_system(thinker::thinker_component_detach_system.system()); - app.add_system(thinker::actor_gone_cleanup.system()); - app.add_system(actions::steps_system.system()); - app.add_system(actions::concurrent_system.system()); - app.add_system(scorers::fixed_score_system.system()); - app.add_system(scorers::all_or_nothing_system.system()); - app.add_system(scorers::sum_of_scorers_system.system()); - app.add_system(scorers::winning_scorer_system.system()); - app.add_system(scorers::evaluating_scorer_system.system()); + use CoreStage::*; + app.add_system_set_to_stage( + First, + SystemSet::new() + .with_system(scorers::fixed_score_system.system()) + .with_system(scorers::all_or_nothing_system.system()) + .with_system(scorers::sum_of_scorers_system.system()) + .with_system(scorers::winning_scorer_system.system()) + .with_system(scorers::evaluating_scorer_system.system()) + .label("scorers"), + ); + app.add_system_to_stage(First, thinker::thinker_system.system().after("scorers")); + + app.add_system_set_to_stage( + PreUpdate, + SystemSet::new() + .with_system(actions::steps_system.system()) + .with_system(actions::concurrent_system.system()) + .label("aggregate-actions"), + ); + + // run your actions in PreUpdate after aggregate-actions or in a later stage + + app.add_system_to_stage(Last, thinker::thinker_component_attach_system.system()); + app.add_system_to_stage(Last, thinker::thinker_component_detach_system.system()); + app.add_system_to_stage(Last, thinker::actor_gone_cleanup.system()); } } diff --git a/tests/steps.rs b/tests/steps.rs new file mode 100644 index 0000000..7767818 --- /dev/null +++ b/tests/steps.rs @@ -0,0 +1,150 @@ +use bevy::{app::AppExit, prelude::*}; +use big_brain::{pickers, prelude::*}; + +#[test] +fn steps() { + println!("steps test"); + App::build() + .add_plugins(MinimalPlugins) + .add_plugin(BigBrainPlugin) + .init_resource::<GlobalState>() + .add_startup_system(setup.system()) + .add_system_to_stage(CoreStage::First, no_failure_score.system()) + .add_system(action1.system()) + .add_system(action2.system()) + .add_system(exit_action.system()) + .add_system(failure_action.system()) + .add_system_to_stage(CoreStage::Last, last.system()) + .run(); + println!("end"); +} + +fn setup(mut cmds: Commands) { + cmds.spawn().insert( + Thinker::build() + .picker(pickers::FirstToScore::new(0.5)) + .when(NoFailureScore, Steps::build().step(FailureAction)) + .otherwise(Steps::build().step(Action1).step(Action2).step(ExitAction)), + ); +} + +#[derive(Default, Debug, Clone)] +struct Action1; +impl ActionBuilder for Action1 { + fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { + cmd.entity(action) + .insert(self.clone()) + .insert(ActionState::Requested); + } +} + +fn action1(mut query: Query<(&Actor, &mut ActionState), With<Action1>>) { + for (Actor(_actor), mut state) in query.iter_mut() { + println!("action1 {:?}", state); + if *state == ActionState::Requested { + *state = ActionState::Executing; + } + if *state == ActionState::Executing { + *state = ActionState::Success; + } + } +} + +#[derive(Default, Debug, Clone)] +struct Action2; +impl ActionBuilder for Action2 { + fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { + cmd.entity(action) + .insert(self.clone()) + .insert(ActionState::Requested); + } +} + +fn action2(mut query: Query<(&Actor, &mut ActionState), With<Action2>>) { + for (Actor(_actor), mut state) in query.iter_mut() { + println!("action2 {:?}", state); + if *state == ActionState::Requested { + *state = ActionState::Executing; + } + if *state == ActionState::Executing { + *state = ActionState::Success; + } + } +} + +#[derive(Default, Debug, Clone)] +struct ExitAction; +impl ActionBuilder for ExitAction { + fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { + cmd.entity(action) + .insert(self.clone()) + .insert(ActionState::Requested); + } +} + +fn exit_action( + mut query: Query<(&Actor, &mut ActionState), With<ExitAction>>, + mut app_exit_events: EventWriter<AppExit>, +) { + for (Actor(_actor), mut state) in query.iter_mut() { + println!("exit_action {:?}", state); + if *state == ActionState::Requested { + *state = ActionState::Executing; + } + if *state == ActionState::Executing { + app_exit_events.send(AppExit); + } + } +} + +fn last() { + println!(); +} + +#[derive(Default, Debug, Clone)] +struct FailureAction; +impl ActionBuilder for FailureAction { + fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { + cmd.entity(action) + .insert(self.clone()) + .insert(ActionState::Requested); + } +} + +fn failure_action( + mut query: Query<(&Actor, &mut ActionState), With<FailureAction>>, + mut global_state: ResMut<GlobalState>, +) { + for (Actor(_actor), mut state) in query.iter_mut() { + println!("failure_action {:?}", state); + if *state == ActionState::Requested { + *state = ActionState::Executing; + } + if *state == ActionState::Executing { + global_state.failure = true; + *state = ActionState::Failure; + } + } +} + +#[derive(Default)] +struct GlobalState { + failure: bool, +} + +#[derive(Debug, Clone)] +struct NoFailureScore; +impl ScorerBuilder for NoFailureScore { + fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { + cmd.entity(action).insert(self.clone()); + } +} + +fn no_failure_score( + mut query: Query<(&NoFailureScore, &mut Score)>, + global_state: Res<GlobalState>, +) { + for (_, mut score) in query.iter_mut() { + score.set(if global_state.failure { 0.0 } else { 1.0 }); + } +} -- GitLab