// Copyright UnexGames 2025. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "UObject/UObjectBaseUtility.h" #include "LatentActions.h" #include "Engine/LatentActionManager.h" #include "Templates/SubclassOf.h" #include "UObject/Package.h" #include "ThreadUtility.h" #include "ThreadBase.generated.h" DECLARE_MULTICAST_DELEGATE(FTaskOnCancelDelegate); UCLASS(HideDropdown, BlueprintType, hidecategories = (Object), meta = (DontUseGenericSpawnObject = "true")) class MULTITHREADLIBRARY_API UThreadBase : public UObject { GENERATED_BODY() public: UThreadBase(); ~UThreadBase(); /** * Attempts to start the Task. * @return Return False if Task already running. */ virtual bool Start(); /** * Attempts to Cancel the Task. */ virtual void Cancel(); /** * Check whether the task is in progress. */ virtual bool IsRunning(); /** * Check whether the task is canceled. */ virtual bool IsCanceled(); /** * Called immediately on Game Thread when the Task is cancelled. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, meta = (DisplayName = "On Cancel"), Category = "MultiThreadLibrary") void OnCancel(); virtual void OnCancel_Implementation(); /** * Wait for work work to complete. */ virtual void WaitToFinish(); /** * Called immediately on Game Thread when the Task is completed. */ UFUNCTION(BlueprintNativeEvent, BlueprintCallable, meta = (DisplayName = "On Complete"), Category = "MultiThreadLibrary") void OnComplete(); virtual void OnComplete_Implementation(); public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiThreadLibrary") bool bIsTickable = false; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiThreadLibrary") bool bIsTickableInEditor = false; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiThreadLibrary") bool bIsTickableWhenPaused = false; FTaskOnCancelDelegate OnCancelDelegate; TFunction BodyFunction; protected: virtual UWorld* GetWorld() const override; private: protected: FThreadSafeBool bCanceled = false; }; UENUM(BlueprintType) enum class ETaskResultBranches : uint8 { OnStart, OnCompleted, OnCanceled, }; UENUM(BlueprintType) enum class EtaskExecutionBranches : uint8 { OnStart, OnTaskBody, OnCompleted, OnCanceled, }; UENUM(BlueprintType) enum class ETaskResultBranchesNoCancel : uint8 { OnStart, OnCompleted, }; UENUM(BlueprintType) enum class ETaskResultBranchesNoComplete : uint8 { OnStart, OnCanceled, }; UENUM(BlueprintType) enum class ETaskResultBranchesNoCompleteWithBody : uint8 { OnStart, OnTaskBody, OnCanceled, }; class MULTITHREADLIBRARY_API FTaskActionBase : public FPendingLatentAction { protected: UObject* Object; FName ExecutionFunction; int32 OutputLink; FWeakObjectPtr CallbackTarget; UThreadBase* Task = nullptr; public: FTaskActionBase(UObject* InObject, const FLatentActionInfo& LatentInfo, TSubclassOf TaskClass) : Object(InObject) , ExecutionFunction(LatentInfo.ExecutionFunction) , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) { UThreadUtility::TaskIndex++; const FString Name = "Task Work" + FString::FromInt(UThreadUtility::TaskIndex); Task = NewObject(InObject, TaskClass, FName(*Name), RF_Transient); if (UObject* CallbackObject = CallbackTarget.Get()) { if (UFunction* Function = CallbackObject->FindFunction(ExecutionFunction)) { checkf(CallbackObject && Function, TEXT("Can't find Task Functions.")); int32 LocalOutputLink = OutputLink; Task->BodyFunction = [CallbackObject, Function, LocalOutputLink]() { int32 FinalOutputLink = LocalOutputLink; if (CallbackObject && Function) { CallbackObject->ProcessEvent(Function, &(FinalOutputLink)); } }; } } } virtual ~FTaskActionBase() { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { Task->Cancel(); } } virtual bool IsRunning() { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { return Task->IsRunning(); } return false; } virtual bool IsCanceled() { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { return Task->IsCanceled(); } return true; } };