// Copyright Epic Games, Inc. All Rights Reserved. #include "StateTreePropertyBindings.h" #include "UObject/EnumProperty.h" #include "Misc/EnumerateRange.h" #include "PropertyPathHelpers.h" #include "StructUtils/PropertyBag.h" #include "StateTreePropertyRef.h" #if WITH_EDITOR #include "UObject/CoreRedirects.h" #include "UObject/Package.h" #include "Engine/BlueprintGeneratedClass.h" #include "StructUtils/UserDefinedStruct.h" #include "UObject/Field.h" #endif #include UE_INLINE_GENERATED_CPP_BY_NAME(StateTreePropertyBindings) namespace UE::StateTree { bool AcceptTaskInstanceData(EStateTreeBindableStructSource Target) { // Condition and Utility are constructed before the task instance data is constructed. return Target != EStateTreeBindableStructSource::StateParameter && Target != EStateTreeBindableStructSource::Condition && Target != EStateTreeBindableStructSource::Consideration; } FString GetDescAndPathAsString(const FStateTreeBindableStructDesc& Desc, const FPropertyBindingPath& Path) { return PropertyBinding::GetDescriptorAndPathAsString(Desc, Path); } #if WITH_EDITOR EStateTreePropertyUsage GetUsageFromMetaData(const FProperty* Property) { static const FName CategoryName(TEXT("Category")); if (Property == nullptr) { return EStateTreePropertyUsage::Invalid; } const FString Category = Property->GetMetaData(CategoryName); if (Category == TEXT("Input")) { return EStateTreePropertyUsage::Input; } if (Category == TEXT("Inputs")) { return EStateTreePropertyUsage::Input; } if (Category == TEXT("Output")) { return EStateTreePropertyUsage::Output; } if (Category == TEXT("Outputs")) { return EStateTreePropertyUsage::Output; } if (Category == TEXT("Context")) { return EStateTreePropertyUsage::Context; } return EStateTreePropertyUsage::Parameter; } const FProperty* GetStructSingleOutputProperty(const UStruct& InStruct) { const FProperty* FuncOutputProperty = nullptr; for (TFieldIterator PropIt(&InStruct, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt) { if (GetUsageFromMetaData(*PropIt) == EStateTreePropertyUsage::Output) { if (FuncOutputProperty) { return nullptr; } FuncOutputProperty = *PropIt; } } return FuncOutputProperty; } #endif } // UE::StateTree PRAGMA_DISABLE_DEPRECATION_WARNINGS #if WITH_EDITORONLY_DATA namespace UE::StateTree::Deprecation { FPropertyBindingPath ConvertEditorPath(const FStateTreeEditorPropertyPath& InEditorPath) { FPropertyBindingPath Path; Path.SetStructID(InEditorPath.StructID); for (const FString& Segment : InEditorPath.Path) { const TCHAR* PropertyNamePtr = nullptr; int32 PropertyNameLength = 0; int32 ArrayIndex = INDEX_NONE; PropertyPathHelpers::FindFieldNameAndArrayIndex(Segment.Len(), *Segment, PropertyNameLength, &PropertyNamePtr, ArrayIndex); FString PropertyNameString(PropertyNameLength, PropertyNamePtr); const FName PropertyName(*PropertyNameString, FNAME_Find); Path.AddPathSegment(PropertyName, ArrayIndex); } return Path; } } // UE::StateTree::Deprecation //----------------------------------------------------------------// // FStateTreePropertyPathBinding //----------------------------------------------------------------// void FStateTreePropertyPathBinding::PostSerialize(const FArchive& Ar) { if (SourcePath_DEPRECATED.IsValid()) { SourcePropertyPath = UE::StateTree::Deprecation::ConvertEditorPath(SourcePath_DEPRECATED); SourcePath_DEPRECATED.StructID = FGuid(); SourcePath_DEPRECATED.Path.Reset(); } if (TargetPath_DEPRECATED.IsValid()) { TargetPropertyPath = UE::StateTree::Deprecation::ConvertEditorPath(TargetPath_DEPRECATED); TargetPath_DEPRECATED.StructID = FGuid(); TargetPath_DEPRECATED.Path.Reset(); } } #endif // WITH_EDITORONLY_DATA PRAGMA_ENABLE_DEPRECATION_WARNINGS //----------------------------------------------------------------// // FStateTreeBindableStructDesc //----------------------------------------------------------------// FString FStateTreeBindableStructDesc::ToString() const { FStringBuilderBase Result; Result += *UEnum::GetDisplayValueAsText(DataSource).ToString(); Result += TEXT(" '"); #if WITH_EDITORONLY_DATA Result += StatePath; Result += TEXT("/"); #endif Result += Name.ToString(); Result += TEXT("'"); return Result.ToString(); } //----------------------------------------------------------------// // FStateTreePropertyBindings //----------------------------------------------------------------// FStateTreePropertyBindings::FStateTreePropertyBindings() { // StateTree supports Property references PropertyReferenceStructType = FStateTreeStructRef::StaticStruct(); // Set copy function PropertyReferenceCopyFunc = [](const FStructProperty& SourceStructProperty, uint8* SourceAddress, uint8* TargetAddress) { FStateTreeStructRef* Target = reinterpret_cast(TargetAddress); Target->Set(FStructView(SourceStructProperty.Struct, SourceAddress)); }; // Set reset function PropertyReferenceResetFunc = [](uint8* TargetAddress) { reinterpret_cast(TargetAddress)->Set(FStructView()); }; } void FStateTreePropertyBindings::OnReset() { SourceStructs.Reset(); PropertyPathBindings.Reset(); PropertyAccesses.Reset(); PropertyReferencePaths.Reset(); } int32 FStateTreePropertyBindings::GetNumBindableStructDescriptors() const { return SourceStructs.Num(); } const FPropertyBindingBindableStructDescriptor* FStateTreePropertyBindings::GetBindableStructDescriptorFromHandle(const FConstStructView InSourceHandleView) const { check(InSourceHandleView.GetScriptStruct() == FStateTreeDataHandle::StaticStruct()); return GetBindableStructDescriptorFromHandle(InSourceHandleView.Get()); } const FPropertyBindingBindableStructDescriptor* FStateTreePropertyBindings::GetBindableStructDescriptorFromHandle(FStateTreeDataHandle InSourceHandle) const { return SourceStructs.FindByPredicate([SourceDataHandle = InSourceHandle](const FStateTreeBindableStructDesc& Desc) { return Desc.DataHandle == SourceDataHandle; }); } void FStateTreePropertyBindings::VisitSourceStructDescriptorInternal( TFunctionRef InFunction) const { for (const FStateTreeBindableStructDesc& SourceStruct : SourceStructs) { if (InFunction(SourceStruct) == EVisitResult::Break) { break; } } } bool FStateTreePropertyBindings::OnResolvingPaths() { // Base class handled common bindings, here we only need to handle Property references bool bResult = true; PropertyAccesses.Reset(); PropertyAccesses.Reserve(PropertyReferencePaths.Num()); for (const FStateTreePropertyRefPath& ReferencePath : PropertyReferencePaths) { FStateTreePropertyAccess& PropertyAccess = PropertyAccesses.AddDefaulted_GetRef(); PropertyAccess.SourceDataHandle = ReferencePath.GetSourceDataHandle(); const FPropertyBindingBindableStructDescriptor* SourceDesc = GetBindableStructDescriptorFromHandle(PropertyAccess.SourceDataHandle); PropertyAccess.SourceStructType = SourceDesc->Struct; FPropertyBindingPathIndirection SourceLeafIndirection; if (!Super::ResolvePath(SourceDesc->Struct, ReferencePath.GetSourcePath(), PropertyAccess.SourceIndirection, SourceLeafIndirection)) { bResult = false; } PropertyAccess.SourceLeafProperty = SourceLeafIndirection.GetProperty(); } return bResult; } int32 FStateTreePropertyBindings::GetNumBindings() const { return PropertyPathBindings.Num(); } void FStateTreePropertyBindings::ForEachBinding(TFunctionRef InFunction) const { for (const FStateTreePropertyPathBinding& Binding : PropertyPathBindings) { InFunction(Binding); } } void FStateTreePropertyBindings::ForEachBinding(const FPropertyBindingIndex16 InBegin, const FPropertyBindingIndex16 InEnd , const TFunctionRef InFunction) const { ensureMsgf(InBegin.IsValid() && InEnd.IsValid(), TEXT("%hs expects valid indices."), __FUNCTION__); for (int32 BindingIndex = InBegin.Get(); BindingIndex < InEnd.Get(); ++BindingIndex) { InFunction(PropertyPathBindings[BindingIndex], BindingIndex); } } void FStateTreePropertyBindings::ForEachMutableBinding(TFunctionRef InFunction) { for (FStateTreePropertyPathBinding& Binding : PropertyPathBindings) { InFunction(Binding); } } void FStateTreePropertyBindings::VisitBindings(TFunctionRef InFunction) const { for (const FStateTreePropertyPathBinding& Binding : PropertyPathBindings) { if (InFunction(Binding) == EVisitResult::Break) { break; } } } void FStateTreePropertyBindings::VisitMutableBindings(TFunctionRef InFunction) { for (FStateTreePropertyPathBinding& Binding : PropertyPathBindings) { if (InFunction(Binding) == EVisitResult::Break) { break; } } } #if WITH_EDITOR FPropertyBindingBinding* FStateTreePropertyBindings::AddBindingInternal(const FPropertyBindingPath& InSourcePath , const FPropertyBindingPath& InTargetPath) { checkf(false, TEXT("Not expected to get called for StateTree runtime bindings." " Editor operations for bindings are handled by FStateTreeEditorPropertyBindings")); return nullptr; } void FStateTreePropertyBindings::RemoveBindingsInternal(TFunctionRef InPredicate) { checkf(false, TEXT("Not expected to get called for StateTree runtime bindings." " Editor operations for bindings are handled by FStateTreeEditorPropertyBindings")); } bool FStateTreePropertyBindings::HasBindingInternal(TFunctionRef InPredicate) const { checkf(false, TEXT("Not expected to get called for StateTree runtime bindings." " Editor operations for bindings are handled by FStateTreeEditorPropertyBindings")); return false; } const FPropertyBindingBinding* FStateTreePropertyBindings::FindBindingInternal(TFunctionRef InPredicate) const { checkf(false, TEXT("Not expected to get called for StateTree runtime bindings." " Editor operations for bindings are handled by FStateTreeEditorPropertyBindings")); return nullptr; } #endif // WITH_EDITOR bool FStateTreePropertyBindings::ResetObjects(const FStateTreeIndex16 TargetBatchIndex, FStateTreeDataView TargetStructView) const { return Super::ResetObjects(TargetBatchIndex, TargetStructView); } const FStateTreePropertyAccess* FStateTreePropertyBindings::GetPropertyAccess(const FStateTreePropertyRef& InPropertyReference) const { if (!InPropertyReference.GetRefAccessIndex().IsValid()) { return nullptr; } if (!ensure(PropertyAccesses.IsValidIndex(InPropertyReference.GetRefAccessIndex().Get()))) { return nullptr; } return &PropertyAccesses[InPropertyReference.GetRefAccessIndex().Get()]; } void FStateTreePropertyBindings::DebugPrintInternalLayout(FString& OutString) const { #if WITH_EDITOR || WITH_PROPERTYBINDINGUTILS_DEBUG OutString += DebugAsString(); #endif } PRAGMA_DISABLE_DEPRECATION_WARNINGS int32 FStateTreePropertyBindings::GetSourceStructNum() const { return SourceStructs.Num(); } const FStateTreePropertyCopyBatch& FStateTreePropertyBindings::GetBatch(const FStateTreeIndex16 TargetBatchIndex) const { check(TargetBatchIndex.IsValid()); static FStateTreePropertyCopyBatch Batch; return Batch; } TConstArrayView FStateTreePropertyBindings::GetBatchCopies(const FStateTreeIndex16 TargetBatchIndex) const { return GetBatchCopies(GetBatch(TargetBatchIndex)); } TConstArrayView FStateTreePropertyBindings::GetBatchCopies(const FStateTreePropertyCopyBatch& Batch) const { return {}; } bool FStateTreePropertyBindings::ResolveCopyType(const FStateTreePropertyPathIndirection& SourceIndirection, const FStateTreePropertyPathIndirection& TargetIndirection, FStateTreePropertyCopy& OutCopy) { return false; } bool FStateTreePropertyBindings::ResolveCopyType(const FPropertyBindingPathIndirection& SourceIndirection, const FPropertyBindingPathIndirection& TargetIndirection, FStateTreePropertyCopy& OutCopy) { return false; } EStateTreePropertyAccessCompatibility FStateTreePropertyBindings::GetPropertyCompatibility(const FProperty* FromProperty, const FProperty* ToProperty) { // Temporary using cast as this method is no longer used and will be removed so we don't care about matching values in both enums //const UE::PropertyBinding::EPropertyCompatibility Value = UE::PropertyBinding::GetPropertyCompatibility(FromProperty, ToProperty); //check(UEnum::GetValueAsName((EStateTreePropertyAccessCompatibility)Value) == // UEnum::GetValueAsName(Value)); return static_cast(UE::PropertyBinding::GetPropertyCompatibility(FromProperty, ToProperty)); } bool FStateTreePropertyBindings::ResolvePath(const UStruct* Struct, const FPropertyBindingPath& Path , FStateTreePropertyIndirection& OutFirstIndirection, FPropertyBindingPathIndirection& OutLeafIndirection) { return false; } const FStateTreeBindableStructDesc* FStateTreePropertyBindings::GetSourceDescByHandle(const FStateTreeDataHandle SourceDataHandle) { return nullptr; } void FStateTreePropertyBindings::PerformCopy(const FStateTreePropertyCopy& Copy, uint8* SourceAddress, uint8* TargetAddress) const { } void FStateTreePropertyBindings::PerformResetObjects(const FStateTreePropertyCopy& Copy, uint8* TargetAddress) const { } uint8* FStateTreePropertyBindings::GetAddress(FStateTreeDataView InStructView, const FStateTreePropertyIndirection& FirstIndirection , const FProperty* LeafProperty) const { return nullptr; } PRAGMA_ENABLE_DEPRECATION_WARNINGS