AController::UnPossess vs StateTreeAIComponent

I have this setup:

AIController with StateTreeAIComponent

  • bStopAILogicOnUnposses = True
  • A State Tree is running a Task that starts an external GameplayTask (and correctly terminates it in ExitState)

Now suddenly, the actor is destroyed, and UnPossess is called:

  1. AAIController::OnUnPossess calls parent OnUnPossess, which clears Pawn
  2. CleanupBrainComponent() stops StateTreeAIComponent
  3. UStateTreeComponent::StopLogic tries to SetContextRequirements
    1. Pawn is missing, therefore SetContextRequirements fails, and StopTree is not called at all
  4. There is now a destroyed AIController that is still running a StateTree. The GameplayTask that was started before is never stopped, as no ExitState is ever called. When a delegate from this GameplayTask is called, it now executes on dead actors

I already had simillar issue in this ticket: [Gameplay Tasks running after NPC [Content removed] , which was also caused by Pawn being cleared too early, before all the other cleanups. James Keeling mentioned they are already considering moving the parent UnPossess call, this is another issue that it would fix.

I will probably be using the same solution as for the previous issue: subclassing AIController, overriding OnUnPossess(), and calling CleanupBrainComponent() before the parent call (this will result in CleanupBrainComponent() beeing called twice in the end, which shoudn’t be an issue).

[Attachment Removed]

Steps to Reproduce

  1. Create AIController with StateTreeAIComponent
    1. StartAllLogicOnPosses and StopAllLogicOnPosses = True
  2. StateTreeAIComponent is running a StateTree with “StateTree AI Component” schema
    1. Content of the tree is irrelevant, it just needs to run. I used infinite Delay task
  3. Create a new Pawn with just this new AIController set
  4. Several seconds after game starts, call UnPossess on the AIController

You will get following warning:

Context Requirements in %hs failed. Component tick is disabled (StateTreeComponent.cpp:260)

The tree will not be stopped, ExitStates won’t be called

[Attachment Removed]

I touched on this in another question along with a proposed fix if you are interested

[Content removed]

[Attachment Removed]

Jeremy’s proposed change would solve this issue. We looked at changing this internally, but the code is very old in how it is setup and could affect lots of projects that have relied on the current behavior of when Super::OnUnPossess is called in AI Controller. Since that is a large concern, we have decided not to change when it is called. There are other issues such as making sure the BrainComponent is not cleaned up prior to handling the GameplayTasks. This could be moved around in local projects easily by moving Super::OnUnPossess later in the function as there would hopefully not be any breaks or they could be tracked down quickly if upgrading existing projects.

We have been discussing using a system such as GameplayTasks which BT uses to make the possessed Pawn able to be used similar to the GameplayTask’s Avatar. While this has been discussed, we have not made any changes for this yet. It was our thought for what to investigate next.

-James

[Attachment Removed]