You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2717513 on 2015/10/06 by Robert.Manuszewski@Robert_Manuszewski_EGUK_M1
GC and WeakObjectPtr performance optimizations.
- Moved some of the EObjectFlags to EInternalObjectFlags and merged them with FUObjectArray
- Moved WeakObjectPtr serial numbersto FUObjectArray
- Added pre-allocated UObject array
Change 2716517 on 2015/10/05 by Robert.Manuszewski@Robert_Manuszewski_EGUK_M1
Make SavePackage thread safe UObject-wise so that StaticFindObject etc can't run in parallel when packages are being saved.
Change 2721142 on 2015/10/08 by Mikolaj.Sieluzycki@Dev-Core_D0920
UHT will now use makefiles to speed up iterative runs.
Change 2726320 on 2015/10/13 by Jaroslaw.Palczynski@jaroslaw.palczynski_D1732_2963
Hot-reload performance optimizations:
1. Got rid of redundant touched BPs optimization (which was necessary before major HR fixes submitted earlier).
2. Parallelized search for old CDOs referencers.
Change 2759032 on 2015/11/09 by Graeme.Thornton@GThornton_DesktopMaster
Dependency preloading improvements
- Asset registry dependencies now resolve asset redirectors
- Rearrange runtime loading to put dependency preloads within BeginLoad/EndLoad for the source package
Change 2754342 on 2015/11/04 by Robert.Manuszewski@Robert_Manuszewski_Stream1
Allow UnfocusedVolumeMultiplier to be set programmatically
Change 2764008 on 2015/11/12 by Robert.Manuszewski@Robert_Manuszewski_Stream1
When cooking, don't add imports that are outers of objects excluded from the current cook target.
Change 2755562 on 2015/11/05 by Steve.Robb@Dev-Core
Inline storage for TFunction.
Fix for delegate inline storage on Win64.
Some build fixes.
Visualizer fixes for new TFunction format.
Change 2735084 on 2015/10/20 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
CrashReporter Web - Search by Platform
Added initial support for streams (GetBranchesAsListItems, CopyToJira)
Change 2762387 on 2015/11/11 by Steve.Robb@Dev-Core
Unnecessary allocation removed when loading empty files in FFileHelper::LoadFileToString.
Change 2762632 on 2015/11/11 by Steve.Robb@Dev-Core
Some TSet function optimisations:
Avoiding unnecessary hashing of function arguments if the container is empty (rather than the hash being empty, which is not necessarily equivalent).
Taking local copies of HashSize during iterations.
Change 2762936 on 2015/11/11 by Steve.Robb@Dev-Core
BulkData zero byte allocations are now handled by an RAII object which owns the memory.
Change 2765758 on 2015/11/13 by Steve.Robb@Dev-Core
FName::operator== and != optimised to be a single comparison.
Change 2757195 on 2015/11/06 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
PR #1305: Improvements in CrashReporter for Symbol Server usage (Contributed by bozaro)
Change 2760778 on 2015/11/10 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
PR #1725: Fixed typos in ProfilerCommon.h; Added comments (Contributed by BGR360)
Also fixed starting condition.
Change 2739804 on 2015/10/23 by Robert.Manuszewski@Robert_Manuszewski_Stream1
PR #1470: [UObjectGlobals] Do not overwrite instanced subobjects with ones from CDO (Contributed by slonopotamus)
Change 2744733 on 2015/10/28 by Steve.Robb@Dev-Core
PR #1540 - Specifying a different Saved folder at launch through a command line parameter
Integrated and optimized.
#lockdown Nick.Penwarden
[CL 2772222 by Robert Manuszewski in Main branch]
638 lines
20 KiB
C++
638 lines
20 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "GameplayTasksPrivatePCH.h"
|
|
#include "GameplayTasksComponent.h"
|
|
#include "GameplayTask.h"
|
|
#include "MessageLog.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "GameplayTasksComponent"
|
|
|
|
namespace
|
|
{
|
|
FORCEINLINE const TCHAR* GetGameplayTaskEventName(EGameplayTaskEvent Event)
|
|
{
|
|
/*static const UEnum* GameplayTaskEventEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EGameplayTaskEvent"));
|
|
return GameplayTaskEventEnum->GetEnumText(static_cast<int32>(Event)).ToString();*/
|
|
|
|
return Event == EGameplayTaskEvent::Add ? TEXT("Add") : TEXT("Remove");
|
|
}
|
|
}
|
|
|
|
UGameplayTasksComponent::UGameplayTasksComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
PrimaryComponentTick.TickGroup = TG_DuringPhysics;
|
|
PrimaryComponentTick.bStartWithTickEnabled = false;
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
|
|
bReplicates = true;
|
|
TopActivePriority = 0;
|
|
}
|
|
|
|
void UGameplayTasksComponent::OnTaskActivated(UGameplayTask& Task)
|
|
{
|
|
if (Task.IsTickingTask())
|
|
{
|
|
check(TickingTasks.Contains(&Task) == false);
|
|
TickingTasks.Add(&Task);
|
|
|
|
// If this is our first ticking task, set this component as active so it begins ticking
|
|
if (TickingTasks.Num() == 1)
|
|
{
|
|
UpdateShouldTick();
|
|
}
|
|
}
|
|
if (Task.IsSimulatedTask())
|
|
{
|
|
check(SimulatedTasks.Contains(&Task) == false);
|
|
SimulatedTasks.Add(&Task);
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::OnTaskDeactivated(UGameplayTask& Task)
|
|
{
|
|
if (Task.IsTickingTask())
|
|
{
|
|
// If we are removing our last ticking task, set this component as inactive so it stops ticking
|
|
TickingTasks.RemoveSingleSwap(&Task);
|
|
}
|
|
|
|
if (Task.IsSimulatedTask())
|
|
{
|
|
SimulatedTasks.RemoveSingleSwap(&Task);
|
|
}
|
|
|
|
UpdateShouldTick();
|
|
|
|
// Resource-using task
|
|
if (Task.RequiresPriorityOrResourceManagement() == true && Task.GetState() == EGameplayTaskState::Finished)
|
|
{
|
|
OnTaskEnded(Task);
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::OnTaskEnded(UGameplayTask& Task)
|
|
{
|
|
ensure(Task.RequiresPriorityOrResourceManagement() == true);
|
|
RemoveResourceConsumingTask(Task);
|
|
}
|
|
|
|
void UGameplayTasksComponent::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
|
|
{
|
|
// Intentionally not calling super: We do not want to replicate bActive which controls ticking. We sometimes need to tick on client predictively.
|
|
DOREPLIFETIME_CONDITION(UGameplayTasksComponent, SimulatedTasks, COND_SkipOwner);
|
|
}
|
|
|
|
bool UGameplayTasksComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
|
|
{
|
|
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
|
|
|
|
if (!RepFlags->bNetOwner)
|
|
{
|
|
for (UGameplayTask* SimulatedTask : SimulatedTasks)
|
|
{
|
|
if (SimulatedTask && !SimulatedTask->IsPendingKill())
|
|
{
|
|
WroteSomething |= Channel->ReplicateSubobject(SimulatedTask, *Bunch, *RepFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
return WroteSomething;
|
|
}
|
|
|
|
void UGameplayTasksComponent::OnRep_SimulatedTasks()
|
|
{
|
|
for (UGameplayTask* SimulatedTask : SimulatedTasks)
|
|
{
|
|
// Temp check
|
|
if (SimulatedTask && SimulatedTask->IsTickingTask() && TickingTasks.Contains(SimulatedTask) == false)
|
|
{
|
|
SimulatedTask->InitSimulatedTask(*this);
|
|
if (TickingTasks.Num() == 0)
|
|
{
|
|
UpdateShouldTick();
|
|
}
|
|
|
|
TickingTasks.Add(SimulatedTask);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_TickGameplayTasks);
|
|
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
|
|
|
// Because we have no control over what a task may do when it ticks, we must be careful.
|
|
// Ticking a task may kill the task right here. It could also potentially kill another task
|
|
// which was waiting on the original task to do something. Since when a tasks is killed, it removes
|
|
// itself from the TickingTask list, we will make a copy of the tasks we want to service before ticking any
|
|
|
|
int32 NumTickingTasks = TickingTasks.Num();
|
|
int32 NumActuallyTicked = 0;
|
|
switch (NumTickingTasks)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
if (TickingTasks[0].IsValid())
|
|
{
|
|
TickingTasks[0]->TickTask(DeltaTime);
|
|
NumActuallyTicked++;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
TArray<TWeakObjectPtr<UGameplayTask> > LocalTickingTasks = TickingTasks;
|
|
for (TWeakObjectPtr<UGameplayTask>& TickingTask : LocalTickingTasks)
|
|
{
|
|
if (TickingTask.IsValid())
|
|
{
|
|
TickingTask->TickTask(DeltaTime);
|
|
NumActuallyTicked++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
};
|
|
|
|
// Stop ticking if no more active tasks
|
|
if (NumActuallyTicked == 0)
|
|
{
|
|
TickingTasks.SetNum(0, false);
|
|
UpdateShouldTick();
|
|
}
|
|
}
|
|
|
|
bool UGameplayTasksComponent::GetShouldTick() const
|
|
{
|
|
return TickingTasks.Num() > 0;
|
|
}
|
|
|
|
void UGameplayTasksComponent::RequestTicking()
|
|
{
|
|
if (bIsActive == false)
|
|
{
|
|
SetActive(true);
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::UpdateShouldTick()
|
|
{
|
|
const bool bShouldTick = GetShouldTick();
|
|
if (bIsActive != bShouldTick)
|
|
{
|
|
SetActive(bShouldTick);
|
|
}
|
|
}
|
|
|
|
AActor* UGameplayTasksComponent::GetAvatarActor(const UGameplayTask* Task) const
|
|
{
|
|
return GetOwner();
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// Priority and resources handling
|
|
//----------------------------------------------------------------------//
|
|
void UGameplayTasksComponent::AddTaskReadyForActivation(UGameplayTask& NewTask)
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Log, TEXT("AddTaskReadyForActivation %s"), *NewTask.GetName());
|
|
|
|
ensure(NewTask.RequiresPriorityOrResourceManagement() == true);
|
|
|
|
TaskEvents.Add(FGameplayTaskEventData(EGameplayTaskEvent::Add, NewTask));
|
|
// trigger the actual processing only if it was the first event added to the list
|
|
if (TaskEvents.Num() == 1)
|
|
{
|
|
ProcessTaskEvents();
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::RemoveResourceConsumingTask(UGameplayTask& Task)
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Log, TEXT("RemoveResourceConsumingTask %s"), *Task.GetName());
|
|
|
|
TaskEvents.Add(FGameplayTaskEventData(EGameplayTaskEvent::Remove, Task));
|
|
// trigger the actual processing only if it was the first event added to the list
|
|
if (TaskEvents.Num() == 1)
|
|
{
|
|
ProcessTaskEvents();
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::EndAllResourceConsumingTasksOwnedBy(const IGameplayTaskOwnerInterface& TaskOwner)// , bool bNotifyOwner)
|
|
{
|
|
for (int32 TaskIndex = TaskPriorityQueue.Num() - 1; TaskIndex >= 0; --TaskIndex)
|
|
{
|
|
if (TaskPriorityQueue[TaskIndex] == nullptr)
|
|
{
|
|
TaskPriorityQueue.RemoveAt(TaskIndex, 1, /*bAllowShrinking=*/false);
|
|
}
|
|
else if (TaskPriorityQueue[TaskIndex]->GetTaskOwner() == &TaskOwner)
|
|
{
|
|
UGameplayTask* Task = TaskPriorityQueue[TaskIndex];
|
|
TaskPriorityQueue.RemoveAt(TaskIndex, 1, /*bAllowShrinking=*/false);
|
|
Task->TaskOwnerEnded();
|
|
}
|
|
}
|
|
|
|
UpdateTaskActivationFromIndex(0, FGameplayResourceSet(), FGameplayResourceSet());
|
|
}
|
|
|
|
void UGameplayTasksComponent::ProcessTaskEvents()
|
|
{
|
|
static const int32 ErronousIterationsLimit = 100;
|
|
|
|
// note that this function allows called functions to add new elements to
|
|
// TaskEvents array that the main loop is iterating over. It's a feature
|
|
for (int32 EventIndex = 0; EventIndex < TaskEvents.Num(); ++EventIndex)
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Verbose, TEXT("UGameplayTasksComponent::ProcessTaskEvents: %s event %s")
|
|
, *TaskEvents[EventIndex].RelatedTask.GetName(), GetGameplayTaskEventName(TaskEvents[EventIndex].Event));
|
|
|
|
if (TaskEvents[EventIndex].RelatedTask.IsPendingKill())
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Verbose, TEXT("%s is PendingKill"), *TaskEvents[EventIndex].RelatedTask.GetName());
|
|
// we should ignore it, but just in case run the removal code.
|
|
RemoveTaskFromPriorityQueue(TaskEvents[EventIndex].RelatedTask);
|
|
continue;
|
|
}
|
|
|
|
switch (TaskEvents[EventIndex].Event)
|
|
{
|
|
case EGameplayTaskEvent::Add:
|
|
if (TaskEvents[EventIndex].RelatedTask.TaskState != EGameplayTaskState::Finished)
|
|
{
|
|
AddTaskToPriorityQueue(TaskEvents[EventIndex].RelatedTask);
|
|
}
|
|
else
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Error, TEXT("UGameplayTasksComponent::ProcessTaskEvents trying to add a finished task to priority queue!"));
|
|
}
|
|
break;
|
|
case EGameplayTaskEvent::Remove:
|
|
RemoveTaskFromPriorityQueue(TaskEvents[EventIndex].RelatedTask);
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
}
|
|
|
|
if (EventIndex >= ErronousIterationsLimit)
|
|
{
|
|
UE_VLOG(this, LogGameplayTasks, Error, TEXT("UGameplayTasksComponent::ProcessTaskEvents has exceeded warning-level of iterations. Check your GameplayTasks for logic loops!"));
|
|
}
|
|
}
|
|
|
|
TaskEvents.Reset();
|
|
}
|
|
|
|
void UGameplayTasksComponent::AddTaskToPriorityQueue(UGameplayTask& NewTask)
|
|
{
|
|
// The generic idea is as follows:
|
|
// 1. Find insertion/removal point X
|
|
// While looking for it add up all ResourcesUsedByActiveUpToX and ResourcesRequiredUpToX
|
|
// ResourcesUsedByActiveUpToX - resources required by ACTIVE tasks
|
|
// ResourcesRequiredUpToX - resources required by both active and inactive tasks on the way to X
|
|
// 2. Insert Task at X
|
|
// 3. Starting from X proceed down the queue
|
|
// a. If ConsideredTask.Resources overlaps with ResourcesRequiredUpToX then PAUSE it
|
|
// b. Else, ACTIVATE ConsideredTask and add ConsideredTask.Resources to ResourcesUsedByActiveUpToX
|
|
// c. Add ConsideredTask.Resources to ResourcesRequiredUpToX
|
|
// 4. Set this->CurrentlyUsedResources to ResourcesUsedByActiveUpToX
|
|
//
|
|
// Points 3-4 are implemented as a separate function, UpdateTaskActivationFromIndex,
|
|
// since it's a common code between adding and removing tasks
|
|
|
|
const bool bStartOnTopOfSamePriority = NewTask.GetResourceOverlapPolicy() == ETaskResourceOverlapPolicy::StartOnTop;
|
|
|
|
FGameplayResourceSet ResourcesClaimedUpToX;
|
|
FGameplayResourceSet ResourcesBlockedUpToIndex;
|
|
int32 InsertionPoint = INDEX_NONE;
|
|
|
|
if (TaskPriorityQueue.Num() > 0)
|
|
{
|
|
for (int32 TaskIndex = 0; TaskIndex < TaskPriorityQueue.Num(); ++TaskIndex)
|
|
{
|
|
ensure(TaskPriorityQueue[TaskIndex]);
|
|
if (TaskPriorityQueue[TaskIndex] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((bStartOnTopOfSamePriority && TaskPriorityQueue[TaskIndex]->GetPriority() <= NewTask.GetPriority())
|
|
|| (!bStartOnTopOfSamePriority && TaskPriorityQueue[TaskIndex]->GetPriority() < NewTask.GetPriority()))
|
|
{
|
|
TaskPriorityQueue.Insert(&NewTask, TaskIndex);
|
|
InsertionPoint = TaskIndex;
|
|
break;
|
|
}
|
|
|
|
const FGameplayResourceSet ClaimedResources = TaskPriorityQueue[TaskIndex]->GetClaimedResources();
|
|
if (TaskPriorityQueue[TaskIndex]->IsActive())
|
|
{
|
|
ResourcesClaimedUpToX.AddSet(ClaimedResources);
|
|
}
|
|
ResourcesBlockedUpToIndex.AddSet(ClaimedResources);
|
|
}
|
|
}
|
|
|
|
if (InsertionPoint == INDEX_NONE)
|
|
{
|
|
TaskPriorityQueue.Add(&NewTask);
|
|
InsertionPoint = TaskPriorityQueue.Num() - 1;
|
|
}
|
|
|
|
UpdateTaskActivationFromIndex(InsertionPoint, ResourcesClaimedUpToX, ResourcesBlockedUpToIndex);
|
|
}
|
|
|
|
void UGameplayTasksComponent::RemoveTaskFromPriorityQueue(UGameplayTask& Task)
|
|
{
|
|
const int32 RemovedTaskIndex = TaskPriorityQueue.Find(&Task);
|
|
if (RemovedTaskIndex != INDEX_NONE)
|
|
{
|
|
if (TaskPriorityQueue.Num() > 1)
|
|
{
|
|
// sum up resources up to TaskIndex
|
|
FGameplayResourceSet ResourcesClaimedUpToX;
|
|
FGameplayResourceSet ResourcesBlockedUpToIndex;
|
|
for (int32 TaskIndex = 0; TaskIndex < RemovedTaskIndex; ++TaskIndex)
|
|
{
|
|
ensure(TaskPriorityQueue[TaskIndex]);
|
|
if (TaskPriorityQueue[TaskIndex] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FGameplayResourceSet ClaimedResources = TaskPriorityQueue[TaskIndex]->GetClaimedResources();
|
|
if (TaskPriorityQueue[TaskIndex]->IsActive())
|
|
{
|
|
ResourcesClaimedUpToX.AddSet(ClaimedResources);
|
|
}
|
|
ResourcesBlockedUpToIndex.AddSet(ClaimedResources);
|
|
}
|
|
|
|
// don't forget to actually remove the task from the queue
|
|
TaskPriorityQueue.RemoveAt(RemovedTaskIndex, 1, /*bAllowShrinking=*/false);
|
|
|
|
// if it wasn't the last item then proceed as usual
|
|
if (RemovedTaskIndex < TaskPriorityQueue.Num())
|
|
{
|
|
UpdateTaskActivationFromIndex(RemovedTaskIndex, ResourcesClaimedUpToX, ResourcesBlockedUpToIndex);
|
|
}
|
|
else
|
|
{
|
|
// no need to do extra processing. This was the last task, so
|
|
// ResourcesUsedByActiveUpToX is the CurrentlyUsedResources
|
|
SetCurrentlyClaimedResources(ResourcesClaimedUpToX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TaskPriorityQueue.Pop(/*bAllowShrinking=*/false);
|
|
SetCurrentlyClaimedResources(FGameplayResourceSet());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// take a note and ignore
|
|
UE_VLOG(this, LogGameplayTasks, Verbose, TEXT("RemoveTaskFromPriorityQueue for %s called, but it's not in the queue. Might have been already removed"), *Task.GetName());
|
|
}
|
|
}
|
|
|
|
void UGameplayTasksComponent::UpdateTaskActivationFromIndex(int32 StartingIndex, FGameplayResourceSet ResourcesClaimedUpToIndex, FGameplayResourceSet ResourcesBlockedUpToIndex)
|
|
{
|
|
if (TaskPriorityQueue.Num() > 0)
|
|
{
|
|
check(TaskPriorityQueue.IsValidIndex(StartingIndex));
|
|
|
|
TArray<UGameplayTask*> TaskPriorityQueueCopy = TaskPriorityQueue;
|
|
for (int32 TaskIndex = StartingIndex; TaskIndex < TaskPriorityQueueCopy.Num(); ++TaskIndex)
|
|
{
|
|
ensure(TaskPriorityQueueCopy[TaskIndex]);
|
|
if (TaskPriorityQueueCopy[TaskIndex] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FGameplayResourceSet RequiredResources = TaskPriorityQueueCopy[TaskIndex]->GetRequiredResources();
|
|
const FGameplayResourceSet ClaimedResources = TaskPriorityQueueCopy[TaskIndex]->GetClaimedResources();
|
|
if (RequiredResources.GetOverlap(ResourcesBlockedUpToIndex).IsEmpty())
|
|
{
|
|
TaskPriorityQueueCopy[TaskIndex]->ActivateInTaskQueue();
|
|
ResourcesClaimedUpToIndex.AddSet(ClaimedResources);
|
|
}
|
|
else
|
|
{
|
|
TaskPriorityQueueCopy[TaskIndex]->PauseInTaskQueue();
|
|
}
|
|
ResourcesBlockedUpToIndex.AddSet(ClaimedResources);
|
|
}
|
|
}
|
|
|
|
SetCurrentlyClaimedResources(ResourcesClaimedUpToIndex);
|
|
}
|
|
|
|
void UGameplayTasksComponent::SetCurrentlyClaimedResources(FGameplayResourceSet NewClaimedSet)
|
|
{
|
|
if (CurrentlyClaimedResources != NewClaimedSet)
|
|
{
|
|
FGameplayResourceSet ReleasedResources = FGameplayResourceSet(CurrentlyClaimedResources).RemoveSet(NewClaimedSet);
|
|
FGameplayResourceSet ClaimedResources = FGameplayResourceSet(NewClaimedSet).RemoveSet(CurrentlyClaimedResources);
|
|
CurrentlyClaimedResources = NewClaimedSet;
|
|
OnClaimedResourcesChange.Broadcast(ClaimedResources, ReleasedResources);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// debugging
|
|
//----------------------------------------------------------------------//
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && ENABLE_VISUAL_LOG
|
|
FString UGameplayTasksComponent::GetTickingTasksDescription() const
|
|
{
|
|
FString TasksDescription;
|
|
for (auto& Task : TickingTasks)
|
|
{
|
|
if (Task.IsValid())
|
|
{
|
|
TasksDescription += FString::Printf(TEXT("\n%s %s"), *GetTaskStateName(Task->GetState()), *Task->GetDebugDescription());
|
|
}
|
|
else
|
|
{
|
|
TasksDescription += TEXT("\nNULL");
|
|
}
|
|
}
|
|
return TasksDescription;
|
|
}
|
|
|
|
FString UGameplayTasksComponent::GetTasksPriorityQueueDescription() const
|
|
{
|
|
FString TasksDescription;
|
|
for (auto Task : TaskPriorityQueue)
|
|
{
|
|
if (Task != nullptr)
|
|
{
|
|
TasksDescription += FString::Printf(TEXT("\n%s %s"), *GetTaskStateName(Task->GetState()), *Task->GetDebugDescription());
|
|
}
|
|
else
|
|
{
|
|
TasksDescription += TEXT("\nNULL");
|
|
}
|
|
}
|
|
return TasksDescription;
|
|
}
|
|
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
|
|
#if ENABLE_VISUAL_LOG
|
|
void UGameplayTasksComponent::DescribeSelfToVisLog(FVisualLogEntry* Snapshot) const
|
|
{
|
|
static const FString CategoryName = TEXT("GameplayTasks");
|
|
static const FString TickingTasksName = TEXT("Ticking tasks");
|
|
static const FString PriorityQueueName = TEXT("Priority Queue");
|
|
|
|
if (IsPendingKill())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FVisualLogStatusCategory StatusCategory(CategoryName);
|
|
|
|
StatusCategory.Add(TickingTasksName, GetTickingTasksDescription());
|
|
StatusCategory.Add(PriorityQueueName, GetTasksPriorityQueueDescription());
|
|
|
|
Snapshot->Status.Add(StatusCategory);
|
|
}
|
|
|
|
FString UGameplayTasksComponent::GetTaskStateName(EGameplayTaskState Value)
|
|
{
|
|
static const UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EGameplayTaskState"));
|
|
check(Enum);
|
|
return Enum->GetEnumName(int32(Value));
|
|
}
|
|
#endif // ENABLE_VISUAL_LOG
|
|
|
|
EGameplayTaskRunResult UGameplayTasksComponent::RunGameplayTask(IGameplayTaskOwnerInterface& TaskOwner, UGameplayTask& Task, uint8 Priority, FGameplayResourceSet AdditionalRequiredResources, FGameplayResourceSet AdditionalClaimedResources)
|
|
{
|
|
const FText NoneText = FText::FromString(TEXT("None"));
|
|
|
|
if (Task.GetState() == EGameplayTaskState::Paused || Task.GetState() == EGameplayTaskState::Active)
|
|
{
|
|
// return as success if already running for the same owner, failure otherwise
|
|
return Task.GetTaskOwner() == &TaskOwner
|
|
? (Task.GetState() == EGameplayTaskState::Paused ? EGameplayTaskRunResult::Success_Paused : EGameplayTaskRunResult::Success_Active)
|
|
: EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
// this is a valid situation if the task has been created via "Construct Object" mechanics
|
|
if (Task.GetState() == EGameplayTaskState::Uninitialized)
|
|
{
|
|
Task.InitTask(TaskOwner, Priority);
|
|
}
|
|
|
|
Task.AddRequiredResourceSet(AdditionalRequiredResources);
|
|
Task.AddClaimedResourceSet(AdditionalClaimedResources);
|
|
Task.ReadyForActivation();
|
|
|
|
switch (Task.GetState())
|
|
{
|
|
case EGameplayTaskState::AwaitingActivation:
|
|
case EGameplayTaskState::Paused:
|
|
return EGameplayTaskRunResult::Success_Paused;
|
|
break;
|
|
case EGameplayTaskState::Active:
|
|
return EGameplayTaskRunResult::Success_Active;
|
|
break;
|
|
case EGameplayTaskState::Finished:
|
|
return EGameplayTaskRunResult::Success_Active;
|
|
break;
|
|
}
|
|
|
|
return EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// BP API
|
|
//----------------------------------------------------------------------//
|
|
EGameplayTaskRunResult UGameplayTasksComponent::K2_RunGameplayTask(TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner, UGameplayTask* Task, uint8 Priority, TArray<TSubclassOf<UGameplayTaskResource> > AdditionalRequiredResources, TArray<TSubclassOf<UGameplayTaskResource> > AdditionalClaimedResources)
|
|
{
|
|
const FText NoneText = FText::FromString(TEXT("None"));
|
|
|
|
if (TaskOwner.GetInterface() == nullptr)
|
|
{
|
|
FMessageLog("PIE").Error(FText::Format(
|
|
LOCTEXT("RunGameplayTaskNullOwner", "Tried running a gameplay task {0} while owner is None!"),
|
|
Task ? FText::FromName(Task->GetFName()) : NoneText));
|
|
return EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
IGameplayTaskOwnerInterface& OwnerInstance = *TaskOwner;
|
|
|
|
if (Task == nullptr)
|
|
{
|
|
FMessageLog("PIE").Error(FText::Format(
|
|
LOCTEXT("RunNullGameplayTask", "Tried running a None task for {0}"),
|
|
FText::FromString(Cast<UObject>(&OwnerInstance)->GetName())
|
|
));
|
|
return EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
if (Task->GetState() == EGameplayTaskState::Paused || Task->GetState() == EGameplayTaskState::Active)
|
|
{
|
|
FMessageLog("PIE").Warning(FText::Format(
|
|
LOCTEXT("RunNullGameplayTask", "Tried running a None task for {0}"),
|
|
FText::FromString(Cast<UObject>(&OwnerInstance)->GetName())
|
|
));
|
|
// return as success if already running for the same owner, failure otherwise
|
|
return Task->GetTaskOwner() == &OwnerInstance
|
|
? (Task->GetState() == EGameplayTaskState::Paused ? EGameplayTaskRunResult::Success_Paused : EGameplayTaskRunResult::Success_Active)
|
|
: EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
// this is a valid situation if the task has been created via "Construct Object" mechanics
|
|
if (Task->GetState() == EGameplayTaskState::Uninitialized)
|
|
{
|
|
Task->InitTask(OwnerInstance, Priority);
|
|
}
|
|
|
|
Task->AddRequiredResourceSet(AdditionalRequiredResources);
|
|
Task->AddClaimedResourceSet(AdditionalClaimedResources);
|
|
Task->ReadyForActivation();
|
|
|
|
switch (Task->GetState())
|
|
{
|
|
case EGameplayTaskState::AwaitingActivation:
|
|
case EGameplayTaskState::Paused:
|
|
return EGameplayTaskRunResult::Success_Paused;
|
|
break;
|
|
case EGameplayTaskState::Active:
|
|
return EGameplayTaskRunResult::Success_Active;
|
|
break;
|
|
case EGameplayTaskState::Finished:
|
|
return EGameplayTaskRunResult::Success_Active;
|
|
break;
|
|
}
|
|
|
|
return EGameplayTaskRunResult::Error;
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// FGameplayResourceSet
|
|
//----------------------------------------------------------------------//
|
|
FString FGameplayResourceSet::GetDebugDescription() const
|
|
{
|
|
static const int32 FlagsCount = sizeof(FFlagContainer)* 8;
|
|
TCHAR Description[FlagsCount + 1];
|
|
FFlagContainer FlagsCopy = Flags;
|
|
int32 FlagIndex = 0;
|
|
for (; FlagIndex < FlagsCount && FlagsCopy != 0; ++FlagIndex)
|
|
{
|
|
Description[FlagIndex] = (FlagsCopy & (1 << FlagIndex)) ? TCHAR('1') : TCHAR('0');
|
|
FlagsCopy &= ~(1 << FlagIndex);
|
|
}
|
|
Description[FlagIndex] = TCHAR('\0');
|
|
return FString(Description);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |