// Copyright UnexGames 2025. All Rights Reserved. #pragma once #include "ThreadBase.h" #include "ThreadTasks.h" #include "TimerManager.h" #include "ThreadTasksLoop.generated.h" DECLARE_MULTICAST_DELEGATE(FLoopTaskDelegate); UCLASS(Blueprintable, hidecategories = (Object), meta = (DontUseGenericSpawnObject = "True")) class MULTITHREADLIBRARY_API UThreadTasksLoop : public UThreadTaskBase { friend class FThreadTasksLoopAction; GENERATED_BODY() public: UThreadTasksLoop(); ~UThreadTasksLoop(); virtual bool Start() override; /** * Called on Background Thread when the Task is executed. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, meta = (DisplayName = "Task Body"), Category = "MultiThreadLibrary") void TaskBody(UThreadTasksLoop* WorkerRef); /** * Check whether the work is in progress. */ virtual bool IsRunning() override; virtual void TaskBody_Implementation(UThreadTasksLoop* WorkerRef); virtual void Cancel_Implementation(); public: int BaseLoopNR; float BaseLoopInterval; int RepeatTime = 0; bool CanLoop = false; bool bCancel = false; FLoopTaskDelegate TaskDelegate; protected: protected: int32 CurrentIndex = 0; bool bStarted = false; FTimerHandle TickHandle; }; class MULTITHREADLIBRARY_API FThreadTasksLoopAction : public FTaskActionBase { EtaskExecutionBranches& Branches; bool bStarted; public: FThreadTasksLoopAction(UObject* InObject, EtaskExecutionBranches& InBranches, const FLatentActionInfo& LatentInfo, TSubclassOf TaskClass, int BaseLoopNR, float LoopInterval) : FTaskActionBase(InObject, LatentInfo, TaskClass) , Branches(InBranches) , bStarted(false) { UThreadTasksLoop* LocalTask = Cast(Task); if (LocalTask) { Branches = EtaskExecutionBranches::OnStart; LocalTask->BodyFunction(); LocalTask->BaseLoopInterval = LoopInterval; LocalTask->BaseLoopNR = BaseLoopNR; 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 ~FThreadTasksLoopAction() { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { UThreadTasksLoop* 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); } } };