// Copyright Epic Games, Inc. All Rights Reserved. #include "StateTreeInstanceData.h" #include "StateTreeExecutionTypes.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" #include "VisualLogger/VisualLogger.h" #include "StateTree.h" #include "Serialization/ObjectAndNameAsStringProxyArchive.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(StateTreeInstanceData) namespace UE::StateTree { /** * Duplicates object, and tries to covert old BP classes (REINST_*) to their newer version. */ UObject* DuplicateNodeInstance(const UObject& Instance, UObject& InOwner) { const UClass* InstanceClass = Instance.GetClass(); if (InstanceClass->HasAnyClassFlags(CLASS_NewerVersionExists)) { const UClass* AuthoritativeClass = InstanceClass->GetAuthoritativeClass(); UObject* NewInstance = NewObject(&InOwner, AuthoritativeClass); // Try to copy the values over using serialization // FObjectAndNameAsStringProxyArchive is used to store and restore names and objects as memory writer does not support UObject references at all. TArray Data; FMemoryWriter Writer(Data); FObjectAndNameAsStringProxyArchive WriterProxy(Writer, /*bInLoadIfFindFails*/true); UObject& NonConstInstance = const_cast(Instance); NonConstInstance.Serialize(WriterProxy); FMemoryReader Reader(Data); FObjectAndNameAsStringProxyArchive ReaderProxy(Reader, /*bInLoadIfFindFails*/true); NewInstance->Serialize(ReaderProxy); const UStateTree* OuterStateTree = Instance.GetTypedOuter(); UE_LOG(LogStateTree, Display, TEXT("FStateTreeInstanceData: Duplicating '%s' with old class '%s' as '%s', potential data loss. Please resave State Tree asset %s."), *GetFullNameSafe(&Instance), *GetNameSafe(InstanceClass), *GetNameSafe(AuthoritativeClass), *GetFullNameSafe(OuterStateTree)); return NewInstance; } return DuplicateObject(&Instance, &InOwner); } } // UE::StateTree //----------------------------------------------------------------// // FStateTreeInstanceStorage //----------------------------------------------------------------// void FStateTreeInstanceStorage::AddTransitionRequest(const UObject* Owner, const FStateTreeTransitionRequest& Request) { constexpr int32 MaxPendingTransitionRequests = 32; if (TransitionRequests.Num() >= MaxPendingTransitionRequests) { UE_VLOG_UELOG(Owner, LogStateTree, Error, TEXT("%s: Too many transition requests sent to '%s' (%d pending). Dropping request."), ANSI_TO_TCHAR(__FUNCTION__), *GetNameSafe(Owner), TransitionRequests.Num()); return; } TransitionRequests.Add(Request); } void FStateTreeInstanceStorage::ResetTransitionRequests() { TransitionRequests.Reset(); } bool FStateTreeInstanceStorage::AreAllInstancesValid() const { for (FConstStructView Instance : InstanceStructs) { if (!Instance.IsValid()) { return false; } } for (const UObject* Instance : InstanceObjects) { if (!Instance) { return false; } } return true; } //----------------------------------------------------------------// // FStateTreeInstanceData //----------------------------------------------------------------// FStateTreeInstanceData::FStateTreeInstanceData() { InstanceStorage.InitializeAs(); } FStateTreeInstanceData::~FStateTreeInstanceData() { Reset(); } bool FStateTreeInstanceStorage::IsValid() const { return InstanceStructs.Num() > 0 || InstanceObjects.Num() > 0; } const FStateTreeInstanceStorage& FStateTreeInstanceData::GetStorage() const { check(InstanceStorage.GetMemory() != nullptr && InstanceStorage.GetScriptStruct() == TBaseStructure::Get()); return *reinterpret_cast(InstanceStorage.GetMemory()); } FStateTreeInstanceStorage& FStateTreeInstanceData::GetMutableStorage() { check(InstanceStorage.GetMemory() != nullptr && InstanceStorage.GetScriptStruct() == TBaseStructure::Get()); return *reinterpret_cast(InstanceStorage.GetMutableMemory()); } const FStateTreeExecutionState* FStateTreeInstanceData::GetExecutionState() const { if (!IsValid()) { return nullptr; } const FConstStructView ExecView = GetStruct(0); // Execution state is fixed at index 0. return ExecView.GetPtr(); } TArray& FStateTreeInstanceData::GetEvents() const { return const_cast(this)->GetMutableStorage().EventQueue.GetEventsArray(); } FStateTreeEventQueue& FStateTreeInstanceData::GetMutableEventQueue() { return GetMutableStorage().EventQueue; } const FStateTreeEventQueue& FStateTreeInstanceData::GetEventQueue() const { return GetStorage().EventQueue; } void FStateTreeInstanceData::AddTransitionRequest(const UObject* Owner, const FStateTreeTransitionRequest& Request) { GetMutableStorage().AddTransitionRequest(Owner, Request); } TConstArrayView FStateTreeInstanceData::GetTransitionRequests() const { return GetStorage().GetTransitionRequests(); } void FStateTreeInstanceData::ResetTransitionRequests() { GetMutableStorage().ResetTransitionRequests(); } bool FStateTreeInstanceData::AreAllInstancesValid() const { return GetStorage().AreAllInstancesValid(); } int32 FStateTreeInstanceData::GetEstimatedMemoryUsage() const { const FStateTreeInstanceStorage& Storage = GetStorage(); int32 Size = sizeof(FStateTreeInstanceData); Size += Storage.InstanceStructs.GetAllocatedMemory(); for (const UObject* InstanceObject : Storage.InstanceObjects) { if (InstanceObject) { Size += InstanceObject->GetClass()->GetStructureSize(); } } return Size; } int32 FStateTreeInstanceData::GetNumItems() const { const FStateTreeInstanceStorage& Storage = GetStorage(); return Storage.InstanceStructs.Num() + Storage.InstanceObjects.Num(); } bool FStateTreeInstanceData::Identical(const FStateTreeInstanceData* Other, uint32 PortFlags) const { if (Other == nullptr) { return false; } // Identical if both are uninitialized. if (!IsValid() && !Other->IsValid()) { return true; } // Not identical if one is valid and other is not. if (IsValid() != Other->IsValid()) { return false; } const FStateTreeInstanceStorage& Storage = GetStorage(); const FStateTreeInstanceStorage& OtherStorage = Other->GetStorage(); // Not identical if different amount of instanced objects. if (Storage.InstanceObjects.Num() != OtherStorage.InstanceObjects.Num()) { return false; } // Not identical if structs are different. if (Storage.InstanceStructs.Identical(&OtherStorage.InstanceStructs, PortFlags) == false) { return false; } // Check that the instance object contents are identical. // Copied from object property. auto AreObjectsIdentical = [](UObject* A, UObject* B, uint32 PortFlags) -> bool { if ((PortFlags & PPF_DuplicateForPIE) != 0) { return false; } if (A == B) { return true; } // Resolve the object handles and run the deep comparison logic if ((PortFlags & (PPF_DeepCompareInstances | PPF_DeepComparison)) != 0) { return FObjectPropertyBase::StaticIdentical(A, B, PortFlags); } return true; }; bool bResult = true; for (int32 Index = 0; Index < Storage.InstanceObjects.Num(); Index++) { if (Storage.InstanceObjects[Index] != nullptr && OtherStorage.InstanceObjects[Index] != nullptr) { if (!AreObjectsIdentical(Storage.InstanceObjects[Index], OtherStorage.InstanceObjects[Index], PortFlags)) { bResult = false; break; } } else { bResult = false; break; } } return bResult; } void FStateTreeInstanceData::PostSerialize(const FArchive& Ar) { #if WITH_EDITORONLY_DATA if (Ar.IsLoading()) { FStateTreeInstanceStorage& Storage = GetMutableStorage(); PRAGMA_DISABLE_DEPRECATION_WARNINGS if (InstanceStructs_DEPRECATED.Num() > 0 || InstanceObjects_DEPRECATED.Num() > 0) { if (!Storage.IsValid()) { Storage.InstanceStructs.Reset(); Storage.InstanceStructs.Append(InstanceStructs_DEPRECATED); Storage.InstanceObjects = InstanceObjects_DEPRECATED; } InstanceStructs_DEPRECATED.Reset(); InstanceObjects_DEPRECATED.Reset(); } PRAGMA_ENABLE_DEPRECATION_WARNINGS } #endif } void FStateTreeInstanceData::CopyFrom(UObject& InOwner, const FStateTreeInstanceData& InOther) { if (&InOther == this) { return; } FStateTreeInstanceStorage& Storage = GetMutableStorage(); const FStateTreeInstanceStorage& OtherStorage = InOther.GetStorage(); // Copy structs Storage.InstanceStructs = OtherStorage.InstanceStructs; // Copy instance objects. Storage.InstanceObjects.Reset(); for (const UObject* Instance : OtherStorage.InstanceObjects) { if (ensure(Instance != nullptr)) { Storage.InstanceObjects.Add(UE::StateTree::DuplicateNodeInstance(*Instance, InOwner)); } } } void FStateTreeInstanceData::Init(UObject& InOwner, TConstArrayView InStructs, TConstArrayView InObjects) { Reset(); Append(InOwner, InStructs, InObjects); } void FStateTreeInstanceData::Init(UObject& InOwner, TConstArrayView InStructs, TConstArrayView InObjects) { Reset(); Append(InOwner, InStructs, InObjects); } void FStateTreeInstanceData::Append(UObject& InOwner, TConstArrayView InStructs, TConstArrayView InObjects) { FStateTreeInstanceStorage& Storage = GetMutableStorage(); Storage.InstanceStructs.Append(InStructs); Storage.InstanceObjects.Reserve(Storage.InstanceObjects.Num() + InObjects.Num()); for (const UObject* Instance : InObjects) { if (ensure(Instance != nullptr)) { Storage.InstanceObjects.Add(UE::StateTree::DuplicateNodeInstance(*Instance, InOwner)); } } } void FStateTreeInstanceData::Append(UObject& InOwner, TConstArrayView InStructs, TConstArrayView InObjects) { FStateTreeInstanceStorage& Storage = GetMutableStorage(); Storage.InstanceStructs.Append(InStructs); Storage.InstanceObjects.Reserve(Storage.InstanceObjects.Num() + InObjects.Num()); for (const UObject* Instance : InObjects) { if (ensure(Instance != nullptr)) { Storage.InstanceObjects.Add(UE::StateTree::DuplicateNodeInstance(*Instance, InOwner)); } } } void FStateTreeInstanceData::ShrinkTo(const int32 NumStructs, const int32 NumObjects) { FStateTreeInstanceStorage& Storage = GetMutableStorage(); check(NumStructs <= Storage.InstanceStructs.Num() && NumObjects <= Storage.InstanceObjects.Num()); Storage.InstanceStructs.SetNum(NumStructs); Storage.InstanceObjects.SetNum(NumObjects); } bool FStateTreeInstanceData::IsValid() const { if (!InstanceStorage.IsValid()) { return false; } return GetStorage().IsValid(); } void FStateTreeInstanceData::Reset() { FStateTreeInstanceStorage& Storage = GetMutableStorage(); Storage.InstanceStructs.Reset(); Storage.InstanceObjects.Reset(); Storage.EventQueue.Reset(); }