ProjectBorealis / IAUS

Infinite axis utility system in UE Behavior Trees
MIT License
59 stars 12 forks source link

Crashes - Nested behavior trees #7

Open Fantastick-svg opened 5 months ago

Fantastick-svg commented 5 months ago

The memory management of utility nodes does not take into account the nested behavioral trees of "RunBehaviorDynamic" tasks within the UBehaviorTreeComponent. It only tries to access the RootTree memory, causing various crashes.

mastercoms commented 5 months ago

Hi, thanks for the report! Could you let us know more about how to set this up/reproduce that with an example tree?

Fantastick-svg commented 5 months ago

Hi, sure no problem. Just create new BehaviorTree asset which has selector under the root node. That selector can for example run EQS to perceive player using perception system. In my case I have two sub nodes of the selector. Each if them is sequence which contains a decorator. One should be run if TargetActor is set and second should run if TargetActor is not set.

image

In AIController OnPossess I run my default BehaviorTree which can be seen on the screenshot above. We also need to set the subtree which will find the RunBehaviorDynamic node within the default BehaviorTree using the GameplayTag.

if(RunBehaviorTree(DefaultBT))
    {
        const TSoftObjectPtr<UBehaviorTree> BehaviorSoftPtr = AIConfig->ActiveBehaviors.FindRef(MyGameplayTags::AI_Role_Active);

        if(IsValid(BehaviorSoftPtr.LoadSynchronous()))
        {
            if(UBehaviorTreeComponent* BTC = Cast<UBehaviorTreeComponent>(GetBrainComponent()))
            {
                BTC->SetDynamicSubtree(MyGameplayTags::AI_Role_Active, BehaviorSoftPtr.Get());
            }
        }
    }

Now, the Active Behavior looks like for example this..

image

Once the UtilityDecorator is reach, we got Crash at

void UIAUSBTDecorator_Utility::InitializeMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
                                                EBTMemoryInit::Type InitType) const

Directly at the line

Memory->MemoryUtilityComposite->BehaviorMemories.Empty();

with output warning: `this' is not accessible (substituting 0)

I don't know if it's because the data is on another thread... are behavior trees multithreaded? Unfortunately I don't have much time to dig deeper. In my opinion, the decorator is trying to access the memory of the node at incorrect instance index "OwnerComp.GetActiveInstanceIdx()", which returns the memory of the node to the RunBehaviorDynamic task in the RootTree? Which might not be accessible that way? I hope this helps at least a little bit.

EDIT: I also noticed a crash even if the UtilitySelector with the UtilityDecorator is located below the classic selector and the "TargetSelector" as shown on the screen receives an abort and move to another lower priority node. At the moment when it is currently scoring considerations from some Behaviors, it sometimes happens that a crash occurs.

FIAUSBehaviorContext IAUSEvaluator::ChooseBehavior(AAIController* AIController, const TArray<AActor*> Targets, bool bCheckTeamAttitude /*= true*/)

    for (const auto& Consideration : Behaviors[Idx].Considerations)
            {
                const float Score = Consideration->Score(Context); // Crash happens right here in the middle of the loop
                if (Score <= 0)
                {
                    Context.TotalScore = 0;
                    break;
                }

                const float Modification = (1.0 - Score) * Behaviors[Idx].CompensationFactor;
                Context.TotalScore *= Score + (Modification * Score);
            }
mastercoms commented 4 months ago

Thanks for the report, we're looking into this.