Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Config/DefaultFlow.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
+PropertyRedirects=(OldName="FlowGraphNode.FlowNode",NewName="FlowGraphNode.NodeInstance")
+StructRedirects=(OldName="/Script/Flow.FlowNamedDataPinOutputProperty",NewName="/Script/Flow.FlowNamedDataPinProperty")
+PropertyRedirects=(OldName="FlowNode_DefineProperties.OutputProperties",NewName="NamedProperties")
+FunctionRedirects=(OldName="/Script/Flow.FlowSubsystem.FinishRootFlow",NewName="/Script/Flow.FlowSubsystem.FinishAndDeinitializeRootFlow")
+FunctionRedirects=(OldName="/Script/Flow.FlowSubsystem.FinishAllRootFlows",NewName="/Script/Flow.FlowSubsystem.FinishAndDeinitializeAllRootFlows")
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MIT License
MIT License

Copyright (c) https://github.com/MothCocoon/FlowGraph/graphs/contributors

Expand Down
103 changes: 90 additions & 13 deletions Source/Flow/Private/FlowAsset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
#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"
#include "Nodes/Graph/FlowNode_Start.h"
#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"

Expand All @@ -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"
Expand Down Expand Up @@ -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<FGuid, UFlowNode*>& NodePair : GetNodes())
{
UFlowNode_SetGraphOutput* SetOutputNode = Cast<UFlowNode_SetGraphOutput>(NodePair.Value);
if (IsValid(SetOutputNode) && SetOutputNode->TryUpdateAutoDataPins())
{
SetOutputNode->OnReconstructionRequested.ExecuteIfBound();
}
}
}
}

void UFlowAsset::PostDuplicate(bool bDuplicateForPIE)
Expand Down Expand Up @@ -896,7 +915,7 @@ void UFlowAsset::ClearInstances()
{
if (ActiveInstances.IsValidIndex(i) && ActiveInstances[i])
{
ActiveInstances[i]->FinishFlow(EFlowFinishPolicy::Keep);
ActiveInstances[i]->FinishFlowAndDeinitializeInstance(EFlowFinishPolicy::Keep);
}
}

Expand Down Expand Up @@ -1023,6 +1042,12 @@ AActor* UFlowAsset::TryFindActorOwner() const
return nullptr;
}

void UFlowAsset::FinishFlowAndDeinitializeInstance(const EFlowFinishPolicy InFinishPolicy)
{
FinishFlow(InFinishPolicy);
DeinitializeInstance();
}

void UFlowAsset::PreStartFlow()
{
ResetNodes();
Expand All @@ -1043,8 +1068,10 @@ void UFlowAsset::PreStartFlow()
#endif
}

void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier)
void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier, IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver)
{
InitializeOutputDataReceiverAndValues(InOutputDataReceiver);

PreStartFlow();

if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode())
Expand Down Expand Up @@ -1074,20 +1101,26 @@ 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<IFlowGraphOutputDataReceiverInterface>(OutputDataReceiver.Get()))
{
Receiver->ReceiveOutputDataSnapshot(OutputDataPinValues);
}

if (NodeOwningThisAssetInstance.IsValid())
{
NodeOwningThisAssetInstance.Get()->TriggerFirstOutput(true);

return;
}

// if this instance is a Root Flow, we need to deregister it from the subsystem first
// if this instance is a Root Flow, we need to deregister it from the subsystem first. This will
// finalize and deinitialize the root flow.
if (Owner.IsValid())
{
const TSet<UFlowAsset*>& RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get());
if (RootFlowInstances.Contains(this))
{
GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep);
GetFlowSubsystem()->FinishAndDeinitializeRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep);

return;
}
Expand All @@ -1108,7 +1141,7 @@ void UFlowAsset::ResetNodes()
RecordedNodes.Empty();
}

void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance /*= true*/)
void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy)
{
FinishPolicy = InFinishPolicy;

Expand All @@ -1119,13 +1152,8 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool b
{
Node->Deactivate();
}
ActiveNodes.Empty();

// provides option to finish game-specific logic prior to removing asset instance
if (bRemoveInstance)
{
DeinitializeInstance();
}
ActiveNodes.Empty();
}

UFlowSubsystem* UFlowAsset::GetFlowSubsystem() const
Expand All @@ -1148,6 +1176,11 @@ TWeakObjectPtr<UFlowAsset> UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr
return ActiveSubGraphs.FindRef(SubGraphNode);
}

FName UFlowAsset::GetDisplayName() const
{
return GetFName();
}

void UFlowAsset::InitializePreloadPolicy()
{
if (PreloadPolicy.IsValid())
Expand All @@ -1174,6 +1207,50 @@ const FFlowPreloadPolicy& UFlowAsset::GetPreloadPolicy() const
return PreloadPolicy.Get();
}

void UFlowAsset::InitializeOutputDataReceiverAndValues(IFlowGraphOutputDataReceiverInterface* InOutputDataReceiver)
{
OutputDataReceiver = Cast<UObject>(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<FFlowDataPinValue>& 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<IFlowGraphOutputDataReceiverInterface>(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)
Expand Down
4 changes: 2 additions & 2 deletions Source/Flow/Private/FlowComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void UFlowComponent::UnregisterWithFlowSubsystem()
{
if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem())
{
FlowSubsystem->FinishAllRootFlows(this, EFlowFinishPolicy::Keep);
FlowSubsystem->FinishAndDeinitializeAllRootFlows(this, EFlowFinishPolicy::Keep);
FlowSubsystem->UnregisterComponent(this);
}
}
Expand Down Expand Up @@ -461,7 +461,7 @@ void UFlowComponent::FinishRootFlow(UFlowAsset* TemplateAsset, const EFlowFinish
{
if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem())
{
FlowSubsystem->FinishRootFlow(this, TemplateAsset, FinishPolicy);
FlowSubsystem->FinishAndDeinitializeRootFlow(this, TemplateAsset, FinishPolicy);
}
}

Expand Down
47 changes: 39 additions & 8 deletions Source/Flow/Private/FlowSubsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -123,7 +127,7 @@ UFlowAsset* UFlowSubsystem::CreateRootFlow(UObject* Owner, UFlowAsset* FlowAsset
return NewFlow;
}

void UFlowSubsystem::FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy)
void UFlowSubsystem::FinishAndDeinitializeRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy)
{
UFlowAsset* InstanceToFinish = nullptr;

Expand All @@ -139,11 +143,11 @@ void UFlowSubsystem::FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, c
if (InstanceToFinish)
{
RootInstances.Remove(InstanceToFinish);
InstanceToFinish->FinishFlow(FinishPolicy);
InstanceToFinish->FinishFlowAndDeinitializeInstance(FinishPolicy);
}
}

void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy)
void UFlowSubsystem::FinishAndDeinitializeAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy)
{
TArray<UFlowAsset*> InstancesToFinish;

Expand All @@ -158,7 +162,7 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy
for (UFlowAsset* InstanceToFinish : InstancesToFinish)
{
RootInstances.Remove(InstanceToFinish);
InstanceToFinish->FinishFlow(FinishPolicy);
InstanceToFinish->FinishFlowAndDeinitializeInstance(FinishPolicy);
}
}

Expand All @@ -182,19 +186,41 @@ 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);
}
}

return NewInstance;
}

void UFlowSubsystem::FinishSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy)
{
if (InstancedSubFlows.Contains(SubGraphNode))
{
// The flow asset running on the subgraph node.
UFlowAsset* SubgraphFlowAsset = InstancedSubFlows[SubGraphNode];

// This is the flow asset that has the subgraph node. Do not confuse with the flow asset that the node is running.
// Remove the subgraph flow from the owning flow active subgraph list.
UFlowAsset* SubgraphNodeParentFlow = SubGraphNode->GetFlowAsset();
SubgraphNodeParentFlow->ActiveSubGraphs.Remove(SubGraphNode);

// Finish the flow but do not remove the instance.
SubgraphFlowAsset->FinishFlow(FinishPolicy);
}
}

void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy)
{
if (InstancedSubFlows.Contains(SubGraphNode))
Expand All @@ -204,7 +230,12 @@ void UFlowSubsystem::RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlow
SubGraphNode->GetFlowAsset()->ActiveSubGraphs.Remove(SubGraphNode);
InstancedSubFlows.Remove(SubGraphNode);

AssetInstance->FinishFlow(FinishPolicy);
if (AssetInstance->IsActive())
{
AssetInstance->FinishFlow(FinishPolicy);
}

AssetInstance->DeinitializeInstance();

// Make sure to set the NodeOwningThisAssetInstance after the FinishFlow call, as it may be needed in the FinishFlow method
AssetInstance->NodeOwningThisAssetInstance = nullptr;
Expand Down
Loading