diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index aba46958..fa067851 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -9,6 +9,8 @@ #include "Asset/FlowAssetParams.h" #include "Asset/FlowAssetParamsUtils.h" #include "Interfaces/FlowExecutionGate.h" +#include "Interfaces/FlowGraphOutputDataReceiverInterface.h" +#include "Types/FlowNamedDataPinProperty.h" #include "Nodes/FlowNodeBase.h" #include "Nodes/Graph/FlowNode_CustomInput.h" #include "Nodes/Graph/FlowNode_CustomOutput.h" @@ -16,6 +18,7 @@ #include "Nodes/Graph/FlowNode_SubGraph.h" #include "Policies/FlowPinConnectionPolicy.h" #include "Policies/FlowPreloadPolicy.h" +#include "Types/FlowAutoDataPinsWorkingData.h" #include "Types/FlowDataPinValue.h" #include "Types/FlowStructUtils.h" @@ -25,6 +28,7 @@ #include "Algo/AnyOf.h" #if WITH_EDITOR +#include "Nodes/Graph/FlowNode_SetGraphOutput.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetToolsModule.h" #include "ContentBrowserModule.h" @@ -77,11 +81,26 @@ void UFlowAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEv { Super::PostEditChangeProperty(PropertyChangedEvent); - if (PropertyChangedEvent.Property && (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomInputs) - || PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomOutputs))) + const FName ChangedPropertyName = PropertyChangedEvent.GetPropertyName(); + const FName ChangedMemberPropertyName = PropertyChangedEvent.GetMemberPropertyName(); + if (PropertyChangedEvent.Property && (ChangedPropertyName == GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomInputs) + || ChangedPropertyName == GET_MEMBER_NAME_CHECKED(UFlowAsset, CustomOutputs) + || ChangedMemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowAsset, OutputDataPinDeclarations))) { OnSubGraphReconstructionRequested.ExecuteIfBound(); } + + if (PropertyChangedEvent.Property && ChangedMemberPropertyName == GET_MEMBER_NAME_CHECKED(UFlowAsset, OutputDataPinDeclarations)) + { + for (const TPair& NodePair : GetNodes()) + { + UFlowNode_SetGraphOutput* SetOutputNode = Cast(NodePair.Value); + if (IsValid(SetOutputNode) && SetOutputNode->TryUpdateAutoDataPins()) + { + SetOutputNode->OnReconstructionRequested.ExecuteIfBound(); + } + } + } } void UFlowAsset::PostDuplicate(bool bDuplicateForPIE) @@ -1043,8 +1062,10 @@ void UFlowAsset::PreStartFlow() #endif } -void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier) +void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier, IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver) { + InitializeOutputDataReceiverAndValues(InOutputDataReceiver); + PreStartFlow(); if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode()) @@ -1074,6 +1095,11 @@ void UFlowAsset::FinishNode(UFlowNode* Node) // if graph reached Finish and this asset instance was created by SubGraph node if (Node->CanFinishGraph()) { + if (IFlowGraphOutputDataReceiverInterface* Receiver = Cast(OutputDataReceiver.Get())) + { + Receiver->ReceiveOutputDataSnapshot(OutputDataPinValues); + } + if (NodeOwningThisAssetInstance.IsValid()) { NodeOwningThisAssetInstance.Get()->TriggerFirstOutput(true); @@ -1174,6 +1200,50 @@ const FFlowPreloadPolicy& UFlowAsset::GetPreloadPolicy() const return PreloadPolicy.Get(); } +void UFlowAsset::InitializeOutputDataReceiverAndValues(IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver) +{ + OutputDataReceiver = Cast(InOutputDataReceiver); + + // Initialize the live output store from the template asset's declarations + OutputDataPinValues.Values.Reset(); + + if (const UFlowAsset* Template = TemplateAsset.Get()) + { + for (const FFlowNamedDataPinProperty& Declaration : Template->OutputDataPinDeclarations) + { + if (Declaration.IsValid()) + { + OutputDataPinValues.Values.Add(Declaration.Name, Declaration.DataPinValue); + } + else + { + UE_LOG(LogFlow, Warning, TEXT("Invalid OutputDataPin %s"), *Declaration.Name.ToString()); + } + } + } +} + +void UFlowAsset::WriteOutputDataPinValue(const FName& PinName, const TInstancedStruct& Value) +{ + if (OutputDataPinValues.Values.Contains(PinName)) + { + OutputDataPinValues.Values[PinName] = Value; + } + else + { + UE_LOG(LogFlow, Warning, TEXT("Could not find pin named %s in WriteOutputDataPinValue"), *PinName.ToString()); + } +} + +void UFlowAsset::FlushOutputDataPinValuesToReceiver() +{ + if (IFlowGraphOutputDataReceiverInterface* Receiver = Cast(OutputDataReceiver.Get())) + { + // Do an immediate push to the receiver + Receiver->ReceiveOutputDataSnapshot(OutputDataPinValues); + } +} + void UFlowAsset::TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier) { for (UFlowNode_CustomInput* CustomInputNode : CustomInputNodes) diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 65c49e9b..50869a5c 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -85,7 +85,11 @@ void UFlowSubsystem::StartRootFlow(UObject* Owner, UFlowAsset* FlowAsset, const { if (UFlowAsset* NewFlow = CreateRootFlow(Owner, FlowAsset, bAllowMultipleInstances)) { - NewFlow->StartFlow(DataPinValueSupplier.GetInterface()); + // TODO (gtaylor) Not implementing output parameters "yet", + // see Subgraph node for the pioneer implementation. + constexpr IFlowGraphOutputDataReceiverInterface* OutputDataReceiverInterface = nullptr; + + NewFlow->StartFlow(DataPinValueSupplier.GetInterface(), OutputDataReceiverInterface); } } #if WITH_EDITOR @@ -182,13 +186,18 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons // get instanced asset from map - in case it was already instanced by calling CreateSubFlow() with bPreloading == true UFlowAsset* AssetInstance = InstancedSubFlows[SubGraphNode]; - AssetInstance->NodeOwningThisAssetInstance = SubGraphNode; + if (!AssetInstance->NodeOwningThisAssetInstance.IsValid()) + { + AssetInstance->NodeOwningThisAssetInstance = SubGraphNode; + } + check(AssetInstance->NodeOwningThisAssetInstance == SubGraphNode); + SubGraphNode->GetFlowAsset()->ActiveSubGraphs.Add(SubGraphNode, AssetInstance); // don't activate Start Node if we're loading Sub Graph from SaveGame if (SavedInstanceName.IsEmpty()) { - AssetInstance->StartFlow(SubGraphNode); + AssetInstance->StartFlow(SubGraphNode, SubGraphNode); } } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp index ea34286d..1afd1297 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_Finish.cpp @@ -2,21 +2,19 @@ #include "Nodes/Graph/FlowNode_Finish.h" +#include "FlowAsset.h" + #include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_Finish) UFlowNode_Finish::UFlowNode_Finish() { -#if WITH_EDITOR - Category = TEXT("Graph"); - NodeDisplayStyle = FlowNodeStyle::InOut; -#endif - OutputPins = {}; - AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; } void UFlowNode_Finish::ExecuteInput(const FName& PinName) { + CommitOutputDataPinValues(); + // this will call FinishFlow() Finish(); } diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SetGraphOutput.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SetGraphOutput.cpp new file mode 100644 index 00000000..0753dd86 --- /dev/null +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SetGraphOutput.cpp @@ -0,0 +1,113 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Nodes/Graph/FlowNode_SetGraphOutput.h" + +#include "FlowAsset.h" +#include "Types/FlowDataPinResults.h" +#include "Types/FlowNamedDataPinProperty.h" + +#if WITH_EDITOR +#include "Types/FlowAutoDataPinsWorkingData.h" +#endif + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowNode_SetGraphOutput) + +UFlowNode_SetGraphOutput::UFlowNode_SetGraphOutput() +{ +#if WITH_EDITOR + Category = TEXT("Graph"); + NodeDisplayStyle = FlowNodeStyle::InOut; +#endif + + AllowedSignalModes = {EFlowSignalMode::Enabled, EFlowSignalMode::Disabled}; +} + +void UFlowNode_SetGraphOutput::ExecuteInput(const FName& PinName) +{ + CommitOutputDataPinValues(); + + TriggerFirstOutput(true); +} + +void UFlowNode_SetGraphOutput::CommitOutputDataPinValues() +{ + UFlowAsset* FlowAsset = GetFlowAsset(); + if (!IsValid(FlowAsset)) + { + return; + } + + const UFlowAsset* TemplateAsset = FlowAsset->GetTemplateAsset(); + if (!IsValid(TemplateAsset)) + { + return; + } + + bool bNeedsFlush = false; + + for (const FFlowNamedDataPinProperty& Declaration : TemplateAsset->GetOutputDataPinDeclarations()) + { + if (!Declaration.IsValid()) + { + continue; + } + + if (!IsInputConnected(Declaration.Name)) + { + continue; + } + + const FFlowDataPinResult PinResult = TryResolveDataPin_SetGraphOutputAccess(Declaration.Name); + if (PinResult.Result == EFlowDataPinResolveResult::Success) + { + bNeedsFlush = true; + + FlowAsset->WriteOutputDataPinValue(Declaration.Name, PinResult.ResultValue); + } + } + + if (bNeedsFlush) + { + FlowAsset->FlushOutputDataPinValuesToReceiver(); + } +} + +#if WITH_EDITOR +bool UFlowNode_SetGraphOutput::SupportsContextPins() const +{ + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (IsValid(FlowAsset) && !FlowAsset->GetOutputDataPinDeclarations().IsEmpty()) + { + return true; + } + + return Super::SupportsContextPins(); +} + +void UFlowNode_SetGraphOutput::AutoGenerateDataPins(FFlowDataPinValueOwner& ValueOwner, FFlowAutoDataPinsWorkingData& InOutWorkingData) +{ + Super::AutoGenerateDataPins(ValueOwner, InOutWorkingData); + + const UFlowAsset* FlowAsset = GetFlowAsset(); + if (!IsValid(FlowAsset)) + { + return; + } + + for (FFlowNamedDataPinProperty DeclarationCopy : FlowAsset->GetOutputDataPinDeclarations()) + { + if (!DeclarationCopy.IsValid()) + { + continue; + } + + if (DeclarationCopy.DataPinValue.IsValid()) + { + FFlowDataPinValue& Value = DeclarationCopy.DataPinValue.GetMutable(); + Value.bIsInputPin = true; + } + + DeclarationCopy.AutoGenerateDataPinForProperty(ValueOwner, InOutWorkingData); + } +} +#endif diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 3c07698f..58dbc926 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -119,6 +119,11 @@ void UFlowNode_SubGraph::OnLoad_Implementation() } } +void UFlowNode_SubGraph::ReceiveOutputDataSnapshot(const FFlowOutputDataPinValues& Snapshot) +{ + CachedOutputDataPinValues = Snapshot; +} + #if WITH_EDITOR FText UFlowNode_SubGraph::K2_GetNodeTitle_Implementation() const @@ -236,6 +241,22 @@ void UFlowNode_SubGraph::AutoGenerateDataPins(FFlowDataPinValueOwner& ValueOwner } } } + + for (FFlowNamedDataPinProperty Declaration : Asset->GetOutputDataPinDeclarations()) + { + if (!Declaration.IsValid()) + { + continue; + } + + if (Declaration.DataPinValue.IsValid()) + { + FFlowDataPinValue& Value = Declaration.DataPinValue.GetMutable(); + Value.bIsInputPin = false; + } + + Declaration.AutoGenerateDataPinForProperty(ValueOwner, InOutWorkingData); + } } FFlowDataPinResult UFlowNode_SubGraph::TrySupplyDataPin(FName PinName) const @@ -248,6 +269,19 @@ FFlowDataPinResult UFlowNode_SubGraph::TrySupplyDataPin(FName PinName) const return Super::TrySupplyDataPin(PinName); } + // Check cached output data pin values first — output pins are never "input connected", + // so this must come before IsInputConnected to avoid a spurious "unknown input pin" error. + if (const TInstancedStruct* CachedValue = CachedOutputDataPinValues.Values.Find(PinName)) + { + if (CachedValue->IsValid()) + { + FFlowDataPinResult Result; + Result.Result = EFlowDataPinResolveResult::Success; + Result.ResultValue = *CachedValue; + return Result; + } + } + if (!IsInputConnected(PinName)) { const bool bHasAssetParams = IsInputConnected(AssetParams_MemberName) || !AssetParams.IsNull(); diff --git a/Source/Flow/Private/Policies/FlowPreloadPolicy.cpp b/Source/Flow/Private/Policies/FlowPreloadPolicy.cpp new file mode 100644 index 00000000..18b8e61b --- /dev/null +++ b/Source/Flow/Private/Policies/FlowPreloadPolicy.cpp @@ -0,0 +1,5 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors + +#include "Policies/FlowPreloadPolicy.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPreloadPolicy) diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index fa1048b0..82854ff2 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -5,7 +5,11 @@ #include "FlowTypes.h" #include "Asset/FlowAssetParamsTypes.h" #include "Asset/FlowDeferredTransitionScope.h" +#include "Interfaces/FlowGraphOutputDataReceiverInterface.h" #include "Nodes/FlowNode.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowNamedDataPinProperty.h" +#include "Types/FlowOutputDataPinValues.h" #if WITH_EDITOR #include "FlowMessageLog.h" @@ -26,6 +30,8 @@ struct FFlowPinConnectionPolicy; class UEdGraph; class UEdGraphNode; +class UFlowAsset; +class UFlowAssetParams; #if !UE_BUILD_SHIPPING DECLARE_DELEGATE(FFlowGraphEvent); @@ -49,6 +55,7 @@ class FLOW_API UFlowAsset : public UObject friend class FFlowAssetDetails; friend class FFlowNode_SubGraphDetails; friend class UFlowGraphSchema; + friend struct FFlowDeferredTransitionScope; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Flow Asset") FGuid AssetGuid; @@ -131,6 +138,15 @@ class FLOW_API UFlowAsset : public UObject UPROPERTY() TMap> Nodes; +public: + const TArray& GetOutputDataPinDeclarations() const { return OutputDataPinDeclarations; } + +protected: + /* Output Data Pins define typed data values that this graph produces when it finishes. + * Sub Graph node using this Flow Asset will generate a context Output Data Pin for every entry on this list. */ + UPROPERTY(EditAnywhere, Category = "Sub Graph") + TArray OutputDataPinDeclarations; + public: #if WITH_EDITOR FFlowGraphEvent OnSubGraphReconstructionRequested; @@ -357,6 +373,16 @@ class FLOW_API UFlowAsset : public UObject UPROPERTY(Transient) EFlowFinishPolicy FinishPolicy; + /* Receiver that will be given a snapshot of OutputDataPinValues when this graph finishes. + * Typically the SubGraph node that created this instance. */ + UPROPERTY(Transient) + TWeakObjectPtr OutputDataReceiver; + + /* Live output data pin values for this running instance. + * Initialized from OutputDataPinDeclarations defaults at StartFlow; updated by SetGraphOutput/Finish nodes. */ + UPROPERTY(Transient) + FFlowOutputDataPinValues OutputDataPinValues; + public: virtual void InitializeInstance(const TWeakObjectPtr InOwner, UFlowAsset& InTemplateAsset); virtual void DeinitializeInstance(); @@ -380,13 +406,23 @@ class FLOW_API UFlowAsset : public UObject AActor* TryFindActorOwner() const; virtual void PreStartFlow(); - virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr); + virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr, IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver = nullptr); + + /* Write a single output data pin value into the live store for this running instance. + * Called by SetGraphOutput and Finish nodes for each connected output pin. */ + void WriteOutputDataPinValue(const FName& PinName, const TInstancedStruct& Value); + + /* Flush all of the OutputDataPinValues to the receiver (if set) */ + void FlushOutputDataPinValuesToReceiver(); + bool HasStartedFlow() const; protected: virtual void FinishNode(UFlowNode* Node); void ResetNodes(); - + + void InitializeOutputDataReceiverAndValues(IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver); + public: virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance = true); diff --git a/Source/Flow/Public/Interfaces/FlowGraphOutputDataReceiverInterface.h b/Source/Flow/Public/Interfaces/FlowGraphOutputDataReceiverInterface.h new file mode 100644 index 00000000..d58e05d9 --- /dev/null +++ b/Source/Flow/Public/Interfaces/FlowGraphOutputDataReceiverInterface.h @@ -0,0 +1,27 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +#pragma once + +#include "UObject/Interface.h" + +#include "FlowGraphOutputDataReceiverInterface.generated.h" + +struct FFlowOutputDataPinValues; + +/** + * Interface for objects that receive a snapshot of a Flow Asset's output data pin values + * when the asset's graph finishes execution. + * Example: UFlowNode_SubGraph receives the snapshot and caches it for downstream pin resolution. + */ +UINTERFACE(MinimalAPI, NotBlueprintable, DisplayName = "Flow Graph Output Data Receiver Interface") +class UFlowGraphOutputDataReceiverInterface : public UInterface +{ + GENERATED_BODY() +}; + +class FLOW_API IFlowGraphOutputDataReceiverInterface +{ + GENERATED_BODY() + +public: + virtual void ReceiveOutputDataSnapshot(const FFlowOutputDataPinValues& OutputDataPinValues) { } +}; diff --git a/Source/Flow/Public/Nodes/FlowNodeBase.h b/Source/Flow/Public/Nodes/FlowNodeBase.h index c3c1a4d0..fac9cba8 100644 --- a/Source/Flow/Public/Nodes/FlowNodeBase.h +++ b/Source/Flow/Public/Nodes/FlowNodeBase.h @@ -288,6 +288,12 @@ class FLOW_API UFlowNodeBase UFUNCTION(BlueprintPure, Category = DataPins, DisplayName = "Resolve DataPin By Name") FFlowDataPinResult TryResolveDataPin(FName PinName) const; +protected: + /* Protected accessor for TryResolveDataPin()'s use + * (we still want "most" flow nodes to not use TryResolveDataPin directly, + * they should be using the template versions below.) */ + FFlowDataPinResult TryResolveDataPin_SetGraphOutputAccess(FName PinName) const { return TryResolveDataPin(PinName); } + public: /* Generic single-value resolve & extractor. */ template diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h b/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h index b4458eba..1717aa3c 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_Finish.h @@ -1,7 +1,7 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once -#include "Nodes/FlowNode.h" +#include "Nodes/Graph/FlowNode_SetGraphOutput.h" #include "FlowNode_Finish.generated.h" /** @@ -9,7 +9,7 @@ * All active nodes and sub graphs will be deactivated. */ UCLASS(NotBlueprintable, meta = (DisplayName = "Finish")) -class FLOW_API UFlowNode_Finish : public UFlowNode +class FLOW_API UFlowNode_Finish : public UFlowNode_SetGraphOutput { GENERATED_BODY() diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SetGraphOutput.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SetGraphOutput.h new file mode 100644 index 00000000..f3b89ef6 --- /dev/null +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SetGraphOutput.h @@ -0,0 +1,35 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +#pragma once + +#include "Nodes/FlowNode.h" +#include "FlowNode_SetGraphOutput.generated.h" + +/** + * Resolves connected input data pins from the Flow Asset's OutputDataPinDeclarations and + * writes their values to the asset's output data store. Functions as a pass-through exec node. + * UFlowNode_Finish derives from this class and additionally finishes the graph. + */ +UCLASS(NotBlueprintable, meta = (DisplayName = "Set Graph Output")) +class FLOW_API UFlowNode_SetGraphOutput : public UFlowNode +{ + GENERATED_BODY() + +public: + UFlowNode_SetGraphOutput(); + +protected: + virtual void ExecuteInput(const FName& PinName) override; + + /* Resolve all connected input data pins and write them to the Flow Asset's output store. */ + void CommitOutputDataPinValues(); + +#if WITH_EDITOR + // IFlowContextPinSupplierInterface + virtual bool SupportsContextPins() const override; + // -- + + // IFlowDataPinValueOwnerInterface + virtual void AutoGenerateDataPins(FFlowDataPinValueOwner& ValueOwner, FFlowAutoDataPinsWorkingData& InOutWorkingData) override; + // -- +#endif +}; diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index 5d7dfaf6..565a12c3 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -1,8 +1,12 @@ // Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors #pragma once +#include "Interfaces/FlowGraphOutputDataReceiverInterface.h" #include "Interfaces/FlowPreloadableInterface.h" #include "Nodes/FlowNode.h" +#include "Types/FlowDataPinValue.h" +#include "Types/FlowOutputDataPinValues.h" +#include "StructUtils/InstancedStruct.h" #include "FlowNode_SubGraph.generated.h" class UFlowAssetParams; @@ -14,11 +18,12 @@ UCLASS(NotBlueprintable, meta = (DisplayName = "Sub Graph")) class FLOW_API UFlowNode_SubGraph : public UFlowNode , public IFlowPreloadableInterface + , public IFlowGraphOutputDataReceiverInterface { GENERATED_BODY() public: - UFlowNode_SubGraph(); + UFlowNode_SubGraph(); friend class UFlowAsset; friend class FFlowNode_SubGraphDetails; @@ -27,7 +32,7 @@ class FLOW_API UFlowNode_SubGraph static FFlowPin StartPin; static FFlowPin FinishPin; -private: +protected: UPROPERTY(EditAnywhere, Category = "Graph") TSoftObjectPtr Asset; @@ -43,7 +48,17 @@ class FLOW_API UFlowNode_SubGraph UPROPERTY(SaveGame) FString SavedAssetInstanceName; + /* Cached output data pin values received from the inner Flow Asset when it finishes. + * Note - Not saved "yet", but should decide if we should include the + * cached output values in the save state. */ + UPROPERTY(Transient) + FFlowOutputDataPinValues CachedOutputDataPinValues; + protected: + // IFlowGraphOutputDataReceiverInterface + virtual void ReceiveOutputDataSnapshot(const FFlowOutputDataPinValues& Snapshot) override; + // -- + virtual bool CanBeAssetInstanced() const; // IFlowPreloadableInterface diff --git a/Source/Flow/Public/Types/FlowOutputDataPinValues.h b/Source/Flow/Public/Types/FlowOutputDataPinValues.h new file mode 100644 index 00000000..6d587ac2 --- /dev/null +++ b/Source/Flow/Public/Types/FlowOutputDataPinValues.h @@ -0,0 +1,25 @@ +// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors +#pragma once + +#include "UObject/NameTypes.h" +#include "StructUtils/InstancedStruct.h" +#include "Containers/Map.h" + +#include "FlowOutputDataPinValues.generated.h" + +struct FFlowDataPinValue; + +/** + * Container for output data pin values from a Flow Asset execution. + * Holds a map of pin names to their resolved values. + */ +USTRUCT() +struct FFlowOutputDataPinValues +{ + GENERATED_BODY() + +public: + /* Map of output pin names to their resolved values. */ + UPROPERTY() + TMap> Values; +};