From 7073addb1f66f4a1597fb04e6fe90c736335023b Mon Sep 17 00:00:00 2001 From: Damian Nowakowski Date: Tue, 28 May 2024 11:08:45 +0200 Subject: [PATCH 1/5] testing bp validity fix --- .../Private/BP/Actions/ECFTimelineBP.cpp | 37 ++++++++++++------- .../Private/BP/ECFActionBP.cpp | 7 +++- .../EnhancedCodeFlow/Private/BP/ECFActionBP.h | 7 +++- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp index 8a5ad11..1395222 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp @@ -8,21 +8,30 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFTimelineBP* UECFTimelineBP::ECFTimeline(const UObject* WorldContextObject, float StartValue, float StopValue, float Time, FECFActionSettings Settings, FECFHandleBP& Handle, EECFBlendFunc BlendFunc /*= EECFBlendFunc::ECFBlend_Linear*/, float BlendExp /*= 1.f*/) { UECFTimelineBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - Proxy->Proxy_Handle = FFlow::AddTimeline(WorldContextObject, - StartValue, StopValue, Time, - [Proxy](float Value, float Time) - { - Proxy->OnTick.Broadcast(Value, Time, false); - }, - [Proxy](float Value, float Time, bool bStopped) - { - Proxy->OnFinished.Broadcast(Value, Time, false); - Proxy->ClearAsyncBPAction(); - }, - BlendFunc, BlendExp, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_Handle = FFlow::AddTimeline(WorldContextObject, + StartValue, StopValue, Time, + [Proxy](float Value, float Time) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnTick.Broadcast(Value, Time, false); + } + }, + [Proxy](float Value, float Time, bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnFinished.Broadcast(Value, Time, false); + Proxy->ClearAsyncBPAction(); + } + }, + BlendFunc, BlendExp, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp index e1fa8ae..8e4f7b3 100644 --- a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp @@ -47,11 +47,16 @@ void UECFActionBP::Activate() void UECFActionBP::ClearAsyncBPAction() { SetReadyToDestroy(); -#if ((ENGINE_MAJOR_VERSION == 5) && (ENGINE_MINOR_VERSION >= 0)) +#if (ENGINE_MAJOR_VERSION == 5) MarkAsGarbage(); #else MarkPendingKill(); #endif } +bool UECFActionBP::IsProxyValid(const UObject* ProxyObject) +{ + return (IsValid(ProxyObject) && (ProxyObject->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed) == false)); +} + ECF_PRAGMA_ENABLE_OPTIMIZATION \ No newline at end of file diff --git a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.h b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.h index 0faabfe..2d7ea9d 100644 --- a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.h +++ b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.h @@ -40,7 +40,7 @@ class ENHANCEDCODEFLOW_API UECFActionBP : public UBlueprintAsyncActionBase // The World Context Object that started this action. UPROPERTY(Transient) - const UObject* Proxy_WorldContextObject; + TObjectPtr Proxy_WorldContextObject; // Handle of the Action to control. FECFHandle Proxy_Handle; @@ -51,6 +51,9 @@ class ENHANCEDCODEFLOW_API UECFActionBP : public UBlueprintAsyncActionBase // Just a handy flag to check if the action has been activated already. bool bActivated = false; - // Mark this async node as ready to destroy + // Mark this async node as ready to destroy. void ClearAsyncBPAction(); + + // Checks if the given proxy object is still valid and safe to use. + static bool IsProxyValid(const UObject* ProxyObject); }; From 4b000ced95fbc27433ef20a5ffa145a8279173b9 Mon Sep 17 00:00:00 2001 From: Damian Nowakowski Date: Tue, 28 May 2024 15:34:14 +0200 Subject: [PATCH 2/5] Applying proxy validity check to every bp node. Added check for a game thread run. --- .../BP/Actions/ECFCustomTimelineBP.cpp | 36 ++++++++----- .../Private/BP/Actions/ECFDelayBP.cpp | 19 ++++--- .../Private/BP/Actions/ECFDelayTicksBP.cpp | 19 ++++--- .../BP/Actions/ECFDoNoMoreThanXTimeBP.cpp | 42 ++++++++------- .../Private/BP/Actions/ECFRunAsyncThenBP.cpp | 35 +++++++----- .../Private/BP/Actions/ECFTickerBP.cpp | 36 ++++++++----- .../Private/BP/Actions/ECFTimelineBP.cpp | 1 - .../BP/Actions/ECFWaitAndExecuteBP.cpp | 42 +++++++++------ .../BP/Actions/ECFWhileTrueExecuteBP.cpp | 53 +++++++++++-------- Source/EnhancedCodeFlow/Public/ECFSubsystem.h | 7 +++ 10 files changed, 178 insertions(+), 112 deletions(-) diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFCustomTimelineBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFCustomTimelineBP.cpp index 45017ed..b58de04 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFCustomTimelineBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFCustomTimelineBP.cpp @@ -8,20 +8,28 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFCustomTimelineBP* UECFCustomTimelineBP::ECFCustomTimeline(const UObject* WorldContextObject, UCurveFloat* CurveFloat, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFCustomTimelineBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_Handle = FFlow::AddCustomTimeline(WorldContextObject, CurveFloat, - [Proxy](float Value, float Time) - { - Proxy->OnTick.Broadcast(Value, Time, false); - }, - [Proxy](float Value, float Time, bool bStopped) - { - Proxy->OnFinished.Broadcast(Value, Time, bStopped); - Proxy->ClearAsyncBPAction(); - }, - Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_Handle = FFlow::AddCustomTimeline(WorldContextObject, CurveFloat, + [Proxy](float Value, float Time) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnTick.Broadcast(Value, Time, false); + } + }, + [Proxy](float Value, float Time, bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnFinished.Broadcast(Value, Time, bStopped); + Proxy->ClearAsyncBPAction(); + } + }, + Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayBP.cpp index 5d3fa0c..95738f3 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayBP.cpp @@ -8,14 +8,19 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFDelayBP* UECFDelayBP::ECFDelay(const UObject* WorldContextObject, float DelayTime, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFDelayBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_Handle = FFlow::Delay(WorldContextObject, DelayTime, [Proxy](bool bStopped) + if (Proxy) { - Proxy->OnComplete.Broadcast(bStopped); - Proxy->ClearAsyncBPAction(); - }, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_Handle = FFlow::Delay(WorldContextObject, DelayTime, [Proxy](bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnComplete.Broadcast(bStopped); + Proxy->ClearAsyncBPAction(); + } + }, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayTicksBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayTicksBP.cpp index 7882490..783a765 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayTicksBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDelayTicksBP.cpp @@ -8,14 +8,19 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFDelayTicksBP* UECFDelayTicksBP::ECFDelayTicks(const UObject* WorldContextObject, int32 DelayTicks, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFDelayTicksBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_Handle = FFlow::DelayTicks(WorldContextObject, DelayTicks, [Proxy](bool bStopped) + if (Proxy) { - Proxy->OnComplete.Broadcast(bStopped); - Proxy->ClearAsyncBPAction(); - }, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_Handle = FFlow::DelayTicks(WorldContextObject, DelayTicks, [Proxy](bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnComplete.Broadcast(bStopped); + Proxy->ClearAsyncBPAction(); + } + }, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDoNoMoreThanXTimeBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDoNoMoreThanXTimeBP.cpp index d779b4c..1c68253 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDoNoMoreThanXTimeBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFDoNoMoreThanXTimeBP.cpp @@ -9,29 +9,35 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFDoNoMoreThanXTimeBP* UECFDoNoMoreThanXTimeBP::ECFDoNoMoreThanXTime(const UObject* WorldContextObject, float Time, FECFHandleBP& Handle, FECFInstanceIdBP& InstanceId, FECFActionSettings Settings, int32 MaxExecsEnqueued /*= 1*/) { UECFDoNoMoreThanXTimeBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - if (InstanceId.InstanceId.IsValid() == false) + if (Proxy) { - UECFBPLibrary::ECFGetNewInstanceId(InstanceId); - } + Proxy->Init(WorldContextObject, Settings); - Proxy->Proxy_Handle = FFlow::DoNoMoreThanXTime(WorldContextObject, [Proxy]() - { - // Because the action will be executed on first call, check if the async action has been activated. - // Not activated actions don't have bindings to delegates! - // Enqueue the OnExecute broadcast for the activation. - if (Proxy->bActivated) + if (InstanceId.InstanceId.IsValid() == false) { - Proxy->OnExecute.Broadcast(); - Proxy->ClearAsyncBPAction(); + UECFBPLibrary::ECFGetNewInstanceId(InstanceId); } - else + + Proxy->Proxy_Handle = FFlow::DoNoMoreThanXTime(WorldContextObject, [Proxy]() { - Proxy->bExecuteOnActivation = true; - } - }, Time, MaxExecsEnqueued, InstanceId.InstanceId, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + // Because the action will be executed on first call, check if the async action has been activated. + // Not activated actions don't have bindings to delegates! + // Enqueue the OnExecute broadcast for the activation. + if (IsProxyValid(Proxy)) + { + if (Proxy->bActivated) + { + Proxy->OnExecute.Broadcast(); + Proxy->ClearAsyncBPAction(); + } + else + { + Proxy->bExecuteOnActivation = true; + } + } + }, Time, MaxExecsEnqueued, InstanceId.InstanceId, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp index 0aa346c..19ed14a 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp @@ -8,20 +8,29 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFRunAsyncThenBP* UECFRunAsyncThenBP::ECFRunAsyncThen(const UObject* WorldContextObject, float InTimeOut, EECFAsyncPrio Priority, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFRunAsyncThenBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); - Proxy->Proxy_Handle = FFlow::RunAsyncThen(WorldContextObject, - [Proxy]() - { - Proxy->AsyncTask.Broadcast(false, false); - }, - [Proxy](bool bTimedOut, bool bStopped) - { - Proxy->OnExecute.Broadcast(bTimedOut, bStopped); - Proxy->ClearAsyncBPAction(); - }, - InTimeOut, Priority, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + Proxy->Proxy_Handle = FFlow::RunAsyncThen(WorldContextObject, + [Proxy]() + { + if (IsProxyValid(Proxy)) + { + Proxy->AsyncTask.Broadcast(false, false); + } + }, + [Proxy](bool bTimedOut, bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnExecute.Broadcast(bTimedOut, bStopped); + Proxy->ClearAsyncBPAction(); + } + }, + InTimeOut, Priority, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTickerBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTickerBP.cpp index eb3e6e2..02c7d47 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTickerBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTickerBP.cpp @@ -8,20 +8,28 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFTickerBP* UECFTickerBP::ECFTicker(const UObject* WorldContextObject, float TickingTime, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFTickerBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_Handle = FFlow::AddTicker(WorldContextObject, TickingTime, - [Proxy](float DeltaTime) - { - Proxy->OnTick.Broadcast(DeltaTime, false); - }, - [Proxy](bool bStopped) - { - Proxy->OnComplete.Broadcast(0.f, bStopped); - Proxy->ClearAsyncBPAction(); - }, - Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_Handle = FFlow::AddTicker(WorldContextObject, TickingTime, + [Proxy](float DeltaTime) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnTick.Broadcast(DeltaTime, false); + } + }, + [Proxy](bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnComplete.Broadcast(0.f, bStopped); + Proxy->ClearAsyncBPAction(); + } + }, + Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp index 1395222..ebec3d1 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFTimelineBP.cpp @@ -8,7 +8,6 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFTimelineBP* UECFTimelineBP::ECFTimeline(const UObject* WorldContextObject, float StartValue, float StopValue, float Time, FECFActionSettings Settings, FECFHandleBP& Handle, EECFBlendFunc BlendFunc /*= EECFBlendFunc::ECFBlend_Linear*/, float BlendExp /*= 1.f*/) { UECFTimelineBP* Proxy = NewObject(); - if (Proxy) { Proxy->Init(WorldContextObject, Settings); diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWaitAndExecuteBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWaitAndExecuteBP.cpp index 933ed35..0bde38c 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWaitAndExecuteBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWaitAndExecuteBP.cpp @@ -8,23 +8,31 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFWaitAndExecuteBP* UECFWaitAndExecuteBP::ECFWaitAndExecute(const UObject* WorldContextObject, float InTimeOut, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFWaitAndExecuteBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_HasFinished = false; - - Proxy->Proxy_Handle = FFlow::WaitAndExecute(WorldContextObject, - [Proxy](float DeltaTime) - { - Proxy->OnWait.Broadcast(Proxy, DeltaTime, false, false); - return Proxy->Proxy_HasFinished; - }, - [Proxy](bool bTimedOut, bool bStopped) - { - Proxy->OnExecute.Broadcast(Proxy, 0.f, bTimedOut, bStopped); - Proxy->ClearAsyncBPAction(); - }, - InTimeOut, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_HasFinished = false; + Proxy->Proxy_Handle = FFlow::WaitAndExecute(WorldContextObject, + [Proxy](float DeltaTime) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnWait.Broadcast(Proxy, DeltaTime, false, false); + return Proxy->Proxy_HasFinished; + } + return true; + }, + [Proxy](bool bTimedOut, bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnExecute.Broadcast(Proxy, 0.f, bTimedOut, bStopped); + Proxy->ClearAsyncBPAction(); + } + }, + InTimeOut, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWhileTrueExecuteBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWhileTrueExecuteBP.cpp index 9c18d5f..00457a5 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWhileTrueExecuteBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFWhileTrueExecuteBP.cpp @@ -8,27 +8,38 @@ ECF_PRAGMA_DISABLE_OPTIMIZATION UECFWhileTrueExecuteBP* UECFWhileTrueExecuteBP::ECFWhileTrueExecute(const UObject* WorldContextObject, float TimeOut, FECFActionSettings Settings, FECFHandleBP& Handle) { UECFWhileTrueExecuteBP* Proxy = NewObject(); - Proxy->Init(WorldContextObject, Settings); - - Proxy->Proxy_IsTrue = true; - - Proxy->Proxy_Handle = FFlow::WhileTrueExecute(WorldContextObject, - [Proxy]() - { - Proxy->OnWhile.Broadcast(Proxy, 0.f, false, false); - return Proxy->Proxy_IsTrue; - }, - [Proxy](float DeltaTime) - { - Proxy->OnExecute.Broadcast(Proxy, DeltaTime, false, false); - }, - [Proxy](bool bTimedOut, bool bStopped) - { - Proxy->OnComplete.Broadcast(Proxy, 0.f, bTimedOut, bStopped); - Proxy->ClearAsyncBPAction(); - }, - TimeOut, Settings); - Handle = FECFHandleBP(Proxy->Proxy_Handle); + if (Proxy) + { + Proxy->Init(WorldContextObject, Settings); + Proxy->Proxy_IsTrue = true; + Proxy->Proxy_Handle = FFlow::WhileTrueExecute(WorldContextObject, + [Proxy]() + { + if (IsProxyValid(Proxy)) + { + Proxy->OnWhile.Broadcast(Proxy, 0.f, false, false); + return Proxy->Proxy_IsTrue; + } + return false; + }, + [Proxy](float DeltaTime) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnExecute.Broadcast(Proxy, DeltaTime, false, false); + } + }, + [Proxy](bool bTimedOut, bool bStopped) + { + if (IsProxyValid(Proxy)) + { + Proxy->OnComplete.Broadcast(Proxy, 0.f, bTimedOut, bStopped); + Proxy->ClearAsyncBPAction(); + } + }, + TimeOut, Settings); + Handle = FECFHandleBP(Proxy->Proxy_Handle); + } return Proxy; } diff --git a/Source/EnhancedCodeFlow/Public/ECFSubsystem.h b/Source/EnhancedCodeFlow/Public/ECFSubsystem.h index ae562b7..24c530e 100644 --- a/Source/EnhancedCodeFlow/Public/ECFSubsystem.h +++ b/Source/EnhancedCodeFlow/Public/ECFSubsystem.h @@ -39,6 +39,9 @@ class ENHANCEDCODEFLOW_API UECFSubsystem : public UGameInstanceSubsystem, public template FECFHandle AddAction(const UObject* InOwner, const FECFActionSettings& Settings, const FECFInstanceId& InstanceId, Ts&& ... Args) { + // Ensure the Action has been started from the Game Thread. + checkf(IsInGameThread(), TEXT("ECF Actions must be started from the Game Thread!")); + // There can be only one instanced action running at the same time. When trying to add an // action with existing instance id - return the currently running action's handle. UECFActionBase* PossibleInstancedAction = GetInstancedAction(InstanceId); @@ -67,6 +70,10 @@ class ENHANCEDCODEFLOW_API UECFSubsystem : public UGameInstanceSubsystem, public template void AddCoroutineAction(const UObject* InOwner, FECFCoroutineHandle InCoroutineHandle, const FECFActionSettings& Settings, Ts&& ... Args) { + // Ensure the Action has been started from the Game Thread. + checkf(IsInGameThread(), TEXT("ECF Coroutines must be started from the Game Thread!")); + + // Create and set new coroutine action. T* NewAction = NewObject(this); NewAction->SetCoroutineAction(InOwner, InCoroutineHandle, ++LastHandleId, Settings); if (NewAction->Setup(Forward(Args)...)) From d6b34243f3a16f5268b090629472d40f668ee559 Mon Sep 17 00:00:00 2001 From: Damian Nowakowski Date: Tue, 28 May 2024 15:39:37 +0200 Subject: [PATCH 3/5] refactor and ensuremsg when checking proxy validity --- .../EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp | 1 - Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp index 19ed14a..a3f16e2 100644 --- a/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/Actions/ECFRunAsyncThenBP.cpp @@ -11,7 +11,6 @@ UECFRunAsyncThenBP* UECFRunAsyncThenBP::ECFRunAsyncThen(const UObject* WorldCont if (Proxy) { Proxy->Init(WorldContextObject, Settings); - Proxy->Proxy_Handle = FFlow::RunAsyncThen(WorldContextObject, [Proxy]() { diff --git a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp index 8e4f7b3..1e9e7e3 100644 --- a/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp +++ b/Source/EnhancedCodeFlow/Private/BP/ECFActionBP.cpp @@ -56,7 +56,9 @@ void UECFActionBP::ClearAsyncBPAction() bool UECFActionBP::IsProxyValid(const UObject* ProxyObject) { - return (IsValid(ProxyObject) && (ProxyObject->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed) == false)); + const bool bIsValid = (IsValid(ProxyObject) && (ProxyObject->HasAnyFlags(RF_BeginDestroyed | RF_FinishDestroyed) == false)); + ensureAlwaysMsgf(bIsValid, TEXT("Invalid ProxyObject!")); + return bIsValid; } ECF_PRAGMA_ENABLE_OPTIMIZATION \ No newline at end of file From f40fad5c4b0ca3c6f20456d3486547e7fb18adab Mon Sep 17 00:00:00 2001 From: Damian Nowakowski Date: Tue, 28 May 2024 15:45:59 +0200 Subject: [PATCH 4/5] Ensure the RunAsyncThen object is still valid after exiting other thread --- .../CodeFlowActions/Coroutines/ECFRunAsyncAndWait.h | 11 ++++++++--- .../Public/CodeFlowActions/ECFRunAsyncThen.h | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/EnhancedCodeFlow/Public/CodeFlowActions/Coroutines/ECFRunAsyncAndWait.h b/Source/EnhancedCodeFlow/Public/CodeFlowActions/Coroutines/ECFRunAsyncAndWait.h index 9d793d4..1e841aa 100644 --- a/Source/EnhancedCodeFlow/Public/CodeFlowActions/Coroutines/ECFRunAsyncAndWait.h +++ b/Source/EnhancedCodeFlow/Public/CodeFlowActions/Coroutines/ECFRunAsyncAndWait.h @@ -64,10 +64,15 @@ class ENHANCEDCODEFLOW_API UECFRunAsyncAndWait: public UECFCoroutineActionBase } bIsAsyncTaskDone = false; - AsyncTask(ThreadType, [this]() + + TWeakObjectPtr WeakThis(this); + AsyncTask(ThreadType, [WeakThis]() { - AsyncTaskFunc(); - bIsAsyncTaskDone = true; + if (ThisClass* StrongThis = WeakThis.Get()) + { + StrongThis->AsyncTaskFunc(); + StrongThis->bIsAsyncTaskDone = true; + } }); return true; diff --git a/Source/EnhancedCodeFlow/Public/CodeFlowActions/ECFRunAsyncThen.h b/Source/EnhancedCodeFlow/Public/CodeFlowActions/ECFRunAsyncThen.h index 3f6daaf..b9d943e 100644 --- a/Source/EnhancedCodeFlow/Public/CodeFlowActions/ECFRunAsyncThen.h +++ b/Source/EnhancedCodeFlow/Public/CodeFlowActions/ECFRunAsyncThen.h @@ -67,10 +67,15 @@ class ENHANCEDCODEFLOW_API UECFRunAsyncThen : public UECFActionBase } bIsAsyncTaskDone = false; - AsyncTask(ThreadType, [this]() + + TWeakObjectPtr WeakThis(this); + AsyncTask(ThreadType, [WeakThis]() { - AsyncTaskFunc(); - bIsAsyncTaskDone = true; + if (ThisClass* StrongThis = WeakThis.Get()) + { + StrongThis->AsyncTaskFunc(); + StrongThis->bIsAsyncTaskDone = true; + } }); return true; From bc031ee5cdbda2d72fccb762a5ad07785f31612e Mon Sep 17 00:00:00 2001 From: Damian Nowakowski Date: Tue, 28 May 2024 15:51:06 +0200 Subject: [PATCH 5/5] gamethread check stops action creations. changelog updated --- Changelog.txt | 5 +++++ Source/EnhancedCodeFlow/Public/ECFSubsystem.h | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 123dfca..26abd22 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,8 @@ +###### 3.3.3 +* More strict checks for proxy validity in all Async Blueprint nodes. +* Prevent from starting any action from a non game thread. +* Ensure the Run Async And Wait object is still valid after exiting another thread. + ###### 3.3.2 * Further fixes to prepare the plugin to be compiled for a marketplace. Tests against UE5.4. diff --git a/Source/EnhancedCodeFlow/Public/ECFSubsystem.h b/Source/EnhancedCodeFlow/Public/ECFSubsystem.h index 24c530e..a3d9173 100644 --- a/Source/EnhancedCodeFlow/Public/ECFSubsystem.h +++ b/Source/EnhancedCodeFlow/Public/ECFSubsystem.h @@ -40,7 +40,11 @@ class ENHANCEDCODEFLOW_API UECFSubsystem : public UGameInstanceSubsystem, public FECFHandle AddAction(const UObject* InOwner, const FECFActionSettings& Settings, const FECFInstanceId& InstanceId, Ts&& ... Args) { // Ensure the Action has been started from the Game Thread. - checkf(IsInGameThread(), TEXT("ECF Actions must be started from the Game Thread!")); + if (IsInGameThread() == false) + { + checkf(false, TEXT("ECF Actions must be started from the Game Thread!")); + return FECFHandle(); + } // There can be only one instanced action running at the same time. When trying to add an // action with existing instance id - return the currently running action's handle. @@ -71,7 +75,11 @@ class ENHANCEDCODEFLOW_API UECFSubsystem : public UGameInstanceSubsystem, public void AddCoroutineAction(const UObject* InOwner, FECFCoroutineHandle InCoroutineHandle, const FECFActionSettings& Settings, Ts&& ... Args) { // Ensure the Action has been started from the Game Thread. - checkf(IsInGameThread(), TEXT("ECF Coroutines must be started from the Game Thread!")); + if (IsInGameThread()) + { + checkf(false, TEXT("ECF Coroutines must be started from the Game Thread!")); + return; + } // Create and set new coroutine action. T* NewAction = NewObject(this);