From 04a0a4d771f3e92d4d3191fc95bf4f626ffec042 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:50:52 -0700 Subject: [PATCH 1/2] Deinitialize Instance for subgraph changes [Flow Graph] Consolidating Finish Flow concept and removing deinitialize instance as part of a flow asset finish. A new method has been introduced (FinishAndDeinitialize) to disambiguate. --- Config/DefaultFlow.ini | 2 + Source/Flow/Private/FlowAsset.cpp | 20 +++++----- Source/Flow/Private/FlowComponent.cpp | 4 +- Source/Flow/Private/FlowSubsystem.cpp | 39 ++++++++++++++++--- .../Private/Nodes/Graph/FlowNode_SubGraph.cpp | 16 +++++++- Source/Flow/Public/FlowAsset.h | 4 ++ Source/Flow/Public/FlowSubsystem.h | 9 ++++- .../Public/Nodes/Graph/FlowNode_SubGraph.h | 3 +- 8 files changed, 74 insertions(+), 23 deletions(-) diff --git a/Config/DefaultFlow.ini b/Config/DefaultFlow.ini index fe5504715..703e82785 100644 --- a/Config/DefaultFlow.ini +++ b/Config/DefaultFlow.ini @@ -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") \ No newline at end of file diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index aba469583..94537d104 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -896,7 +896,7 @@ void UFlowAsset::ClearInstances() { if (ActiveInstances.IsValidIndex(i) && ActiveInstances[i]) { - ActiveInstances[i]->FinishFlow(EFlowFinishPolicy::Keep); + ActiveInstances[i]->FinishFlowAndDeinitializeInstance(EFlowFinishPolicy::Keep); } } @@ -1023,6 +1023,11 @@ AActor* UFlowAsset::TryFindActorOwner() const return nullptr; } +{ + FinishFlow(InFinishPolicy); + DeinitializeInstance(); +} + void UFlowAsset::PreStartFlow() { ResetNodes(); @@ -1081,13 +1086,14 @@ void UFlowAsset::FinishNode(UFlowNode* Node) 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& RootFlowInstances = GetFlowSubsystem()->GetRootInstancesByOwner(Owner.Get()); if (RootFlowInstances.Contains(this)) { - GetFlowSubsystem()->FinishRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); + GetFlowSubsystem()->FinishAndDeinitializeRootFlow(Owner.Get(), TemplateAsset, EFlowFinishPolicy::Keep); return; } @@ -1108,7 +1114,7 @@ void UFlowAsset::ResetNodes() RecordedNodes.Empty(); } -void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool bRemoveInstance /*= true*/) +void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy) { FinishPolicy = InFinishPolicy; @@ -1120,12 +1126,6 @@ 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(); - } } UFlowSubsystem* UFlowAsset::GetFlowSubsystem() const diff --git a/Source/Flow/Private/FlowComponent.cpp b/Source/Flow/Private/FlowComponent.cpp index da1dbe1f6..62ca1dbc3 100644 --- a/Source/Flow/Private/FlowComponent.cpp +++ b/Source/Flow/Private/FlowComponent.cpp @@ -99,7 +99,7 @@ void UFlowComponent::UnregisterWithFlowSubsystem() { if (UFlowSubsystem* FlowSubsystem = GetFlowSubsystem()) { - FlowSubsystem->FinishAllRootFlows(this, EFlowFinishPolicy::Keep); + FlowSubsystem->FinishAndDeinitializeAllRootFlows(this, EFlowFinishPolicy::Keep); FlowSubsystem->UnregisterComponent(this); } } @@ -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); } } diff --git a/Source/Flow/Private/FlowSubsystem.cpp b/Source/Flow/Private/FlowSubsystem.cpp index 65c49e9b0..ad4d2c728 100644 --- a/Source/Flow/Private/FlowSubsystem.cpp +++ b/Source/Flow/Private/FlowSubsystem.cpp @@ -123,7 +123,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; @@ -139,11 +139,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 InstancesToFinish; @@ -158,7 +158,7 @@ void UFlowSubsystem::FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy for (UFlowAsset* InstanceToFinish : InstancesToFinish) { RootInstances.Remove(InstanceToFinish); - InstanceToFinish->FinishFlow(FinishPolicy); + InstanceToFinish->FinishFlowAndDeinitializeInstance(FinishPolicy); } } @@ -182,7 +182,12 @@ 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 @@ -195,6 +200,23 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons 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)) @@ -204,7 +226,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; diff --git a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp index 3c07698f7..0ad873f26 100644 --- a/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp +++ b/Source/Flow/Private/Nodes/Graph/FlowNode_SubGraph.cpp @@ -97,14 +97,26 @@ void UFlowNode_SubGraph::ExecuteInput(const FName& PinName) void UFlowNode_SubGraph::Cleanup() { - if (CanBeAssetInstanced() && GetFlowSubsystem()) + UFlowSubsystem* FlowSubsystem = GetFlowSubsystem(); + if (CanBeAssetInstanced() && FlowSubsystem) { - GetFlowSubsystem()->RemoveSubFlow(this, EFlowFinishPolicy::Keep); + FlowSubsystem->FinishSubFlow(this, EFlowFinishPolicy::Keep); } Super::Cleanup(); } +void UFlowNode_SubGraph::DeinitializeInstance() +{ + UFlowSubsystem* FlowSubsystem = GetFlowSubsystem(); + if (CanBeAssetInstanced() && FlowSubsystem) + { + FlowSubsystem->RemoveSubFlow(this, EFlowFinishPolicy::Keep); + } + + Super::DeinitializeInstance(); +} + void UFlowNode_SubGraph::ForceFinishNode() { TriggerFirstOutput(true); diff --git a/Source/Flow/Public/FlowAsset.h b/Source/Flow/Public/FlowAsset.h index fa1048b08..6bc0cf415 100644 --- a/Source/Flow/Public/FlowAsset.h +++ b/Source/Flow/Public/FlowAsset.h @@ -362,6 +362,8 @@ class FLOW_API UFlowAsset : public UObject virtual void DeinitializeInstance(); bool IsInstanceInitialized() const { return IsValid(TemplateAsset); } + void FinishFlowAndDeinitializeInstance(const EFlowFinishPolicy InFinishPolicy); + UFlowAsset* GetTemplateAsset() const { return TemplateAsset; } /* Object that spawned Root Flow instance, i.e. World Settings or Player Controller. @@ -381,6 +383,8 @@ class FLOW_API UFlowAsset : public UObject virtual void PreStartFlow(); virtual void StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier = nullptr); + virtual void FinishFlow(const EFlowFinishPolicy InFinishPolicy); + bool HasStartedFlow() const; protected: diff --git a/Source/Flow/Public/FlowSubsystem.h b/Source/Flow/Public/FlowSubsystem.h index ea68d8921..5cf330581 100644 --- a/Source/Flow/Public/FlowSubsystem.h +++ b/Source/Flow/Public/FlowSubsystem.h @@ -78,16 +78,21 @@ class FLOW_API UFlowSubsystem : public UGameInstanceSubsystem * Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted * Example: Spawn node might despawn all actors if Flow Graph is aborted, not completed */ UFUNCTION(BlueprintCallable, Category = "FlowSubsystem", meta = (DefaultToSelf = "Owner")) - virtual void FinishRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); + virtual void FinishAndDeinitializeRootFlow(UObject* Owner, UFlowAsset* TemplateAsset, const EFlowFinishPolicy FinishPolicy); /* Finish Policy value is read by Flow Node * Nodes have opportunity to terminate themselves differently if Flow Graph has been aborted * Example: Spawn node might despawn all actors if Flow Graph is aborted, not completed */ UFUNCTION(BlueprintCallable, Category = "FlowSubsystem", meta = (DefaultToSelf = "Owner")) - virtual void FinishAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy); + virtual void FinishAndDeinitializeAllRootFlows(UObject* Owner, const EFlowFinishPolicy FinishPolicy); protected: UFlowAsset* CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, const FString& SavedInstanceName = FString(), const bool bPreloading = false); + + /* Finishes the SubFlow running in the SubGraphNode. It does not deinitialize or removes from the internal InstancedSubFlows list */ + void FinishSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); + + /* Removes the Subflow from the InstancedSubFlows list; and Finishes and Deinitializes it. */ void RemoveSubFlow(UFlowNode_SubGraph* SubGraphNode, const EFlowFinishPolicy FinishPolicy); public: diff --git a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h index 5d7dfaf62..547c8fdce 100644 --- a/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h +++ b/Source/Flow/Public/Nodes/Graph/FlowNode_SubGraph.h @@ -18,7 +18,7 @@ class FLOW_API UFlowNode_SubGraph GENERATED_BODY() public: - UFlowNode_SubGraph(); + UFlowNode_SubGraph(); friend class UFlowAsset; friend class FFlowNode_SubGraphDetails; @@ -53,6 +53,7 @@ class FLOW_API UFlowNode_SubGraph virtual void ExecuteInput(const FName& PinName) override; virtual void Cleanup() override; + virtual void DeinitializeInstance() override; public: virtual void ForceFinishNode() override; From d75cfdca4848f5ff0d8946a1138bb4c931b96828 Mon Sep 17 00:00:00 2001 From: LindyHopperGT <91915878+LindyHopperGT@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:51:08 -0700 Subject: [PATCH 2/2] Update FlowAsset.cpp --- Source/Flow/Private/FlowAsset.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Flow/Private/FlowAsset.cpp b/Source/Flow/Private/FlowAsset.cpp index 94537d104..34d291981 100644 --- a/Source/Flow/Private/FlowAsset.cpp +++ b/Source/Flow/Private/FlowAsset.cpp @@ -1023,6 +1023,7 @@ AActor* UFlowAsset::TryFindActorOwner() const return nullptr; } +void UFlowAsset::FinishFlowAndDeinitializeInstance(const EFlowFinishPolicy InFinishPolicy) { FinishFlow(InFinishPolicy); DeinitializeInstance();