October3d55/M/MultiThread/Source/MultiThreadLibrary/Public/ThreadBase.h

197 lines
4.8 KiB
C++

// 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<void()> 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<class UThreadBase> 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<UThreadBase>(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;
}
};