Files
UnrealEngine/Engine/Plugins/Runtime/StateTree/Source/StateTreeModule/Private/StateTreeAsyncExecutionContext.cpp
2025-05-18 13:04:45 +08:00

404 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "StateTreeAsyncExecutionContext.h"
#include "StateTreeExecutionContext.h"
namespace UE::StateTree::Async
{
const FStateTreeNodeBase& FActivePathInfo::GetNode() const
{
check(IsValid() && Frame->StateTree && NodeIndex.IsValid());
return *(Frame->StateTree->GetNode(NodeIndex.AsInt32()).GetPtr<const FStateTreeNodeBase>());
}
}
template <bool bWithWriteAccess>
TStateTreeStrongExecutionContext<bWithWriteAccess>::TStateTreeStrongExecutionContext(const FStateTreeWeakExecutionContext& WeakContext)
: Owner(WeakContext.Owner.Pin()),
StateTree(WeakContext.StateTree.Pin()),
Storage(WeakContext.Storage.Pin()),
FrameID(WeakContext.FrameID),
StateID(WeakContext.StateID),
NodeIndex(WeakContext.NodeIndex)
{
if (Owner && StateTree && Storage)
{
bAccessAcquired = true;
if constexpr (bWithWriteAccess)
{
Storage->AcquireWriteAccess();
}
else
{
Storage->AcquireReadAccess();
}
}
}
template <bool bWithWriteAccess>
TStateTreeStrongExecutionContext<bWithWriteAccess>::~TStateTreeStrongExecutionContext()
{
if (bAccessAcquired)
{
check(IsValidInstanceStorage());
if constexpr (bWithWriteAccess)
{
Storage->ReleaseWriteAccess();
}
else
{
Storage->ReleaseReadAccess();
}
}
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::SendEvent(const FGameplayTag Tag, const FConstStructView Payload, const FName Origin) const
{
static_assert(bWithWriteAccess);
if (IsValid())
{
FStateTreeMinimalExecutionContext Context(Owner.Get(), StateTree.Get(), *Storage.Get());
Context.SendEvent(Tag, Payload, Origin);
return true;
}
return false;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::RequestTransition(FStateTreeStateHandle TargetState,
EStateTreeTransitionPriority Priority, EStateTreeSelectionFallback Fallback) const
{
static_assert(bWithWriteAccess);
UE::StateTree::Async::FActivePathInfo ActivePath = GetActivePathInfo();
if (ActivePath.IsValid())
{
FStateTreeTransitionRequest Request;
Request.SourceFrameID = FrameID;
Request.SourceStateID = StateID;
Request.TargetState = TargetState;
Request.Priority = Priority;
Request.Fallback = Fallback;
Storage->AddTransitionRequest(Owner.Get(), Request);
ScheduleNextTick(Owner.Get(), StateTree.Get(), *Storage);
return true;
}
return false;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::BroadcastDelegate(const FStateTreeDelegateDispatcher& Dispatcher)
const
{
static_assert(bWithWriteAccess);
if (!Dispatcher.IsValid())
{
// Nothings binds to the delegate, not an error.
return true;
}
UE::StateTree::Async::FActivePathInfo ActivePath = GetActivePathInfo();
if (ActivePath.IsValid())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
Exec.DelegateActiveListeners.BroadcastDelegate(Dispatcher, Exec);
if (UE::StateTree::ExecutionContext::MarkDelegateAsBroadcasted(Dispatcher, *ActivePath.Frame, *Storage))
{
ScheduleNextTick(Owner.Get(), StateTree.Get(), *Storage);
}
return true;
}
return false;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::BindDelegate(const FStateTreeDelegateListener& Listener, FSimpleDelegate Delegate) const
{
static_assert(bWithWriteAccess);
if (!Listener.IsValid())
{
// Nothing binds to the delegate, not an error.
return true;
}
UE::StateTree::Async::FActivePathInfo ActivePath = GetActivePathInfo();
if (ActivePath.IsValid())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
if (ensure(ActivePath.Frame->StateTree))
{
Exec.DelegateActiveListeners.Add(Listener, MoveTemp(Delegate), FrameID, StateID, NodeIndex);
return true;
}
}
return false;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::UnbindDelegate(const FStateTreeDelegateListener& Listener) const
{
static_assert(bWithWriteAccess);
if (!Listener.IsValid())
{
// The listener is not bound to a dispatcher. It will never trigger the delegate. It is not an error.
return true;
}
// Allow unbinding from context created outside the ExecContext loop
if (IsValid())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
Exec.DelegateActiveListeners.Remove(Listener);
return true;
}
return false;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::FinishTask(EStateTreeFinishTaskType FinishType) const
{
static_assert(bWithWriteAccess);
bool bSucceed = false;
UE::StateTree::Async::FActivePathInfo ActivePath = GetActivePathInfo();
if (ActivePath.IsValid())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
FStateTreeExecutionFrame* NonConstFrame = const_cast<FStateTreeExecutionFrame*>(ActivePath.Frame);
if (ensure(NonConstFrame->StateTree))
{
using namespace UE::StateTree;
const UStateTree* FrameStateTree = NonConstFrame->StateTree;
const int32 AssetNodeIndex = ActivePath.NodeIndex.AsInt32();
const int32 GlobalTaskBeginIndex = FrameStateTree->GlobalTasksBegin;
const int32 GlobalTaskEndIndex = FrameStateTree->GlobalTasksBegin + FrameStateTree->GlobalTasksNum;
const bool bIsGlobalTask = AssetNodeIndex >= GlobalTaskBeginIndex && AssetNodeIndex < GlobalTaskEndIndex;
const ETaskCompletionStatus TaskStatus = ExecutionContext::CastToTaskStatus(FinishType);
bool bCompleted = false;
if (NonConstFrame->bIsGlobalFrame && bIsGlobalTask)
{
const int32 FrameTaskIndex = AssetNodeIndex - GlobalTaskBeginIndex;
FTasksCompletionStatus GlobalTasksStatus = NonConstFrame->ActiveTasksStatus.GetStatus(NonConstFrame->StateTree);
GlobalTasksStatus.SetStatusWithPriority(FrameTaskIndex, TaskStatus);
bCompleted = GlobalTasksStatus.IsCompleted();
bSucceed = true;
}
else
{
const FCompactStateTreeState& State = NonConstFrame->StateTree->States[ActivePath.StateHandle.Index];
const int32 StateTaskBeginIndex = State.TasksBegin;
const int32 StateTaskEndIndex = State.TasksBegin + State.TasksNum;
const bool bIsStateTask = AssetNodeIndex >= StateTaskBeginIndex && AssetNodeIndex < StateTaskEndIndex;
if (bIsStateTask)
{
const int32 StateTaskIndex = AssetNodeIndex - State.TasksBegin;
FTasksCompletionStatus StateTasksStatus = NonConstFrame->ActiveTasksStatus.GetStatus(State);
StateTasksStatus.SetStatusWithPriority(StateTaskIndex, TaskStatus);
bCompleted = StateTasksStatus.IsCompleted();
bSucceed = true;
}
}
if (bCompleted)
{
Exec.bHasPendingCompletedState = true;
ScheduleNextTick(Owner.Get(), StateTree.Get(), *Storage);
}
}
}
return bSucceed;
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
bool TStateTreeStrongExecutionContext<bWithWriteAccess>::UpdateScheduledTickRequest(UE::StateTree::FScheduledTickHandle Handle, FStateTreeScheduledTick ScheduledTick) const
{
static_assert(bWithWriteAccess);
if (IsValid())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
if (Exec.UpdateScheduledTickRequest(Handle, ScheduledTick))
{
ScheduleNextTick(Owner.Get(), StateTree.Get(), *Storage);
}
return true;
}
return false;
}
template <bool bWithWriteAccess>
UE::StateTree::Async::FActivePathInfo TStateTreeStrongExecutionContext<bWithWriteAccess>::GetActivePathInfo() const
{
if (IsValidInstanceStorage())
{
FStateTreeExecutionState& Exec = Storage->GetMutableExecutionState();
int32 FrameIndex = Exec.IndexOfActiveFrame(FrameID);
if (FrameIndex != INDEX_NONE)
{
FStateTreeExecutionFrame* Frame = &Exec.ActiveFrames[FrameIndex];
FStateTreeStateHandle StateHandle = StateID.IsValid() ? Frame->ActiveStates.FindStateHandle(StateID) : FStateTreeStateHandle::Invalid;
if (!StateID.IsValid() || StateHandle.IsValid())
{
FStateTreeExecutionFrame* ParentFrame = FrameIndex != 0 ? &Exec.ActiveFrames[FrameIndex - 1] : nullptr;
return {Frame, ParentFrame, StateHandle, NodeIndex};
}
}
}
return {};
}
template <bool bWithWriteAccess>
FStateTreeDataView TStateTreeStrongExecutionContext<bWithWriteAccess>::GetInstanceDataPtrInternal() const
{
using namespace UE::StateTree;
Async::FActivePathInfo ActivePath = GetActivePathInfo();
if (ActivePath.IsValid())
{
const FStateTreeNodeBase& Node = ActivePath.GetNode();
FStateTreeDataView InstanceDataView = InstanceData::GetDataViewOrTemporary(
*Storage,
/* SharedInstanceStorage */ nullptr,
ActivePath.ParentFrame,
*ActivePath.Frame,
ActivePath.GetNode().InstanceDataHandle);
return InstanceDataView;
}
return {};
}
template <bool bWithWriteAccess>
template<bool bWriteAccess UE_REQUIRES_DEFINITION(bWriteAccess)>
void TStateTreeStrongExecutionContext<bWithWriteAccess>::ScheduleNextTick(TNotNull<UObject*> Owner, TNotNull<const UStateTree*> RootStateTree, FStateTreeInstanceStorage& Storage)
{
static_assert(bWithWriteAccess);
TInstancedStruct<FStateTreeExecutionExtension>& ExecutionExtension = Storage.GetMutableExecutionState().ExecutionExtension;
if (RootStateTree->IsScheduledTickAllowed() && ExecutionExtension.IsValid())
{
ExecutionExtension.GetMutable().ScheduleNextTick(FStateTreeExecutionExtension::FContextParameters(*Owner, *RootStateTree, Storage));
}
}
template struct TStateTreeStrongExecutionContext<false>;
template struct TStateTreeStrongExecutionContext<true>;
//@todo: remove all these instantiations once past 5.6 because of c++20 support
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::SendEvent(const FGameplayTag Tag, const FConstStructView Payload, const FName Origin) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::RequestTransition(FStateTreeStateHandle TargetState, EStateTreeTransitionPriority Priority, EStateTreeSelectionFallback Fallback) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::BroadcastDelegate(const FStateTreeDelegateDispatcher& Dispatcher) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::BindDelegate(const FStateTreeDelegateListener& Listener, FSimpleDelegate Delegate) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::UnbindDelegate(const FStateTreeDelegateListener& Listener) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::FinishTask(EStateTreeFinishTaskType FinishType) const;
template bool STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::UpdateScheduledTickRequest(UE::StateTree::FScheduledTickHandle Handle, FStateTreeScheduledTick ScheduledTick) const;
template void STATETREEMODULE_API TStateTreeStrongExecutionContext<true>::ScheduleNextTick(TNotNull<UObject*> Owner, TNotNull<const UStateTree*> RootStateTree, FStateTreeInstanceStorage& Storage);
FStateTreeWeakExecutionContext::FStateTreeWeakExecutionContext(const FStateTreeExecutionContext& Context)
: Owner(Context.GetOwner())
, StateTree(Context.GetStateTree())
, Storage(Context.GetMutableInstanceData()->GetWeakMutableStorage())
{
if (const FStateTreeExecutionFrame* Frame = Context.GetCurrentlyProcessedFrame())
{
FrameID = Frame->FrameID;
const int32 ActiveStateIndex = Frame->ActiveStates.IndexOfReverse(Context.GetCurrentlyProcessedState());
if (ActiveStateIndex != INDEX_NONE)
{
StateID = Frame->ActiveStates.StateIDs[ActiveStateIndex];
}
// Only writes down the NodeIndex if it is created synchronously from the ExecContext tick frame
NodeIndex = Context.GetCurrentlyProcessedNodeIndex();
}
}
bool FStateTreeWeakExecutionContext::SendEvent(const FGameplayTag Tag, const FConstStructView Payload, const FName Origin) const
{
return MakeStrongExecutionContext().SendEvent(Tag, Payload, Origin);
}
bool FStateTreeWeakExecutionContext::RequestTransition(FStateTreeStateHandle TargetState, EStateTreeTransitionPriority Priority, const EStateTreeSelectionFallback Fallback) const
{
return MakeStrongExecutionContext().RequestTransition(TargetState, Priority, Fallback);
}
bool FStateTreeWeakExecutionContext::BroadcastDelegate(const FStateTreeDelegateDispatcher& Dispatcher) const
{
return MakeStrongExecutionContext().BroadcastDelegate(Dispatcher);
}
bool FStateTreeWeakExecutionContext::BindDelegate(const FStateTreeDelegateListener& Listener, FSimpleDelegate Delegate) const
{
constexpr bool bWithWriteAccess = true;
return MakeStrongExecutionContext().BindDelegate(Listener, Delegate);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
bool FStateTreeWeakExecutionContext::BindDelegate(const FStateTreeWeakTaskRef& Task, const FStateTreeDelegateListener& Listener, FSimpleDelegate Delegate) const
{
// Deprecated. Use the version without the TaskRef.
return BindDelegate(Listener, Delegate);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
bool FStateTreeWeakExecutionContext::RemoveDelegateListener(const FStateTreeDelegateListener& Listener) const
{
return UnbindDelegate(Listener);
}
bool FStateTreeWeakExecutionContext::UnbindDelegate(const FStateTreeDelegateListener& Listener) const
{
return MakeStrongExecutionContext().UnbindDelegate(Listener);
}
bool FStateTreeWeakExecutionContext::FinishTask(EStateTreeFinishTaskType FinishType) const
{
return MakeStrongExecutionContext().FinishTask(FinishType);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
bool FStateTreeWeakExecutionContext::FinishTask(const FStateTreeWeakTaskRef& Task, EStateTreeFinishTaskType FinishType) const
{
return FinishTask(FinishType);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
bool FStateTreeWeakExecutionContext::UpdateScheduledTickRequest(UE::StateTree::FScheduledTickHandle Handle, FStateTreeScheduledTick ScheduledTick) const
{
return MakeStrongExecutionContext().UpdateScheduledTickRequest(Handle, ScheduledTick);
}