From 7050c761aa07bcbf6a7475929240507afe5b9a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= <kzm@zkat.tech> Date: Mon, 12 Apr 2021 23:17:51 -0700 Subject: [PATCH] stop leaking Scorers and Actions? Fixes: https://github.com/zkat/big-brain/issues/12 I *think* this fixes it, but I need to test it more thoroughly and make sure everything is actually ok --- examples/thirst.rs | 12 +++++++----- src/actions.rs | 12 ++++++------ src/choices.rs | 4 +++- src/scorers.rs | 8 +++++--- src/thinker.rs | 11 +++++++---- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/examples/thirst.rs b/examples/thirst.rs index b9bab55..062eeca 100644 --- a/examples/thirst.rs +++ b/examples/thirst.rs @@ -78,9 +78,9 @@ fn drink_action_system( // usually happen because the target action changed (due to a different // Scorer winning). But you can also cancel the actions yourself by // setting the state in the Action system. - mut query: Query<(&Parent, &mut ActionState), With<Drink>>, + mut query: Query<(&Actor, &mut ActionState), With<Drink>>, ) { - for (Parent(actor), mut state) in query.iter_mut() { + for (Actor(actor), mut state) in query.iter_mut() { // Use the drink_action's actor to look up the corresponding Thirst. if let Ok(mut thirst) = thirsts.get_mut(*actor) { match *state { @@ -128,9 +128,9 @@ impl ScorerBuilder for ThirstyBuilder { pub fn thirsty_scorer_system( thirsts: Query<&Thirst>, // Same dance with the Parent here, but now Big Brain has added a Score component! - mut query: Query<(&Parent, &mut Score), With<Thirsty>>, + mut query: Query<(&Actor, &mut Score), With<Thirsty>>, ) { - for (Parent(actor), mut score) in query.iter_mut() { + for (Actor(actor), mut score) in query.iter_mut() { if let Ok(thirst) = thirsts.get(*actor) { // This is really what the job of a Scorer is. To calculate a // generic Utility value that the Big Brain engine will compare @@ -153,13 +153,15 @@ pub fn init_entities(mut cmd: Commands) { let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id(); // And finally, we put all the pieces together! - Thinker::build() + let thinker = Thinker::build() .picker(FirstToScore { threshold: 80.0 }) // Note that what we pass in are _builders_, not components! .when(Thirsty::build(), Drink::build()) // .attach will do all the necessary work of attaching this component // and hooking it up to the AI system. .attach(&mut cmd, actor); + // TODO: this is a footgun and a pita. Please ignore. + cmd.entity(actor).push_children(&[thinker]); } fn main() { diff --git a/src/actions.rs b/src/actions.rs index 336540b..849752f 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use bevy::prelude::*; -use crate::ActionEnt; +use crate::{ActionEnt, Actor}; #[derive(Debug, Clone, Eq, PartialEq)] pub enum ActionState { @@ -42,8 +42,7 @@ pub trait ActionBuilder: std::fmt::Debug + Send + Sync { fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity); fn attach(&self, cmd: &mut Commands, actor: Entity) -> Entity { let action_ent = ActionEnt(cmd.spawn().id()); - cmd.entity(action_ent.0).insert(ActionState::new()); - cmd.entity(actor).push_children(&[action_ent.0]); + cmd.entity(action_ent.0).insert(ActionState::new()).insert(Actor(actor)); self.build(cmd, action_ent.0, actor); action_ent.0 } @@ -68,7 +67,7 @@ impl ActionBuilder for StepsBuilder { active_step: 0, active_ent: ActionEnt(child_action), steps: self.steps.clone(), - }); + }).push_children(&[child_action]); } } #[derive(Debug)] @@ -86,11 +85,11 @@ impl Steps { pub fn steps_system( mut cmd: Commands, - mut steps_q: Query<(Entity, &Parent, &mut Steps)>, + mut steps_q: Query<(Entity, &Actor, &mut Steps)>, mut states: Query<&mut ActionState>, ) { use ActionState::*; - for (seq_ent, Parent(actor), mut steps_action) in steps_q.iter_mut() { + 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(); match current_state { Requested => { @@ -131,6 +130,7 @@ pub fn steps_system( steps_action.active_step += 1; 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; } diff --git a/src/choices.rs b/src/choices.rs index 2058964..cc8a30e 100644 --- a/src/choices.rs +++ b/src/choices.rs @@ -36,7 +36,9 @@ impl ChoiceBuilder { } } - pub fn build(&self, cmd: &mut Commands, actor: Entity) -> Choice { + pub fn build(&self, cmd: &mut Commands, actor: Entity, parent: Entity) -> Choice { + let scorer_ent = self.when.attach(cmd, actor); + cmd.entity(parent).push_children(&[scorer_ent]); Choice { scorer: ScorerEnt(self.when.attach(cmd, actor)), action: ActionBuilderWrapper::new(self.then.clone()), diff --git a/src/scorers.rs b/src/scorers.rs index 19ee535..4ed4310 100644 --- a/src/scorers.rs +++ b/src/scorers.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use bevy::prelude::*; -use crate::ScorerEnt; +use crate::{Actor, ScorerEnt}; #[derive(Debug, Clone, Default)] pub struct Score(pub(crate) f32); @@ -23,8 +23,9 @@ pub trait ScorerBuilder: std::fmt::Debug + Sync + Send { fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity); fn attach(&self, cmd: &mut Commands, actor: Entity) -> Entity { let scorer_ent = cmd.spawn().id(); - cmd.entity(scorer_ent).insert(Score::default()); - cmd.entity(actor).push_children(&[scorer_ent]); + cmd.entity(scorer_ent) + .insert(Score::default()) + .insert(Actor(actor)); self.build(cmd, scorer_ent, actor); scorer_ent } @@ -114,6 +115,7 @@ impl ScorerBuilder for AllOrNothingBuilder { .collect(); cmd.entity(scorer) .insert(Score::default()) + .push_children(&scorers[..]) .insert(super::AllOrNothing { threshold: self.threshold, scorers: scorers.into_iter().map(ScorerEnt).collect(), diff --git a/src/thinker.rs b/src/thinker.rs index b8bcce6..a9ec2df 100644 --- a/src/thinker.rs +++ b/src/thinker.rs @@ -12,6 +12,9 @@ use crate::{ scorers::{Score, ScorerBuilder}, }; +#[derive(Debug, Clone, Copy)] +pub struct Actor(pub Entity); + #[derive(Debug, Clone, Copy)] pub struct ActionEnt(pub Entity); @@ -74,7 +77,7 @@ impl ActionBuilder for ThinkerBuilder { let choices = self .choices .iter() - .map(|choice| choice.build(cmd, actor)) + .map(|choice| choice.build(cmd, actor, action_ent)) .collect(); cmd.entity(action_ent) .insert(Thinker { @@ -116,12 +119,12 @@ impl Default for ThinkerIterations { pub fn thinker_system( mut cmd: Commands, mut iterations: Local<ThinkerIterations>, - mut thinker_q: Query<(Entity, &Parent, &mut Thinker, &ActiveThinker)>, + mut thinker_q: Query<(Entity, &Actor, &mut Thinker, &ActiveThinker)>, utilities: Query<&Score>, mut action_states: Query<&mut actions::ActionState>, ) { let start = Instant::now(); - for (thinker_ent, Parent(actor), mut thinker, active_thinker) in + for (thinker_ent, Actor(actor), mut thinker, active_thinker) in thinker_q.iter_mut().skip(iterations.index) { iterations.index += 1; @@ -189,7 +192,7 @@ pub fn thinker_system( actions::ActionState::Init | actions::ActionState::Success | actions::ActionState::Failure => { - cmd.entity(current.0 .0).despawn_recursive(); + cmd.entity(current.0.0).despawn_recursive(); thinker.current_action = None; } _ => { -- GitLab