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
21 changes: 21 additions & 0 deletions Source/Flow/Private/FlowAsset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "AddOns/FlowNodeAddOn.h"
#include "Asset/FlowAssetParams.h"
#include "Asset/FlowAssetParamsUtils.h"
#include "Interfaces/FlowExecutionGate.h"
#include "Nodes/FlowNodeBase.h"
#include "Nodes/Graph/FlowNode_CustomInput.h"
#include "Nodes/Graph/FlowNode_CustomOutput.h"
Expand Down Expand Up @@ -1012,6 +1013,11 @@ void UFlowAsset::PreStartFlow()

void UFlowAsset::StartFlow(IFlowDataPinValueSupplierInterface* DataPinValueSupplier)
{
if (FFlowExecutionGate::IsHalted())
{
return;
}

PreStartFlow();

if (UFlowNode* ConnectedEntryNode = GetDefaultEntryNode())
Expand Down Expand Up @@ -1075,6 +1081,11 @@ TWeakObjectPtr<UFlowAsset> UFlowAsset::GetFlowInstance(UFlowNode_SubGraph* SubGr

void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNode, const FName& EventName) const
{
if (FFlowExecutionGate::IsHalted())
{
return;
}

// NOTE (gtaylor) Custom Input nodes cannot currently add data pins (like Start or DefineProperties nodes can)
// but we may want to allow them to source parameters, so I am providing the subgraph node as the
// IFlowDataPinValueSupplierInterface when triggering the node (even though it's not used at this time).
Expand All @@ -1088,6 +1099,11 @@ void UFlowAsset::TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* SubGraphNod

void UFlowAsset::TriggerCustomInput(const FName& EventName, IFlowDataPinValueSupplierInterface* DataPinValueSupplier)
{
if (FFlowExecutionGate::IsHalted())
{
return;
}

for (UFlowNode_CustomInput* CustomInputNode : CustomInputNodes)
{
if (CustomInputNode->EventName == EventName)
Expand Down Expand Up @@ -1127,6 +1143,11 @@ void UFlowAsset::TriggerCustomOutput(const FName& EventName)

void UFlowAsset::TriggerInput(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin)
{
if (FFlowExecutionGate::EnqueueDeferredTriggerInput(this, NodeGuid, PinName, FromPin))
{
return;
}

if (UFlowNode* Node = Nodes.FindRef(NodeGuid))
{
if (!ActiveNodes.Contains(Node))
Expand Down
134 changes: 134 additions & 0 deletions Source/Flow/Private/Interfaces/FlowExecutionGate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "Interfaces/FlowExecutionGate.h"

#include "FlowAsset.h"
#include "Nodes/FlowPin.h"

namespace FlowExecutionGate_Private
{
struct FDeferredTriggerInput
{
TWeakObjectPtr<UFlowAsset> FlowAssetInstance;
FGuid NodeGuid;
FName PinName;
FConnectedPin FromPin;
};

static TArray<FDeferredTriggerInput> DeferredTriggerInputs;
static bool bIsFlushing = false;
}

IFlowExecutionGate* FFlowExecutionGate::Gate = nullptr;

void FFlowExecutionGate::SetGate(IFlowExecutionGate* InGate)
{
Gate = InGate;
}

IFlowExecutionGate* FFlowExecutionGate::GetGate()
{
return Gate;
}

bool FFlowExecutionGate::IsHalted()
{
return (Gate != nullptr) && Gate->IsFlowExecutionHalted();
}

bool FFlowExecutionGate::EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin)
{
using namespace FlowExecutionGate_Private;

// If we're halted, always enqueue (even during flushing). The whole point is to stop propagation.
if (IsHalted())
{
if (!IsValid(FlowAssetInstance))
{
return true; // treat as handled while halted
}

FDeferredTriggerInput& Entry = DeferredTriggerInputs.AddDefaulted_GetRef();
Entry.FlowAssetInstance = FlowAssetInstance;
Entry.NodeGuid = NodeGuid;
Entry.PinName = PinName;
Entry.FromPin = FromPin;

return true;
}

// Not halted:
// During flush we must not enqueue "normal" triggers (we want them to execute now),
// otherwise we can get infinite deferral.
if (bIsFlushing)
{
return false;
}

return false;
}

void FFlowExecutionGate::FlushDeferredTriggerInputs()
{
using namespace FlowExecutionGate_Private;

if (bIsFlushing)
{
return;
}

// Do not flush while halted; callers should clear the halt first.
if (IsHalted())
{
return;
}

if (DeferredTriggerInputs.IsEmpty())
{
return;
}

bIsFlushing = true;

// Move into a local array so new deferred triggers can be added while we flush.
TArray<FDeferredTriggerInput> Local = MoveTemp(DeferredTriggerInputs);
DeferredTriggerInputs.Reset();

for (int32 Index = 0; Index < Local.Num(); ++Index)
{
// If a breakpoint was hit during this flush, stop immediately and re-queue remaining work.
if (IsHalted())
{
const int32 Remaining = Local.Num() - Index;
if (Remaining > 0)
{
TArray<FDeferredTriggerInput> RemainingItems;
RemainingItems.Reserve(Remaining);

for (int32 j = Index; j < Local.Num(); ++j)
{
RemainingItems.Add(Local[j]);
}

// RemainingItems should run before any items that may already be queued.
if (!DeferredTriggerInputs.IsEmpty())
{
RemainingItems.Append(MoveTemp(DeferredTriggerInputs));
}

DeferredTriggerInputs = MoveTemp(RemainingItems);
}

bIsFlushing = false;
return;
}

const FDeferredTriggerInput& Entry = Local[Index];
if (UFlowAsset* Asset = Entry.FlowAssetInstance.Get())
{
Asset->TriggerDeferredInputFromDebugger(Entry.NodeGuid, Entry.PinName, Entry.FromPin);
}
}

bIsFlushing = false;
}
1 change: 0 additions & 1 deletion Source/Flow/Private/Nodes/FlowPin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
// Pin Record

#if !UE_BUILD_SHIPPING
FString FPinRecord::NoActivations = TEXT("No activations");
FString FPinRecord::PinActivations = TEXT("Pin activations");
FString FPinRecord::ForcedActivation = TEXT(" (forced activation)");
FString FPinRecord::PassThroughActivation = TEXT(" (pass-through activation)");
Expand Down
2 changes: 1 addition & 1 deletion Source/Flow/Private/Types/FlowDataPinValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowDataPinValue)

const FString FFlowDataPinValue::StringArraySeparator = TEXT(",");
const FString FFlowDataPinValue::StringArraySeparator = TEXT(", ");

const FFlowPinType* FFlowDataPinValue::LookupPinType() const
{
Expand Down
38 changes: 38 additions & 0 deletions Source/Flow/Private/Types/FlowDataPinValuesStandard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,44 @@ FFlowDataPinValue_InstancedStruct::FFlowDataPinValue_InstancedStruct(const TArra
#endif
}

bool FFlowDataPinValue_InstancedStruct::TryConvertValuesToString(FString& OutString) const
{
const FInstancedStruct DefaultValue;

OutString = FlowArray::FormatArrayString<FInstancedStruct>(
Values,
[&DefaultValue](const FInstancedStruct& InstancedStruct)
{
FString ExportedString;

constexpr UObject* ParentObject = nullptr;
constexpr UObject* ExportRootScope = nullptr;

const bool bExported = InstancedStruct.ExportTextItem(
ExportedString,
DefaultValue,
ParentObject,
PPF_None,
ExportRootScope);

if (!bExported)
{
// Fallback: just show the contained struct type name (or None)
if (const UScriptStruct* ScriptStruct = InstancedStruct.GetScriptStruct())
{
return ScriptStruct->GetName();
}

return FString();
}

return ExportedString;
},
StringArraySeparator);

return true;
}

//======================================================================
// Object
//======================================================================
Expand Down
6 changes: 5 additions & 1 deletion Source/Flow/Public/FlowAsset.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class UFlowAssetParams;

#if !UE_BUILD_SHIPPING
DECLARE_DELEGATE(FFlowGraphEvent);
DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, const UFlowNode* /*Node*/, const FName& /*PinName*/);
DECLARE_DELEGATE_TwoParams(FFlowSignalEvent, UFlowNode* /*FlowNode*/, const FName& /*PinName*/);
#endif

/**
Expand Down Expand Up @@ -358,6 +358,10 @@ class FLOW_API UFlowAsset : public UObject
// Get Flow Asset instance created by the given SubGraph node
TWeakObjectPtr<UFlowAsset> GetFlowInstance(UFlowNode_SubGraph* SubGraphNode) const;

// Public trigger input signature for the FFlowExecutionGate mechanism in the Flow Debugger
FORCEINLINE void TriggerDeferredInputFromDebugger(const FGuid& NodeGuid, const FName& PinName, const FConnectedPin& FromPin)
{ TriggerInput(NodeGuid, PinName, FromPin); }

protected:
void TriggerCustomInput_FromSubGraph(UFlowNode_SubGraph* Node, const FName& EventName) const;
void TriggerCustomOutput(const FName& EventName);
Expand Down
45 changes: 45 additions & 0 deletions Source/Flow/Public/Interfaces/FlowExecutionGate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#pragma once

#include "CoreMinimal.h"

class UFlowAsset;

/**
* Implemented by a debugger/runtime system (in another module) that can halt Flow execution.
* Flow runtime queries this through FFlowExecutionGate without depending on the debugger module.
*/
class FLOW_API IFlowExecutionGate
{
public:
virtual ~IFlowExecutionGate() = default;

/** Return true when Flow execution should be halted globally. */
virtual bool IsFlowExecutionHalted() const = 0;
};

/**
* Global registry + minimal deferred-execution queue for Flow runtime.
*/
class FLOW_API FFlowExecutionGate
{
public:
static void SetGate(IFlowExecutionGate* InGate);
static IFlowExecutionGate* GetGate();

/** True if a gate exists and it currently wants Flow execution halted. */
static bool IsHalted();

/** If halted, queues the trigger for later. Returns true if queued (caller should early-out). */
static bool EnqueueDeferredTriggerInput(UFlowAsset* FlowAssetInstance, const FGuid& NodeGuid, const FName& PinName, const struct FConnectedPin& FromPin);

/**
* Flushes queued trigger inputs (FIFO).
* Safe to call even if nothing is queued.
*/
static void FlushDeferredTriggerInputs();

private:
static IFlowExecutionGate* Gate;
};
1 change: 0 additions & 1 deletion Source/Flow/Public/Nodes/FlowPin.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ struct FLOW_API FPinRecord
FString HumanReadableTime;
EFlowPinActivationType ActivationType;

static FString NoActivations;
static FString PinActivations;
static FString ForcedActivation;
static FString PassThroughActivation;
Expand Down
Loading