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: 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
8 changes: 8 additions & 0 deletions Source/Flow/Private/AddOns/FlowNodeAddOn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ EFlowAddOnAcceptResult UFlowNodeAddOn::AcceptFlowNodeAddOnParent_Implementation(
return EFlowAddOnAcceptResult::Undetermined;
}

void UFlowNodeAddOn::NotifyPreloadComplete()
{
if (ensure(FlowNode))
{
FlowNode->NotifyPreloadComplete();
}
}

UFlowNode* UFlowNodeAddOn::GetFlowNode() const
{
// We are making the assumption that this would always be known during runtime
Expand Down
58 changes: 51 additions & 7 deletions Source/Flow/Private/FlowAsset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "Nodes/Graph/FlowNode_Start.h"
#include "Nodes/Graph/FlowNode_SubGraph.h"
#include "Policies/FlowPinConnectionPolicy.h"
#include "Policies/FlowPreloadPolicy.h"
#include "Types/FlowDataPinValue.h"
#include "Types/FlowStructUtils.h"

Expand Down Expand Up @@ -55,6 +56,7 @@ UFlowAsset::UFlowAsset(const FObjectInitializer& ObjectInitializer)
, TemplateAsset(nullptr)
, FinishPolicy(EFlowFinishPolicy::Keep)
, PinConnectionPolicy()
, PreloadPolicy()
{
if (!AssetGuid.IsValid())
{
Expand All @@ -70,6 +72,7 @@ void UFlowAsset::PostInitProperties()

#if WITH_EDITOR
InitializePinConnectionPolicy();
InitializePreloadPolicy();
#endif
}

Expand Down Expand Up @@ -1012,13 +1015,6 @@ void UFlowAsset::FinishFlow(const EFlowFinishPolicy InFinishPolicy, const bool b
}
ActiveNodes.Empty();

// flush preloaded content
for (UFlowNode* PreloadedNode : PreloadedNodes)
{
PreloadedNode->TriggerFlush();
}
PreloadedNodes.Empty();

// provides option to finish game-specific logic prior to removing asset instance
if (bRemoveInstance)
{
Expand Down Expand Up @@ -1104,6 +1100,19 @@ AActor* UFlowAsset::TryFindActorOwner() const
return OwnerAsActor;
}

// If the owner is a Component, return its owning Actor
if (const UActorComponent* OwnerAsComponent = Cast<UActorComponent>(OwnerObject))
{
{
return nullptr;
}

// If the owner is already an Actor, return it directly
if (AActor* OwnerAsActor = Cast<AActor>(OwnerObject))
{
return OwnerAsActor;
}

// If the owner is a Component, return its owning Actor
if (const UActorComponent* OwnerAsComponent = Cast<UActorComponent>(OwnerObject))
{
Expand Down Expand Up @@ -1449,6 +1458,7 @@ const FFlowPinConnectionPolicy& UFlowAsset::GetPinConnectionPolicy() const
}

// Graceful fallback: if PinConnectionPolicy was never initialized (asset predates this feature,
// or was never opened in editor), read directly from project settings at runtime.
// or was never opened in editor), read directly from Project Settings at runtime.
if (!PinConnectionPolicy.IsValid())
{
Expand All @@ -1464,6 +1474,31 @@ const FFlowPinConnectionPolicy& UFlowAsset::GetPinConnectionPolicy() const
return PinConnectionPolicy.Get();
}

const FFlowPreloadPolicy& UFlowAsset::GetPreloadPolicy() const
{
// Runtime instances delegate to their template, which holds the serialized policy.
if (!PreloadPolicy.IsValid() && IsValid(TemplateAsset))
{
return TemplateAsset->GetPreloadPolicy();
}
}

// Graceful fallback: if PreloadPolicy was never initialized (asset predates this feature,
// or was never opened in editor), read directly from project settings at runtime.
if (!PreloadPolicy.IsValid())
{
const FFlowPreloadPolicy* SettingsPolicy = GetDefault<UFlowSettings>()->GetPreloadPolicy();
ensureAlways(SettingsPolicy);
if (SettingsPolicy)
{
return *SettingsPolicy;
}
}

check(PreloadPolicy.IsValid());
return PreloadPolicy.Get();
}

#if WITH_EDITOR

void UFlowAsset::InitializePinConnectionPolicy()
Expand All @@ -1475,6 +1510,15 @@ void UFlowAsset::InitializePinConnectionPolicy()
}
}

void UFlowAsset::InitializePreloadPolicy()
{
const FInstancedStruct& SourceStruct = GetDefault<UFlowSettings>()->PreloadPolicy;
if (ensure(SourceStruct.IsValid()))
{
PreloadPolicy.InitializeAsScriptStruct(SourceStruct.GetScriptStruct(), SourceStruct.GetMemory());
}
}

void UFlowAsset::LogError(const FString& MessageToLog, const UFlowNodeBase* Node) const
{
LogRuntimeMessage(EMessageSeverity::Error, MessageToLog, Node);
Expand Down
11 changes: 11 additions & 0 deletions Source/Flow/Private/FlowSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

#include "FlowSettings.h"
#include "FlowComponent.h"
#include "Policies/FlowPreloadPolicy.h"
#include "Policies/FlowStandardPinConnectionPolicies.h"
#include "Policies/FlowStandardPreloadPolicies.h"
#include "Policies/FlowStandardPinConnectionPolicies.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowSettings)

UFlowSettings::UFlowSettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, PinConnectionPolicy(FFlowPinConnectionPolicy_VeryRelaxed::StaticStruct())
, PreloadPolicy(FFlowPreloadPolicy_Standard::StaticStruct())
, bDeferTriggeredOutputsWhileTriggering(true)
, bLogOnSignalDisabled(true)
, bLogOnSignalPassthrough(true)
Expand All @@ -24,7 +28,13 @@ const FFlowPinConnectionPolicy* UFlowSettings::GetPinConnectionPolicy() const
return PinConnectionPolicy.GetPtr<FFlowPinConnectionPolicy>();
}

const FFlowPreloadPolicy* UFlowSettings::GetPreloadPolicy() const
{
return PreloadPolicy.GetPtr<FFlowPreloadPolicy>();
}

#if WITH_EDITOR

void UFlowSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
Expand All @@ -34,6 +44,7 @@ void UFlowSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange
(void)OnAdaptiveNodeTitlesChanged.ExecuteIfBound();
}
}

#endif

UClass* UFlowSettings::GetDefaultExpectedOwnerClass() const
Expand Down
5 changes: 0 additions & 5 deletions Source/Flow/Private/FlowSubsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,6 @@ UFlowAsset* UFlowSubsystem::CreateSubFlow(UFlowNode_SubGraph* SubGraphNode, cons
if (NewInstance)
{
InstancedSubFlows.Add(SubGraphNode, NewInstance);

if (bPreloading)
{
NewInstance->PreloadNodes();
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions Source/Flow/Private/Interfaces/FlowPreloadableInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright https://github.com/MothCocoon/FlowGraph/graphs/contributors

#include "Interfaces/FlowPreloadableInterface.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(FlowPreloadableInterface)

bool IFlowPreloadableInterface::ImplementsInterfaceSafe(const UObject* Object)
{
return IsValid(Object) && Object->GetClass()->ImplementsInterface(UFlowPreloadableInterface::StaticClass());
}
44 changes: 27 additions & 17 deletions Source/Flow/Private/Nodes/Actor/FlowNode_ExecuteComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Nodes/Actor/FlowNode_ExecuteComponent.h"
#include "Interfaces/FlowCoreExecutableInterface.h"
#include "Interfaces/FlowPreloadableInterface.h"
#include "Interfaces/FlowExternalExecutableInterface.h"
#include "Interfaces/FlowContextPinSupplierInterface.h"
#include "FlowAsset.h"
Expand Down Expand Up @@ -74,38 +75,40 @@ void UFlowNode_ExecuteComponent::DeinitializeInstance()
Super::DeinitializeInstance();
}

void UFlowNode_ExecuteComponent::PreloadContent()
EFlowPreloadResult UFlowNode_ExecuteComponent::PreloadContent()
{
Super::PreloadContent();

if (UActorComponent* ResolvedComp = TryResolveComponent())
{
if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast<IFlowCoreExecutableInterface>(ResolvedComp))
{
ComponentAsCoreExecutable->PreloadContent();
}
else if (ResolvedComp->Implements<UFlowCoreExecutableInterface>())
if (IFlowPreloadableInterface* PreloadableComponent = Cast<IFlowPreloadableInterface>(ResolvedComp))
{
IFlowCoreExecutableInterface::Execute_K2_PreloadContent(ResolvedComp);
FLOW_ASSERT_ENUM_MAX(EFlowPreloadResult, 2);

const EFlowPreloadResult PreloadableComponentResult = PreloadableComponent->PreloadContent();

// TODO (gtaylor) Consider adding a mechanism for components to do an async preload.
// Components have no back-reference to this node and cannot call NotifyPreloadComplete().
// Async (PreloadInProgress) component preloads are therefore unsupported (For Now(tm)):
// if a component returns PreloadInProgress the PendingPreloadCount would never reach zero.
ensureAlwaysMsgf(PreloadableComponentResult == EFlowPreloadResult::Completed,
TEXT("Component '%s' returned PreloadInProgress from PreloadContent(), but UFlowNode_ExecuteComponent has no mechanism to receive the async completion callback. Treating as Completed."),
*ResolvedComp->GetName());

return EFlowPreloadResult::Completed;
}
}

return EFlowPreloadResult::Completed;
}

void UFlowNode_ExecuteComponent::FlushContent()
{
if (UActorComponent* ResolvedComp = TryResolveComponent())
{
if (IFlowCoreExecutableInterface* ComponentAsCoreExecutable = Cast<IFlowCoreExecutableInterface>(ResolvedComp))
{
ComponentAsCoreExecutable->FlushContent();
}
else if (ResolvedComp->Implements<UFlowCoreExecutableInterface>())
if (IFlowPreloadableInterface* Preloadable = Cast<IFlowPreloadableInterface>(ResolvedComp))
{
IFlowCoreExecutableInterface::Execute_K2_FlushContent(ResolvedComp);
Preloadable->FlushContent();
}
}

Super::FlushContent();
}

void UFlowNode_ExecuteComponent::OnActivate()
Expand Down Expand Up @@ -180,6 +183,13 @@ void UFlowNode_ExecuteComponent::ForceFinishNode()

void UFlowNode_ExecuteComponent::ExecuteInput(const FName& PinName)
{
// Since this node implements IFlowPreloadableInterface,
// we need to call this to allow the PreloadHelper to intercept preload-specific PinNames
if (DispatchExecuteInputToPreloadHelper(PinName))
{
return;
}

Super::ExecuteInput(PinName);

if (UActorComponent* ResolvedComp = TryResolveComponent())
Expand Down
37 changes: 32 additions & 5 deletions Source/Flow/Private/Nodes/Actor/FlowNode_PlayLevelSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,24 +100,44 @@ void UFlowNode_PlayLevelSequence::PostEditChangeProperty(FPropertyChangedEvent&
}
#endif

void UFlowNode_PlayLevelSequence::PreloadContent()
EFlowPreloadResult UFlowNode_PlayLevelSequence::PreloadContent()
{
#if ENABLE_VISUAL_LOG
UE_VLOG(this, LogFlow, Log, TEXT("Preloading"));
UE_VLOG(this, LogFlow, Log, TEXT("Preloading Content"));
#endif

if (!Sequence.IsNull())
FLOW_ASSERT_ENUM_MAX(EFlowPreloadResult, 2);

if (Sequence.IsNull())
{
StreamableManager.RequestAsyncLoad({Sequence.ToSoftObjectPath()}, FStreamableDelegate());
return EFlowPreloadResult::Completed;
}

// Bind a weak delegate so NotifyPreloadComplete() is called when streaming finishes.
// If the asset is already cached, RequestAsyncLoad fires the delegate synchronously
// (safe — PendingPreloadCount is already set by TriggerPreload before this call).
PreloadHandle = StreamableManager.RequestAsyncLoad(
Sequence.ToSoftObjectPath(),
FStreamableDelegate::CreateWeakLambda(this, [this]()
{
NotifyPreloadComplete();
}));

return EFlowPreloadResult::PreloadInProgress;
}

void UFlowNode_PlayLevelSequence::FlushContent()
{
#if ENABLE_VISUAL_LOG
UE_VLOG(this, LogFlow, Log, TEXT("Flushing preload"));
UE_VLOG(this, LogFlow, Log, TEXT("Flushing Preloaded Content"));
#endif

if (PreloadHandle.IsValid())
{
PreloadHandle->CancelHandle();
PreloadHandle.Reset();
}

if (!Sequence.IsNull())
{
StreamableManager.Unload(Sequence.ToSoftObjectPath());
Expand Down Expand Up @@ -166,6 +186,13 @@ void UFlowNode_PlayLevelSequence::CreatePlayer()

void UFlowNode_PlayLevelSequence::ExecuteInput(const FName& PinName)
{
// Since this node implements IFlowPreloadableInterface,
// we need to call this to allow the PreloadHelper to intercept preload-specific PinNames
if (DispatchExecuteInputToPreloadHelper(PinName))
{
return;
}

if (PinName == TEXT("Start"))
{
LoadedSequence = Sequence.LoadSynchronous();
Expand Down
Loading