Forums › đź’¬ NodeCanvas › ⚙️ Support › Bug (?): OnStop() called after executing interrupting Action; Not atomic?
Hi There,
I utilize the OnStop() method in ActionTasks to clean-up an agents state within the ActionTask before moving into another state. I’ve ran into a problem though using the dynamic selector. It seems that when a higher priority state is activated, its first ActionTask is executed before OnStop() is called in the interrupted ActionTask. Please see the screen grab. Branch 1 runs and “Produce for 60 seconds” is executed. It gets interrupted by branch 2 before it is done processing. I would have expected OnStop() to be called in “Produce for 60 seconds” before the first ActionTask is executed in branch 2 ($owner.ReleaseTaskProvider()), but the opposite happens. $owner.ReleaseTaskProvider() is executed, then OnStop() is called on “Produce for 60 seconds”. I think, conceptually, an ActionTask should be an atomic operation and should complete its lifecycle before another is executed. Can you confirm if this is a bug, or is expected behaviour.
Thanks for your time and attention on this.
Geoff
Hello,
It is true that in such a case, the action on the left is executed before the OnStop method of the action on the right is called.
The reason why this happens, is that for the lower priority task (on the right) to be interrupted in the first place, a higher priority task (on the left) much already have returned Success, which means that for this to happen, it already is executed (and only after that the interruption takes place).
While I understand that conceptually it may make more sense the way you describe it, this is not really a bug, but rather an expected behaviour with the current implementation and how interruption works. Ideally, tasks should not be tightly coupled together in a sense that they depend on one another. What I mean by that, is that if a task required a specific “world state” for it to function properly, then it’s always best if it takes care of that state itself in it’s “OnExecute”, rather than requiring other tasks “OnStop” to do so.
Please let me know if that helps.
Thank you.
Hi there,
I understand your comments regarding the isolation of world state. It’s not exactly what’s happening in my case – all states are local to the agent executing the FSM. The problem is that I set some flags in “Produce for 60 seconds” and in OnStop() I reset those flags. Since $owner.ReleaseTaskProvider() assumes all those flags have been reset before executing, I end up with undesired behavior. To complicate matters, the screen grab that I sent was actually a simplification, the lower priority block in my actual use case is a dynamic nested FSM – it executes one of many possible jobs my agents may be assigned. My plan was to have each nested FSM clean-up any state changes it has made prior to executing the higher priority $owner.ReleaseTaskProvider() block. I’m not sure how to handle this now short of writing an ugly task that resets all possible data that could be modified in any one of the possible nested FSMs.
To your point about decoupling, I actual try to decouple tasks by making each task responsible for cleaning up after itself in OnStop().
I’ll try to figure out a workaround. Thanks for your clarification and time on this. Honestly, your asset is by far the best FSM and BT tool set I’ve come across, I appreciate your support.
Hello again,
Thank you very much for your kind feedback on NodeCanvas, as well as for the further details provided on the subject at hand.
I completely understand your points and I can see how calling OnStop before the next action OnExecute is called can be convenient. Even though it would be a hard task to make this happen considering the current Behaviour Trees implementation, I will definetely look at that possibility. It could probably be achieved with some delegate callback that will be called before an action is executed so that the previous running action if any is stopped, but I can’t confirm anything until I investigate this further, and which I will definetely do.
Thank you!
Fantastic, I look forward to any (potential) solution 🙂 Thanks in advance for any time you spend on this.
I also come across that. OnStop() called after executing interrupting action would make the AI design mess. I think it would be fine if BT call OnStop() when the action stops or interrupts.
Hello and sorry for the late reply due to Eastern!
Can you please clarify what you mean on your last point? OnStop, should be called when an action is interrupted as well as when an action is normally stoped (because it finished execution). You can determine whether it was an interuption or not, by using the special OnStop override with the argument (OnStop(bool isInterrupt)) by the way.
Let me know.
Thanks!
So since this thread has been resurrected, I figured I’d circle back and see if you have put anymore thought into this? I understand the challenges in implementing this functionality after looking at the code that was causing it, but I think it really deserves some attention. Without a fix, the bug invalidates the deterministic statefulness of the agent and really limits the complexity you can build into a dynamic behavior.
As always, thanks for your time.
