// Copyright UnexGames 2025. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ThreadTaskBase.h" #include "HAL/ThreadManager.h" #include "ThreadTasks.generated.h" DECLARE_MULTICAST_DELEGATE(FThreadTaskDelegate); UCLASS(HideDropdown, Blueprintable, hidecategories = (Object), meta = (DontUseGenericSpawnObject = "true")) class MULTITHREADLIBRARY_API UThreadTasks : public UThreadTaskBase { GENERATED_BODY() public: virtual bool Start() override; /** * Called on Background Thread when the Task is executed. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, meta = (DisplayName = "Task Body"), Category = "MultiThreadLibrary") void TaskBody(); virtual void TaskBody_Implementation(); public: FThreadTaskDelegate TaskDelegate; protected: }; class MULTITHREADLIBRARY_API FThreadTaskOnly : public FTaskActionBase { ETaskResultBranches& Branches; bool bStarted; public: FThreadTaskOnly(UObject* InObject, ETaskResultBranches& InBranches, const FLatentActionInfo& LatentInfo, TSubclassOf TaskClass, UThreadBase*& OutTask) : FTaskActionBase(InObject, LatentInfo, TaskClass) , Branches(InBranches) , bStarted(false) { OutTask = Task; UThreadTasks* LocalTask = Cast(Task); if (LocalTask) { Branches = ETaskResultBranches::OnStart; LocalTask->BodyFunction(); bStarted = Task->Start(); } else { return; } } virtual void UpdateOperation(FLatentResponse& Response) override { if (bStarted) { if (!IsCanceled()) { if (!IsRunning()) { Branches = ETaskResultBranches::OnCompleted; Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); } } else { Branches = ETaskResultBranches::OnCanceled; Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); } } else { //If we reached this point it means the task was unable to start. Branches = ETaskResultBranches::OnCompleted; Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); } } }; class MULTITHREADLIBRARY_API FThreadTaskWithBody : public FTaskActionBase { EtaskExecutionBranches& Branches; bool bStarted; public: FThreadTaskWithBody(UObject* InObject, EtaskExecutionBranches& InBranches, const FLatentActionInfo& LatentInfo, TSubclassOf TaskClass, const ETaskExecutionType& InExecutionType, UThreadBase*& OutTask) : FTaskActionBase(InObject, LatentInfo, TaskClass) , Branches(InBranches) , bStarted(false) { OutTask = Task; UThreadTasks* LocalTask = Cast(Task); if (LocalTask) { Branches = EtaskExecutionBranches::OnStart; LocalTask->BodyFunction(); LocalTask->TaskDelegate.AddLambda([LocalTask, &InBranches] { InBranches = EtaskExecutionBranches::OnTaskBody; LocalTask->BodyFunction(); }); LocalTask->OnCancelDelegate.AddLambda([LocalTask, &InBranches] { InBranches = EtaskExecutionBranches::OnCanceled; LocalTask->BodyFunction(); }); bStarted = Task->Start(); } else { return; } } virtual ~FThreadTaskWithBody() { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { UThreadTasks* LocalTask = Cast(Task); if (LocalTask) { LocalTask->TaskDelegate.RemoveAll(this); LocalTask->OnCancelDelegate.RemoveAll(this); } } } virtual void UpdateOperation(FLatentResponse& Response) override { if (bStarted) { if (!IsCanceled()) { if (!IsRunning()) { Branches = EtaskExecutionBranches::OnCompleted; Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); } } else { Response.DoneIf(true); } } else { //If we reached this point it means the task was unable to start. Branches = EtaskExecutionBranches::OnCompleted; Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); } } };