2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2022-04-05 09:44:28 -04:00
# include "StateTreeCompiler.h"
2021-09-28 13:33:17 -04:00
# include "StateTree.h"
# include "StateTreeEditorData.h"
# include "StateTreeTypes.h"
# include "Conditions/StateTreeCondition_Common.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeTaskBase.h"
2021-10-21 04:08:20 -04:00
# include "StateTreeConditionBase.h"
2021-09-28 13:33:17 -04:00
# include "StateTreeState.h"
# include "StateTreeExecutionContext.h"
# include "CoreMinimal.h"
# include "StateTreePropertyBindingCompiler.h"
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : Compile ( UStateTree & InStateTree )
2021-09-28 13:33:17 -04:00
{
StateTree = & InStateTree ;
TreeData = Cast < UStateTreeEditorData > ( StateTree - > EditorData ) ;
if ( ! TreeData )
{
return false ;
}
// Cleanup existing state
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2021-09-28 13:33:17 -04:00
2022-04-06 10:04:05 -04:00
if ( ! BindingsCompiler . Init ( StateTree - > PropertyBindings , Log ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2021-09-28 13:33:17 -04:00
2021-10-21 04:08:20 -04:00
if ( ! CreateStates ( ) )
2021-09-28 13:33:17 -04:00
{
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2021-09-28 13:33:17 -04:00
return false ;
}
2022-04-05 03:20:57 -04:00
if ( ! CreateExecutionInfos ( ) )
{
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2022-04-05 03:20:57 -04:00
return false ;
}
if ( ! CreateStateEvaluators ( ) )
{
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2022-04-05 03:20:57 -04:00
return false ;
}
if ( ! CreateStateTasks ( ) )
{
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2022-04-05 03:20:57 -04:00
return false ;
}
2021-10-21 04:08:20 -04:00
if ( ! CreateStateTransitions ( ) )
2021-09-28 13:33:17 -04:00
{
2022-04-05 09:44:28 -04:00
StateTree - > ResetCompiled ( ) ;
2021-09-28 13:33:17 -04:00
return false ;
}
BindingsCompiler . Finalize ( ) ;
2022-04-06 10:04:05 -04:00
if ( ! StateTree - > Link ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2021-09-28 13:33:17 -04:00
return true ;
}
2022-04-05 09:44:28 -04:00
FStateTreeHandle FStateTreeCompiler : : GetStateHandle ( const FGuid & StateID ) const
2021-10-29 04:33:21 -04:00
{
const int32 * Idx = IDToState . Find ( StateID ) ;
if ( Idx = = nullptr )
{
return FStateTreeHandle : : Invalid ;
}
return FStateTreeHandle ( uint16 ( * Idx ) ) ;
}
2022-04-05 09:44:28 -04:00
UStateTreeState * FStateTreeCompiler : : GetState ( const FGuid & StateID )
2022-04-05 03:20:57 -04:00
{
const int32 * Idx = IDToState . Find ( StateID ) ;
if ( Idx = = nullptr )
{
return nullptr ;
}
return SourceStates [ * Idx ] ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateStates ( )
2021-09-28 13:33:17 -04:00
{
// Create item for the runtime execution state
2021-11-12 05:48:11 -05:00
StateTree - > Instances . Add ( FInstancedStruct : : Make < FStateTreeExecutionState > ( ) ) ;
2021-09-28 13:33:17 -04:00
2022-01-28 03:38:15 -05:00
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
2021-09-28 13:33:17 -04:00
{
2022-01-28 03:38:15 -05:00
if ( SubTree ! = nullptr )
2021-09-28 13:33:17 -04:00
{
2022-01-28 03:38:15 -05:00
if ( ! CreateStateRecursive ( * SubTree , FStateTreeHandle : : Invalid ) )
2021-09-28 13:33:17 -04:00
{
return false ;
}
}
}
2021-11-12 05:48:11 -05:00
2021-09-28 13:33:17 -04:00
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateStateRecursive ( UStateTreeState & State , const FStateTreeHandle Parent )
2022-04-05 03:20:57 -04:00
{
FStateTreeCompilerLogStateScope LogStateScope ( & State , Log ) ;
const int32 StateIdx = StateTree - > States . AddDefaulted ( ) ;
2022-04-05 09:44:28 -04:00
FCompactStateTreeState & BakedState = StateTree - > States [ StateIdx ] ;
2022-04-05 03:20:57 -04:00
BakedState . Name = State . Name ;
BakedState . Parent = Parent ;
SourceStates . Add ( & State ) ;
IDToState . Add ( State . ID , StateIdx ) ;
check ( StateTree - > Instances . Num ( ) < = int32 ( MAX_uint16 ) ) ;
// Child states
check ( StateTree - > States . Num ( ) < = int32 ( MAX_uint16 ) ) ;
BakedState . ChildrenBegin = uint16 ( StateTree - > States . Num ( ) ) ;
for ( UStateTreeState * Child : State . Children )
{
if ( Child )
{
if ( ! CreateStateRecursive ( * Child , FStateTreeHandle ( ( uint16 ) StateIdx ) ) )
{
return false ;
}
}
}
check ( StateTree - > States . Num ( ) < = int32 ( MAX_uint16 ) ) ;
StateTree - > States [ StateIdx ] . ChildrenEnd = uint16 ( StateTree - > States . Num ( ) ) ; // Cannot use BakedState here, it may be invalid due to array resize.
return true ;
}
2022-04-05 09:44:28 -04:00
FString FStateTreeCompiler : : GetExecutionPathString ( const TConstArrayView < const UStateTreeState * > Path )
2022-04-05 03:20:57 -04:00
{
FString PathStr ;
const UStateTreeState * PrevState = nullptr ;
for ( int32 Index = 0 ; Index < Path . Num ( ) ; Index + + )
{
const UStateTreeState * State = Path [ Index ] ;
check ( State ) ;
if ( PrevState ! = nullptr )
{
if ( PrevState ! = State - > Parent )
{
// Linked
PathStr + = TEXT ( " > " ) ;
}
else
{
PathStr + = TEXT ( " / " ) ;
}
}
PathStr + = State - > Name . ToString ( ) ;
PrevState = State ;
}
return PathStr ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : IsPathLinked ( const TConstArrayView < const UStateTreeState * > Path )
2022-04-05 03:20:57 -04:00
{
bool bIsLinked = false ;
const UStateTreeState * PrevState = nullptr ;
for ( int32 Index = 0 ; Index < Path . Num ( ) ; Index + + )
{
const UStateTreeState * State = Path [ Index ] ;
check ( State ) ;
// If the previous state is not the parent state in tree, then the connection must have come from a linked state (a state linking to an arbitrary state in the tree).
if ( PrevState & & PrevState ! = State - > Parent )
{
bIsLinked = true ;
break ;
}
PrevState = State ;
}
return bIsLinked ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateExecutionInfos ( )
2022-04-05 03:20:57 -04:00
{
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
{
if ( SubTree ! = nullptr )
{
TArray < const UStateTreeState * > Path ;
if ( ! CreateExecutionInfosRecursive ( * SubTree , Path ) )
{
return false ;
}
}
}
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateExecutionInfosRecursive ( UStateTreeState & State , TArray < const UStateTreeState * > & Path )
2022-04-05 03:20:57 -04:00
{
Path . Add ( & State ) ;
if ( Path . Num ( ) > FStateTreeActiveStates : : MaxStates )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Reached maximum execution depth %d at: '%s'. " ) , FStateTreeActiveStates : : MaxStates , * GetExecutionPathString ( Path ) ) ;
return false ;
}
FStateExecutionInfo & ExecInfo = ExecutionInfos . FindOrAdd ( & State ) ;
ExecInfo . ExecutionPaths . Emplace ( Path ) ;
if ( State . Type = = EStateTreeStateType : : Linked )
{
if ( UStateTreeState * LinkedState = GetState ( State . LinkedState . ID ) )
{
if ( ! CreateExecutionInfosRecursive ( * LinkedState , Path ) )
{
return false ;
}
}
}
for ( UStateTreeState * Child : State . Children )
{
if ( Child )
{
if ( ! CreateExecutionInfosRecursive ( * Child , Path ) )
{
return false ;
}
}
}
Path . Pop ( ) ;
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateConditions ( UStateTreeState & State , TConstArrayView < FStateTreeEditorNode > Conditions )
2022-03-28 04:48:50 -04:00
{
for ( int32 Index = 0 ; Index < Conditions . Num ( ) ; Index + + )
{
const bool bIsFirst = Index = = 0 ;
const FStateTreeEditorNode & CondNode = Conditions [ Index ] ;
// First operand should be copy as we dont have a previous item to operate on.
const EStateTreeConditionOperand Operand = bIsFirst ? EStateTreeConditionOperand : : Copy : CondNode . ConditionOperand ;
// First indent must be 0 to make the parentheses calculation match.
const int32 CurrIndent = bIsFirst ? 0 : CondNode . ConditionIndent ;
// Next indent, or terminate at zero.
const int32 NextIndent = Conditions . IsValidIndex ( Index + 1 ) ? Conditions [ Index ] . ConditionIndent : 0 ;
const int32 DeltaIndent = NextIndent - CurrIndent ;
check ( DeltaIndent > = MIN_int8 & & DeltaIndent < = MAX_int8 ) ;
2022-04-05 03:20:57 -04:00
if ( ! CreateCondition ( State , CondNode , Operand , ( int8 ) DeltaIndent ) )
2022-03-28 04:48:50 -04:00
{
return false ;
}
}
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateStateTasks ( )
2022-04-05 03:20:57 -04:00
{
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
2022-04-05 09:44:28 -04:00
FCompactStateTreeState & BakedState = StateTree - > States [ i ] ;
2022-04-05 03:20:57 -04:00
UStateTreeState * SourceState = SourceStates [ i ] ;
check ( SourceState ! = nullptr ) ;
FStateTreeCompilerLogStateScope LogStateScope ( SourceState , Log ) ;
// Create tasks
check ( StateTree - > Nodes . Num ( ) < = int32 ( MAX_uint16 ) ) ;
BakedState . TasksBegin = uint16 ( StateTree - > Nodes . Num ( ) ) ;
for ( FStateTreeEditorNode & TaskNode : SourceState - > Tasks )
{
if ( ! CreateTask ( * SourceState , TaskNode ) )
{
return false ;
}
}
if ( ! CreateTask ( * SourceState , SourceState - > SingleTask ) )
{
return false ;
}
const int32 TasksNum = StateTree - > Nodes . Num ( ) - int32 ( BakedState . TasksBegin ) ;
check ( TasksNum < = int32 ( MAX_uint8 ) ) ;
BakedState . TasksNum = uint8 ( TasksNum ) ;
}
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateStateEvaluators ( )
2022-04-05 03:20:57 -04:00
{
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
2022-04-05 09:44:28 -04:00
FCompactStateTreeState & BakedState = StateTree - > States [ i ] ;
2022-04-05 03:20:57 -04:00
UStateTreeState * SourceState = SourceStates [ i ] ;
check ( SourceState ! = nullptr ) ;
FStateTreeCompilerLogStateScope LogStateScope ( SourceState , Log ) ;
// Collect evaluators
check ( StateTree - > Nodes . Num ( ) < = int32 ( MAX_uint16 ) ) ;
BakedState . EvaluatorsBegin = uint16 ( StateTree - > Nodes . Num ( ) ) ;
for ( FStateTreeEditorNode & EvalNode : SourceState - > Evaluators )
{
if ( ! CreateEvaluator ( * SourceState , EvalNode ) )
{
return false ;
}
}
const int32 EvaluatorsNum = StateTree - > Nodes . Num ( ) - int32 ( BakedState . EvaluatorsBegin ) ;
check ( EvaluatorsNum < = int32 ( MAX_uint8 ) ) ;
BakedState . EvaluatorsNum = uint8 ( EvaluatorsNum ) ;
}
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateStateTransitions ( )
2021-09-28 13:33:17 -04:00
{
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
2022-04-05 09:44:28 -04:00
FCompactStateTreeState & BakedState = StateTree - > States [ i ] ;
2021-09-28 13:33:17 -04:00
UStateTreeState * SourceState = SourceStates [ i ] ;
2021-11-24 04:26:12 -05:00
check ( SourceState ! = nullptr ) ;
2021-09-28 13:33:17 -04:00
2021-10-29 04:33:21 -04:00
FStateTreeCompilerLogStateScope LogStateScope ( SourceState , Log ) ;
2021-09-28 13:33:17 -04:00
// Enter conditions.
2022-02-24 08:19:23 -05:00
BakedState . EnterConditionsBegin = uint16 ( StateTree - > Nodes . Num ( ) ) ;
2022-04-05 03:20:57 -04:00
if ( ! CreateConditions ( * SourceState , SourceState - > EnterConditions ) )
2021-09-28 13:33:17 -04:00
{
2022-03-28 04:48:50 -04:00
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to create state enter condition. " ) ) ;
return false ;
2021-09-28 13:33:17 -04:00
}
2022-02-24 08:19:23 -05:00
BakedState . EnterConditionsNum = uint8 ( uint16 ( StateTree - > Nodes . Num ( ) ) - BakedState . EnterConditionsBegin ) ;
2021-09-28 13:33:17 -04:00
2022-04-05 03:20:57 -04:00
// Linked state
if ( SourceState - > Type = = EStateTreeStateType : : Linked )
{
// Make sure the linked state is not self or parent to this state.
const UStateTreeState * LinkedParentState = nullptr ;
for ( const UStateTreeState * State = SourceState ; State ! = nullptr ; State = State - > Parent )
{
if ( State - > ID = = SourceState - > LinkedState . ID )
{
LinkedParentState = State ;
break ;
}
}
if ( LinkedParentState ! = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " State is linked to it's parent state '%s', which will create infinite loop. " ) ,
* LinkedParentState - > Name . ToString ( ) ) ;
return false ;
}
BakedState . LinkedState = GetStateHandle ( SourceState - > LinkedState . ID ) ;
if ( ! BakedState . LinkedState . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve linked state '%s'. " ) ,
* SourceState - > LinkedState . Name . ToString ( ) ) ;
return false ;
}
}
2021-09-28 13:33:17 -04:00
// Transitions
BakedState . TransitionsBegin = uint16 ( StateTree - > Transitions . Num ( ) ) ;
2021-10-21 04:08:20 -04:00
for ( FStateTreeTransition & Transition : SourceState - > Transitions )
2021-09-28 13:33:17 -04:00
{
2022-04-05 09:44:28 -04:00
FCompactStateTransition & BakedTransition = StateTree - > Transitions . AddDefaulted_GetRef ( ) ;
2021-09-28 13:33:17 -04:00
BakedTransition . Event = Transition . Event ;
BakedTransition . Type = Transition . State . Type ;
BakedTransition . GateDelay = ( uint8 ) FMath : : Clamp ( FMath : : CeilToInt ( Transition . GateDelay * 10.0f ) , 0 , 255 ) ;
BakedTransition . State = FStateTreeHandle : : Invalid ;
2021-10-29 04:33:21 -04:00
if ( ! ResolveTransitionState ( * SourceState , Transition . State , BakedTransition . State ) )
2021-09-28 13:33:17 -04:00
{
return false ;
}
// Note: Unset transition is allowed here. It can be used to mask a transition at parent.
2022-02-24 08:19:23 -05:00
BakedTransition . ConditionsBegin = uint16 ( StateTree - > Nodes . Num ( ) ) ;
2022-04-05 03:20:57 -04:00
if ( ! CreateConditions ( * SourceState , Transition . Conditions ) )
2021-09-28 13:33:17 -04:00
{
2022-03-28 04:48:50 -04:00
Log . Reportf ( EMessageSeverity : : Error ,
2022-04-05 03:20:57 -04:00
TEXT ( " Failed to create condition for transition to '%s'. " ) ,
2022-03-28 04:48:50 -04:00
* Transition . State . Name . ToString ( ) ) ;
return false ;
2021-09-28 13:33:17 -04:00
}
2022-02-24 08:19:23 -05:00
BakedTransition . ConditionsNum = uint8 ( uint16 ( StateTree - > Nodes . Num ( ) ) - BakedTransition . ConditionsBegin ) ;
2021-09-28 13:33:17 -04:00
}
BakedState . TransitionsNum = uint8 ( uint16 ( StateTree - > Transitions . Num ( ) ) - BakedState . TransitionsBegin ) ;
}
2021-10-29 04:33:21 -04:00
// @todo: Add test to check that all success/failure transition is possible (see editor).
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : ResolveTransitionState ( const UStateTreeState & SourceState , const FStateTreeStateLink & Link , FStateTreeHandle & OutTransitionHandle ) const
2021-10-29 04:33:21 -04:00
{
if ( Link . Type = = EStateTreeTransitionType : : GotoState )
{
OutTransitionHandle = GetStateHandle ( Link . ID ) ;
if ( ! OutTransitionHandle . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-04-05 03:20:57 -04:00
TEXT ( " Failed to resolve transition to state '%s'. " ) ,
2021-10-29 04:33:21 -04:00
* Link . Name . ToString ( ) ) ;
return false ;
}
}
else if ( Link . Type = = EStateTreeTransitionType : : NextState )
{
// Find next state.
const UStateTreeState * NextState = SourceState . GetNextSiblingState ( ) ;
if ( NextState = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve transition, there's no next state. " ) ) ;
return false ;
}
OutTransitionHandle = GetStateHandle ( NextState - > ID ) ;
if ( ! OutTransitionHandle . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-04-05 03:20:57 -04:00
TEXT ( " Failed to resolve transition next state, no handle found for '%s'. " ) ,
2021-10-29 04:33:21 -04:00
* NextState - > Name . ToString ( ) ) ;
return false ;
}
}
2021-09-28 13:33:17 -04:00
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateCondition ( UStateTreeState & State , const FStateTreeEditorNode & CondNode , const EStateTreeConditionOperand Operand , const int8 DeltaIndent )
2021-09-28 13:33:17 -04:00
{
2022-02-03 09:13:49 -05:00
if ( ! CondNode . Node . IsValid ( ) )
2021-09-28 13:33:17 -04:00
{
// Empty line in conditions array, just silently ignore.
return true ;
}
2021-11-24 04:26:12 -05:00
FStateTreeBindableStructDesc StructDesc ;
2022-02-03 09:13:49 -05:00
StructDesc . ID = CondNode . ID ;
StructDesc . Name = CondNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
2021-11-24 04:26:12 -05:00
// Check that item has valid instance initialized.
2022-02-03 09:13:49 -05:00
if ( ! CondNode . Instance . IsValid ( ) & & CondNode . InstanceObject = = nullptr )
2021-11-24 04:26:12 -05:00
{
Log . Reportf ( EMessageSeverity : : Error , StructDesc ,
TEXT ( " Malformed condition, missing instance value. " ) ) ;
return false ;
}
2021-09-28 13:33:17 -04:00
2021-11-12 05:48:11 -05:00
// Copy the condition
2022-02-24 08:19:23 -05:00
FInstancedStruct & Item = StateTree - > Nodes . AddDefaulted_GetRef ( ) ;
2022-02-03 09:13:49 -05:00
Item = CondNode . Node ;
2021-11-12 05:48:11 -05:00
FStateTreeConditionBase & Cond = Item . GetMutable < FStateTreeConditionBase > ( ) ;
2021-09-28 13:33:17 -04:00
2022-03-28 04:48:50 -04:00
Cond . Operand = Operand ;
Cond . DeltaIndent = DeltaIndent ;
2022-02-03 09:13:49 -05:00
if ( CondNode . Instance . IsValid ( ) )
2021-11-24 04:26:12 -05:00
{
// Struct instance
FInstancedStruct & Instance = StateTree - > Instances . AddDefaulted_GetRef ( ) ;
const int32 InstanceIndex = StateTree - > Instances . Num ( ) - 1 ;
2022-02-03 09:13:49 -05:00
Instance = CondNode . Instance ;
2021-11-24 04:26:12 -05:00
// Create binding source struct descriptor.
StructDesc . Struct = Instance . GetScriptStruct ( ) ;
2022-04-05 03:20:57 -04:00
StructDesc . Name = Cond . Name ;
2021-11-24 04:26:12 -05:00
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Cond . InstanceIndex = uint16 ( InstanceIndex ) ;
Cond . bInstanceIsObject = false ;
}
else
{
// Object Instance
2022-02-03 09:13:49 -05:00
check ( CondNode . InstanceObject ! = nullptr ) ;
2021-11-24 04:26:12 -05:00
2022-02-03 09:13:49 -05:00
UObject * Instance = DuplicateObject ( CondNode . InstanceObject , StateTree ) ;
2021-11-24 04:26:12 -05:00
StateTree - > InstanceObjects . Add ( Instance ) ;
const int32 InstanceIndex = StateTree - > InstanceObjects . Num ( ) - 1 ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
2022-04-05 03:20:57 -04:00
StructDesc . Name = Cond . Name ;
2021-11-24 04:26:12 -05:00
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Cond . InstanceIndex = uint16 ( InstanceIndex ) ;
Cond . bInstanceIsObject = true ;
}
2021-11-12 05:48:11 -05:00
// Mark the struct as binding source.
const int32 SourceStructIndex = BindingsCompiler . AddSourceStruct ( StructDesc ) ;
2021-09-28 13:33:17 -04:00
// Check that the bindings for this struct are still all valid.
TArray < FStateTreeEditorPropertyBinding > Bindings ;
2022-04-05 03:20:57 -04:00
if ( ! GetAndValidateBindings ( State , StructDesc , Bindings ) )
2021-09-28 13:33:17 -04:00
{
return false ;
}
// Compile batch copy for this struct, we pass in all the bindings, the compiler will pick up the ones for the target structs.
int32 BatchIndex = INDEX_NONE ;
if ( ! BindingsCompiler . CompileBatch ( StructDesc , Bindings , BatchIndex ) )
{
return false ;
}
check ( BatchIndex < int32 ( MAX_uint16 ) ) ;
Cond . BindingsBatch = BatchIndex = = INDEX_NONE ? FStateTreeHandle : : Invalid : FStateTreeHandle ( uint16 ( BatchIndex ) ) ;
2021-11-12 05:48:11 -05:00
check ( SourceStructIndex < = int32 ( MAX_uint16 ) ) ;
Cond . DataViewIndex = uint16 ( SourceStructIndex ) ;
2021-09-28 13:33:17 -04:00
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateTask ( UStateTreeState & State , const FStateTreeEditorNode & TaskNode )
2022-02-03 09:13:49 -05:00
{
// Silently ignore empty items.
if ( ! TaskNode . Node . IsValid ( ) )
{
return true ;
}
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = TaskNode . ID ;
StructDesc . Name = TaskNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
// Check that item has valid instance initialized.
if ( ! TaskNode . Instance . IsValid ( ) & & TaskNode . InstanceObject = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , StructDesc ,
2022-04-05 03:20:57 -04:00
TEXT ( " Malformed task, missing instance value. " ) ) ;
2022-02-03 09:13:49 -05:00
return false ;
}
// Copy the task
2022-02-24 08:19:23 -05:00
FInstancedStruct & Item = StateTree - > Nodes . AddDefaulted_GetRef ( ) ;
2022-02-03 09:13:49 -05:00
Item = TaskNode . Node ;
FStateTreeTaskBase & Task = Item . GetMutable < FStateTreeTaskBase > ( ) ;
if ( TaskNode . Instance . IsValid ( ) )
{
// Struct Instance
FInstancedStruct & Instance = StateTree - > Instances . AddDefaulted_GetRef ( ) ;
const int32 InstanceIndex = StateTree - > Instances . Num ( ) - 1 ;
Instance = TaskNode . Instance ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance . GetScriptStruct ( ) ;
StructDesc . Name = Task . Name ;
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Task . InstanceIndex = uint16 ( InstanceIndex ) ;
Task . bInstanceIsObject = false ;
}
else
{
// Object Instance
check ( TaskNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( TaskNode . InstanceObject , StateTree ) ;
StateTree - > InstanceObjects . Add ( Instance ) ;
const int32 InstanceIndex = StateTree - > InstanceObjects . Num ( ) - 1 ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
StructDesc . Name = Task . Name ;
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Task . InstanceIndex = uint16 ( InstanceIndex ) ;
Task . bInstanceIsObject = true ;
}
// Mark the instance as binding source.
const int32 SourceStructIndex = BindingsCompiler . AddSourceStruct ( StructDesc ) ;
// Check that the bindings for this struct are still all valid.
TArray < FStateTreeEditorPropertyBinding > Bindings ;
2022-04-05 03:20:57 -04:00
if ( ! GetAndValidateBindings ( State , StructDesc , Bindings ) )
2022-02-03 09:13:49 -05:00
{
return false ;
}
// Compile batch copy for this struct, we pass in all the bindings, the compiler will pick up the ones for the target structs.
int32 BatchIndex = INDEX_NONE ;
if ( ! BindingsCompiler . CompileBatch ( StructDesc , Bindings , BatchIndex ) )
{
return false ;
}
check ( BatchIndex < int32 ( MAX_uint16 ) ) ;
Task . BindingsBatch = BatchIndex = = INDEX_NONE ? FStateTreeHandle : : Invalid : FStateTreeHandle ( uint16 ( BatchIndex ) ) ;
check ( SourceStructIndex < = int32 ( MAX_uint16 ) ) ;
Task . DataViewIndex = uint16 ( SourceStructIndex ) ;
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : CreateEvaluator ( UStateTreeState & State , const FStateTreeEditorNode & EvalNode )
2022-02-03 09:13:49 -05:00
{
// Silently ignore empty items.
if ( ! EvalNode . Node . IsValid ( ) )
{
return true ;
}
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = EvalNode . ID ;
StructDesc . Name = EvalNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
// Check that item has valid instance initialized.
if ( ! EvalNode . Instance . IsValid ( ) & & EvalNode . InstanceObject = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , StructDesc ,
TEXT ( " Malformed evaluator, missing instance value. " ) ) ;
return false ;
}
// Copy the evaluator
2022-02-24 08:19:23 -05:00
FInstancedStruct & Item = StateTree - > Nodes . AddDefaulted_GetRef ( ) ;
2022-02-03 09:13:49 -05:00
Item = EvalNode . Node ;
FStateTreeEvaluatorBase & Eval = Item . GetMutable < FStateTreeEvaluatorBase > ( ) ;
if ( EvalNode . Instance . IsValid ( ) )
{
// Struct Instance
FInstancedStruct & Instance = StateTree - > Instances . AddDefaulted_GetRef ( ) ;
const int32 InstanceIndex = StateTree - > Instances . Num ( ) - 1 ;
Instance = EvalNode . Instance ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance . GetScriptStruct ( ) ;
StructDesc . Name = Eval . Name ;
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Eval . InstanceIndex = uint16 ( InstanceIndex ) ;
Eval . bInstanceIsObject = false ;
}
else
{
// Object Instance
check ( EvalNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( EvalNode . InstanceObject , StateTree ) ;
StateTree - > InstanceObjects . Add ( Instance ) ;
const int32 InstanceIndex = StateTree - > InstanceObjects . Num ( ) - 1 ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
StructDesc . Name = Eval . Name ;
check ( InstanceIndex < = int32 ( MAX_uint16 ) ) ;
Eval . InstanceIndex = uint16 ( InstanceIndex ) ;
Eval . bInstanceIsObject = true ;
}
// Mark the instance as binding source.
const int32 SourceStructIndex = BindingsCompiler . AddSourceStruct ( StructDesc ) ;
// Check that the bindings for this struct are still all valid.
TArray < FStateTreeEditorPropertyBinding > Bindings ;
2022-04-05 03:20:57 -04:00
if ( ! GetAndValidateBindings ( State , StructDesc , Bindings ) )
2022-02-03 09:13:49 -05:00
{
return false ;
}
// Compile batch copy for this struct, we pass in all the bindings, the compiler will pick up the ones for the target structs.
int32 BatchIndex = INDEX_NONE ;
if ( ! BindingsCompiler . CompileBatch ( StructDesc , Bindings , BatchIndex ) )
{
return false ;
}
check ( BatchIndex < int32 ( MAX_uint16 ) ) ;
Eval . BindingsBatch = BatchIndex = = INDEX_NONE ? FStateTreeHandle : : Invalid : FStateTreeHandle ( uint16 ( BatchIndex ) ) ;
check ( SourceStructIndex < = int32 ( MAX_uint16 ) ) ;
Eval . DataViewIndex = uint16 ( SourceStructIndex ) ;
return true ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : IsPropertyAnyEnum ( const FStateTreeBindableStructDesc & Struct , FStateTreeEditorPropertyPath Path ) const
2021-09-28 13:33:17 -04:00
{
bool bIsAnyEnum = false ;
TArray < FStateTreePropertySegment > Segments ;
2021-10-29 04:33:21 -04:00
const FProperty * LeafProperty = nullptr ;
2021-09-28 13:33:17 -04:00
int32 LeafArrayIndex = INDEX_NONE ;
const bool bResolved = FStateTreePropertyBindingCompiler : : ResolvePropertyPath ( Struct , Path , Segments , LeafProperty , LeafArrayIndex ) ;
if ( bResolved & & LeafProperty )
{
2021-10-29 04:33:21 -04:00
if ( const FProperty * OwnerProperty = LeafProperty - > GetOwnerProperty ( ) )
2021-09-28 13:33:17 -04:00
{
if ( const FStructProperty * OwnerStructProperty = CastField < FStructProperty > ( OwnerProperty ) )
{
bIsAnyEnum = OwnerStructProperty - > Struct = = FStateTreeAnyEnum : : StaticStruct ( ) ;
}
}
}
return bIsAnyEnum ;
}
2022-04-05 09:44:28 -04:00
bool FStateTreeCompiler : : GetAndValidateBindings ( UStateTreeState & State , const FStateTreeBindableStructDesc & TargetStruct , TArray < FStateTreeEditorPropertyBinding > & OutBindings ) const
2021-09-28 13:33:17 -04:00
{
OutBindings . Reset ( ) ;
for ( const FStateTreeEditorPropertyBinding & Binding : TreeData - > EditorBindings . GetBindings ( ) )
{
if ( Binding . TargetPath . StructID ! = TargetStruct . ID )
{
continue ;
}
// Source must be one of the source structs we have discovered in the tree.
const FGuid SourceStructID = Binding . SourcePath . StructID ;
const int32 SourceStructIdx = BindingsCompiler . GetSourceStructIndexByID ( SourceStructID ) ;
if ( SourceStructIdx = = INDEX_NONE )
{
2021-10-29 04:33:21 -04:00
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2022-04-05 03:20:57 -04:00
TEXT ( " Failed to find binding source '%s:%s'. " ) ,
* TargetStruct . Name . ToString ( ) , * Binding . TargetPath . ToString ( ) ) ;
2021-09-28 13:33:17 -04:00
return false ;
}
const FStateTreeBindableStructDesc & SourceStruct = BindingsCompiler . GetSourceStructDesc ( SourceStructIdx ) ;
2022-04-05 03:20:57 -04:00
// Source must be accessible by the target struct via all execution paths.
2021-09-28 13:33:17 -04:00
TArray < FStateTreeBindableStructDesc > AccessibleStructs ;
2022-04-05 03:20:57 -04:00
const FStateExecutionInfo & ExecInfo = ExecutionInfos . FindChecked ( & State ) ;
for ( const FExecutionPath & ExecPath : ExecInfo . ExecutionPaths )
2021-09-28 13:33:17 -04:00
{
2022-04-05 03:20:57 -04:00
AccessibleStructs . Reset ( ) ;
TreeData - > GetAccessibleStructs ( ExecPath . Path , Binding . TargetPath . StructID , AccessibleStructs ) ;
2021-09-28 13:33:17 -04:00
2022-04-05 03:20:57 -04:00
const bool SourceAccessible = AccessibleStructs . ContainsByPredicate ( [ SourceStructID ] ( const FStateTreeBindableStructDesc & Structs )
{
return ( Structs . ID = = SourceStructID ) ;
} ) ;
if ( ! SourceAccessible )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Property '%s:%s' cannot be bound to '%s:%s', because the binding source '%s' is not updated before '%s' in the tree. " ) ,
* SourceStruct . Name . ToString ( ) , * Binding . SourcePath . ToString ( ) ,
* TargetStruct . Name . ToString ( ) , * Binding . TargetPath . ToString ( ) ,
* SourceStruct . Name . ToString ( ) , * TargetStruct . Name . ToString ( ) ) ;
if ( IsPathLinked ( ExecPath . Path ) )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " The binding source is not updated when executing via linked state: %s. " ) ,
* GetExecutionPathString ( ExecPath . Path ) ) ;
}
return false ;
}
}
2021-09-28 13:33:17 -04:00
// Special case fo AnyEnum. StateTreeBindingExtension allows AnyEnums to bind to other enum types.
// The actual copy will be done via potential type promotion copy, into the value property inside the AnyEnum.
// We amend the paths here to point to the 'Value' property.
const bool bSourceIsAnyEnum = IsPropertyAnyEnum ( SourceStruct , Binding . SourcePath ) ;
const bool bTargetIsAnyEnum = IsPropertyAnyEnum ( TargetStruct , Binding . TargetPath ) ;
if ( bSourceIsAnyEnum | | bTargetIsAnyEnum )
{
FStateTreeEditorPropertyBinding ModifiedBinding ( Binding ) ;
if ( bSourceIsAnyEnum )
{
ModifiedBinding . SourcePath . Path . Add ( GET_MEMBER_NAME_STRING_CHECKED ( FStateTreeAnyEnum , Value ) ) ;
}
if ( bTargetIsAnyEnum )
{
ModifiedBinding . TargetPath . Path . Add ( GET_MEMBER_NAME_STRING_CHECKED ( FStateTreeAnyEnum , Value ) ) ;
}
OutBindings . Add ( ModifiedBinding ) ;
}
else
{
OutBindings . Add ( Binding ) ;
}
}
return true ;
}