Files
UnrealEngineUWP/Engine/Source/Runtime/AIModule/Private/BehaviorTree/BlackboardData.cpp
marc audy 6553e6cd0a Remove as much C++ deprecation as possible up to 4.17 (along with a few scattered removals from beyond)
#preflight 61eefc77ba69a4fdb220bf23

#ROBOMERGE-AUTHOR: marc.audy
#ROBOMERGE-SOURCE: CL 18712765 in //UE5/Release-5.0/... via CL 18712784 via CL 18713147
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v903-18687472)

[CL 18713191 by marc audy in ue5-main branch]
2022-01-24 15:07:48 -05:00

310 lines
7.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BehaviorTree/BlackboardData.h"
#include "GameFramework/Actor.h"
#include "BehaviorTree/BTNode.h"
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "AISystem.h"
UBlackboardData::FKeyUpdate UBlackboardData::OnUpdateKeys;
#if WITH_EDITOR
#include "Async/Async.h"
#include "Editor/EditorEngine.h"
extern UNREALED_API UEditorEngine* GEditor;
UBlackboardData::FBlackboardDataChanged UBlackboardData::OnBlackboardDataChanged;
#endif
static void UpdatePersistentKeys(UBlackboardData& Asset)
{
if (GET_AI_CONFIG_VAR(bAddBlackboardSelfKey))
{
// note that UpdatePersistentKey will return non-null only if a given key gets newly created
UBlackboardKeyType_Object* SelfKeyType = Asset.UpdatePersistentKey<UBlackboardKeyType_Object>(FBlackboard::KeySelf);
if (SelfKeyType)
{
SelfKeyType->BaseClass = AActor::StaticClass();
#if WITH_EDITOR
// MarkPackageDirty returning false means marking wasn't possible at this moment. Give it one more try in a moment
if (GEditor != nullptr && Asset.MarkPackageDirty() == false)
{
TWeakObjectPtr<UBlackboardData> WeakAsset = &Asset;
AsyncTask(ENamedThreads::GameThread, [WeakAsset](){
if (UBlackboardData* AssetPtr = WeakAsset.Get())
{
AssetPtr->MarkPackageDirty();
}
});
}
#endif // WITH_EDITOR
}
}
}
bool FBlackboardEntry::operator==(const FBlackboardEntry& Other) const
{
return (EntryName == Other.EntryName) &&
((KeyType && Other.KeyType && KeyType->GetClass() == Other.KeyType->GetClass()) || (KeyType == NULL && Other.KeyType == NULL));
}
UBlackboardData::UBlackboardData(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
FBlackboard::FKey UBlackboardData::GetKeyID(const FName& KeyName) const
{
return InternalGetKeyID(KeyName, CheckParentKeys);
}
FName UBlackboardData::GetKeyName(FBlackboard::FKey KeyID) const
{
const FBlackboardEntry* KeyEntry = GetKey(KeyID);
return KeyEntry ? KeyEntry->EntryName : NAME_None;
}
TSubclassOf<UBlackboardKeyType> UBlackboardData::GetKeyType(FBlackboard::FKey KeyID) const
{
const FBlackboardEntry* KeyEntry = GetKey(KeyID);
return KeyEntry && KeyEntry->KeyType ? KeyEntry->KeyType->GetClass() : NULL;
}
bool UBlackboardData::IsKeyInstanceSynced(FBlackboard::FKey KeyID) const
{
const FBlackboardEntry* KeyEntry = GetKey(KeyID);
return KeyEntry ? KeyEntry->bInstanceSynced : false;
}
const FBlackboardEntry* UBlackboardData::GetKey(FBlackboard::FKey KeyID) const
{
if (KeyID != FBlackboard::InvalidKey)
{
if (KeyID >= FirstKeyID)
{
return &Keys[KeyID - FirstKeyID];
}
else if (Parent)
{
return Parent->GetKey(KeyID);
}
}
return NULL;
}
int32 UBlackboardData::GetNumKeys() const
{
return FirstKeyID + Keys.Num();
}
FBlackboard::FKey UBlackboardData::InternalGetKeyID(const FName& KeyName, EKeyLookupMode LookupMode) const
{
for (int32 KeyIndex = 0; KeyIndex < Keys.Num(); KeyIndex++)
{
if (Keys[KeyIndex].EntryName == KeyName)
{
return KeyIndex + FirstKeyID;
}
}
return Parent && (LookupMode == CheckParentKeys) ? Parent->InternalGetKeyID(KeyName, LookupMode) : FBlackboard::InvalidKey;
}
bool UBlackboardData::IsValid() const
{
if (Parent)
{
for (int32 KeyIndex = 0; KeyIndex < Keys.Num(); KeyIndex++)
{
const FBlackboard::FKey KeyID = Parent->InternalGetKeyID(Keys[KeyIndex].EntryName, CheckParentKeys);
if (KeyID != FBlackboard::InvalidKey)
{
UE_LOG(LogBehaviorTree, Warning, TEXT("Blackboard asset (%s) has duplicated key (%s) in parent chain!"),
*GetName(), *Keys[KeyIndex].EntryName.ToString());
return false;
}
}
}
return true;
}
void UBlackboardData::UpdateIfHasSynchronizedKeys()
{
bHasSynchronizedKeys = Parent != nullptr && Parent->bHasSynchronizedKeys;
for (int32 KeyIndex = 0; KeyIndex < Keys.Num() && bHasSynchronizedKeys == false; ++KeyIndex)
{
bHasSynchronizedKeys |= Keys[KeyIndex].bInstanceSynced;
}
}
void UBlackboardData::PostInitProperties()
{
Super::PostInitProperties();
if (HasAnyFlags(RF_NeedPostLoad | RF_ClassDefaultObject) == false)
{
UpdatePersistentKeys(*this);
}
}
void UBlackboardData::PostLoad()
{
Super::PostLoad();
// we cache some information based on Parent asset
// but while UnrealEngine guarantees the Parent is already loaded
// it does not guarantee that it's PostLoad has been called
// Following is a little hack that's widely used in the
// engine to address this
if (Parent)
{
Parent->ConditionalPostLoad();
}
UpdateParentKeys();
UpdateIfHasSynchronizedKeys();
}
#if WITH_EDITOR
void UBlackboardData::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName NAME_Parent = GET_MEMBER_NAME_CHECKED(UBlackboardData, Parent);
static const FName NAME_InstanceSynced = GET_MEMBER_NAME_CHECKED(FBlackboardEntry, bInstanceSynced);
static const FName NAME_Keys = GET_MEMBER_NAME_CHECKED(UBlackboardData, Keys);
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
if (PropertyChangedEvent.Property->GetFName() == NAME_Parent)
{
// look for cycles
if (Parent && Parent->IsChildOf(*this))
{
UE_LOG(LogBehaviorTree, Warning, TEXT("Blackboard asset (%s) has (%s) in parent chain! Clearing value to avoid cycle."),
*GetNameSafe(Parent), *GetNameSafe(this));
Parent = NULL;
}
UpdateParentKeys();
}
if (PropertyChangedEvent.Property->GetFName() == NAME_InstanceSynced || PropertyChangedEvent.Property->GetFName() == NAME_Parent)
{
UpdateIfHasSynchronizedKeys();
}
}
if (PropertyChangedEvent.MemberProperty)
{
if (PropertyChangedEvent.MemberProperty->GetFName() == NAME_Keys)
{
// look for BB assets using this one as a parent and update them as well
PropagateKeyChangesToDerivedBlackboardAssets();
}
}
UBlackboardData::OnBlackboardDataChanged.Broadcast(this);
}
#endif // WITH_EDITOR
void UBlackboardData::PropagateKeyChangesToDerivedBlackboardAssets()
{
for (TObjectIterator<UBlackboardData> It; It; ++It)
{
if (It->Parent == this)
{
It->UpdateParentKeys();
It->UpdateIfHasSynchronizedKeys();
It->PropagateKeyChangesToDerivedBlackboardAssets();
}
}
}
static bool ContainsKeyName(FName KeyName, const TArray<FBlackboardEntry>& Keys, const TArray<FBlackboardEntry>& ParentKeys)
{
for (int32 KeyIndex = 0; KeyIndex < Keys.Num(); KeyIndex++)
{
if (Keys[KeyIndex].EntryName == KeyName)
{
return true;
}
}
for (int32 KeyIndex = 0; KeyIndex < ParentKeys.Num(); KeyIndex++)
{
if (ParentKeys[KeyIndex].EntryName == KeyName)
{
return true;
}
}
return false;
}
void UBlackboardData::UpdateParentKeys()
{
if (Parent == this)
{
Parent = NULL;
}
#if WITH_EDITORONLY_DATA
ParentKeys.Reset();
for (UBlackboardData* It = Parent; It; It = It->Parent)
{
for (int32 KeyIndex = 0; KeyIndex < It->Keys.Num(); KeyIndex++)
{
const bool bAlreadyExist = ContainsKeyName(It->Keys[KeyIndex].EntryName, Keys, ParentKeys);
if (!bAlreadyExist)
{
ParentKeys.Add(It->Keys[KeyIndex]);
}
}
}
#endif // WITH_EDITORONLY_DATA
UpdateKeyIDs();
UpdatePersistentKeys(*this);
OnUpdateKeys.Broadcast(this);
}
void UBlackboardData::UpdateKeyIDs()
{
FirstKeyID = Parent ? Parent->GetNumKeys() : 0;
}
void UBlackboardData::UpdateDeprecatedKeys()
{
for (int32 KeyIndex = 0; KeyIndex < Keys.Num(); KeyIndex++)
{
FBlackboardEntry& Entry = Keys[KeyIndex];
if (Entry.KeyType)
{
UBlackboardKeyType* UpdatedKey = Entry.KeyType->UpdateDeprecatedKey();
if (UpdatedKey)
{
Entry.KeyType = UpdatedKey;
}
}
}
}
bool UBlackboardData::IsChildOf(const UBlackboardData& OtherAsset) const
{
const UBlackboardData* TmpParent = Parent;
// rewind
while (TmpParent != nullptr && TmpParent != &OtherAsset)
{
TmpParent = TmpParent->Parent;
}
return (TmpParent == &OtherAsset);
}