2022-05-31 04:51:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeCompiler.h"
# include "StateTree.h"
# include "StateTreeEditorData.h"
# include "StateTreeTypes.h"
2022-08-23 13:04:52 -04:00
# include "Conditions/StateTreeCommonConditions.h"
2022-05-31 04:51:18 -04:00
# include "StateTreeEvaluatorBase.h"
# include "StateTreeTaskBase.h"
# include "StateTreeConditionBase.h"
# include "StateTreeState.h"
# include "StateTreeExecutionContext.h"
# include "StateTreePropertyBindingCompiler.h"
namespace UE : : StateTree : : Compiler
{
void FValidationResult : : Log ( FStateTreeCompilerLog & Log , const TCHAR * ContextText , const FStateTreeBindableStructDesc & ContextStruct ) const
{
Log . Reportf ( EMessageSeverity : : Error , ContextStruct , TEXT ( " The StateTree is too complex. Compact index %s out of range %d/%d. " ) , ContextText , Value , MaxValue ) ;
}
2022-06-29 04:52:18 -04:00
const UScriptStruct * GetBaseStructFromMetaData ( const FProperty * Property , FString & OutBaseStructName )
{
static const FName NAME_BaseStruct = " BaseStruct " ;
const UScriptStruct * Result = nullptr ;
OutBaseStructName = Property - > GetMetaData ( NAME_BaseStruct ) ;
if ( ! OutBaseStructName . IsEmpty ( ) )
{
Result = UClass : : TryFindTypeSlow < UScriptStruct > ( OutBaseStructName ) ;
if ( ! Result )
{
Result = LoadObject < UScriptStruct > ( nullptr , * OutBaseStructName ) ;
}
}
return Result ;
}
2022-05-31 04:51:18 -04:00
} ; // UE::StateTree::Compiler
bool FStateTreeCompiler : : Compile ( UStateTree & InStateTree )
{
StateTree = & InStateTree ;
TreeData = Cast < UStateTreeEditorData > ( StateTree - > EditorData ) ;
if ( ! TreeData )
{
return false ;
}
// Cleanup existing state
StateTree - > ResetCompiled ( ) ;
if ( ! BindingsCompiler . Init ( StateTree - > PropertyBindings , Log ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
// Copy schema the EditorData
StateTree - > Schema = DuplicateObject ( TreeData - > Schema , StateTree ) ;
// Copy parameters from EditorData
StateTree - > Parameters = TreeData - > RootParameters . Parameters ;
// Mark parameters as binding source
const FStateTreeBindableStructDesc ParametersDesc = {
TEXT ( " Parameters " ) ,
StateTree - > Parameters . GetPropertyBagStruct ( ) ,
EStateTreeBindableStructSource : : TreeParameter ,
TreeData - > RootParameters . ID
} ;
const int32 ParametersDataViewIndex = BindingsCompiler . AddSourceStruct ( ParametersDesc ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex8 ( ParametersDataViewIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ParametersDataViewIndex " ) , ParametersDesc ) ;
return false ;
}
StateTree - > ParametersDataViewIndex = FStateTreeIndex8 ( ParametersDataViewIndex ) ;
// Mark all named external values as binding source
if ( StateTree - > Schema )
{
StateTree - > NamedExternalDataDescs = StateTree - > Schema - > GetNamedExternalDataDescs ( ) ;
for ( FStateTreeExternalDataDesc & Desc : StateTree - > NamedExternalDataDescs )
{
const FStateTreeBindableStructDesc ExtDataDesc = {
Desc . Name ,
Desc . Struct ,
EStateTreeBindableStructSource : : TreeData ,
Desc . ID
} ;
const int32 ExternalStructIndex = BindingsCompiler . AddSourceStruct ( ExtDataDesc ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex8 ( ExternalStructIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ExternalStructIndex " ) , ParametersDesc ) ;
return false ;
}
Desc . Handle . DataViewIndex = FStateTreeIndex8 ( ExternalStructIndex ) ;
}
}
if ( ! CreateStates ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
if ( ! CreateEvaluators ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
if ( ! CreateStateTasksAndParameters ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
if ( ! CreateStateTransitions ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
StateTree - > Nodes = Nodes ;
StateTree - > DefaultInstanceData . Init ( * StateTree , InstanceStructs , InstanceObjects ) ;
StateTree - > SharedInstanceData . Init ( * StateTree , SharedInstanceStructs , SharedInstanceObjects ) ;
BindingsCompiler . Finalize ( ) ;
if ( ! StateTree - > Link ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
return true ;
}
FStateTreeStateHandle FStateTreeCompiler : : GetStateHandle ( const FGuid & StateID ) const
{
const int32 * Idx = IDToState . Find ( StateID ) ;
if ( Idx = = nullptr )
{
return FStateTreeStateHandle : : Invalid ;
}
return FStateTreeStateHandle ( uint16 ( * Idx ) ) ;
}
UStateTreeState * FStateTreeCompiler : : GetState ( const FGuid & StateID )
{
const int32 * Idx = IDToState . Find ( StateID ) ;
if ( Idx = = nullptr )
{
return nullptr ;
}
return SourceStates [ * Idx ] ;
}
bool FStateTreeCompiler : : CreateStates ( )
{
// Create item for the runtime execution state
InstanceStructs . Add ( FInstancedStruct : : Make < FStateTreeExecutionState > ( ) ) ;
// Create main tree (omit subtrees)
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
{
if ( SubTree ! = nullptr )
{
if ( ! CreateStateRecursive ( * SubTree , FStateTreeStateHandle : : Invalid ) )
{
return false ;
}
}
}
// Create Subtrees
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
{
TArray < UStateTreeState * > Stack ;
Stack . Push ( SubTree ) ;
while ( ! Stack . IsEmpty ( ) )
{
if ( UStateTreeState * State = Stack . Pop ( ) )
{
if ( State - > Type = = EStateTreeStateType : : Subtree )
{
if ( ! CreateStateRecursive ( * State , FStateTreeStateHandle : : Invalid ) )
{
return false ;
}
}
Stack . Append ( State - > Children ) ;
}
}
}
return true ;
}
bool FStateTreeCompiler : : CreateStateRecursive ( UStateTreeState & State , const FStateTreeStateHandle Parent )
{
FStateTreeCompilerLogStateScope LogStateScope ( & State , Log ) ;
const int32 StateIdx = StateTree - > States . AddDefaulted ( ) ;
FCompactStateTreeState & CompactState = StateTree - > States [ StateIdx ] ;
CompactState . Name = State . Name ;
CompactState . Parent = Parent ;
CompactState . Type = State . Type ;
SourceStates . Add ( & State ) ;
IDToState . Add ( State . ID , StateIdx ) ;
// Child states
const int32 ChildrenBegin = StateTree - > States . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( ChildrenBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ChildrenBegin " ) ) ;
return false ;
}
CompactState . ChildrenBegin = uint16 ( ChildrenBegin ) ;
for ( UStateTreeState * Child : State . Children )
{
if ( Child ! = nullptr & & Child - > Type ! = EStateTreeStateType : : Subtree )
{
if ( ! CreateStateRecursive ( * Child , FStateTreeStateHandle ( ( uint16 ) StateIdx ) ) )
{
return false ;
}
}
}
const int32 ChildrenEnd = StateTree - > States . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( ChildrenEnd ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ChildrenEnd " ) ) ;
return false ;
}
StateTree - > States [ StateIdx ] . ChildrenEnd = uint16 ( ChildrenEnd ) ;
return true ;
}
bool FStateTreeCompiler : : CreateConditions ( UStateTreeState & State , TConstArrayView < FStateTreeEditorNode > Conditions )
{
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 : FMath : : Clamp ( ( int32 ) CondNode . ConditionIndent , 0 , UE : : StateTree : : MaxConditionIndent ) ;
// Next indent, or terminate at zero.
const int32 NextIndent = Conditions . IsValidIndex ( Index + 1 ) ? FMath : : Clamp ( ( int32 ) Conditions [ Index ] . ConditionIndent , 0 , UE : : StateTree : : MaxConditionIndent ) : 0 ;
const int32 DeltaIndent = NextIndent - CurrIndent ;
if ( ! CreateCondition ( State , CondNode , Operand , ( int8 ) DeltaIndent ) )
{
return false ;
}
}
return true ;
}
bool FStateTreeCompiler : : CreateEvaluators ( )
{
const int32 EvaluatorsBegin = Nodes . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( EvaluatorsBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " EvaluatorsBegin " ) ) ;
return false ;
}
StateTree - > EvaluatorsBegin = uint16 ( EvaluatorsBegin ) ;
for ( FStateTreeEditorNode & EvalNode : TreeData - > Evaluators )
{
if ( ! CreateEvaluator ( EvalNode ) )
{
return false ;
}
}
const int32 EvaluatorsNum = Nodes . Num ( ) - EvaluatorsBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( EvaluatorsNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " EvaluatorsNum " ) ) ;
return false ;
}
StateTree - > EvaluatorsNum = uint16 ( EvaluatorsNum ) ;
return true ;
}
bool FStateTreeCompiler : : CreateStateTasksAndParameters ( )
{
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
FCompactStateTreeState & CompactState = StateTree - > States [ i ] ;
UStateTreeState * SourceState = SourceStates [ i ] ;
check ( SourceState ! = nullptr ) ;
FStateTreeCompilerLogStateScope LogStateScope ( SourceState , Log ) ;
// Create parameters
if ( SourceState - > Type = = EStateTreeStateType : : Linked | | SourceState - > Type = = EStateTreeStateType : : Subtree )
{
// Both linked and subtree has instance data describing their parameters.
// This allows to resolve the binding paths and lets us have bindable parameters when transitioned into a parameterized subtree directly.
FInstancedStruct & Instance = InstanceStructs . AddDefaulted_GetRef ( ) ;
const int32 InstanceIndex = InstanceStructs . Num ( ) - 1 ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) ) ;
return false ;
}
CompactState . ParameterInstanceIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Instance . InitializeAs < FCompactStateTreeParameters > ( ) ;
FCompactStateTreeParameters & CompactParams = Instance . GetMutable < FCompactStateTreeParameters > ( ) ;
CompactParams . Parameters = SourceState - > Parameters . Parameters ;
if ( SourceState - > Type = = EStateTreeStateType : : Subtree )
{
// Register a binding source
const FStateTreeBindableStructDesc SubtreeParamsDesc = {
SourceState - > Name ,
SourceState - > Parameters . Parameters . GetPropertyBagStruct ( ) ,
EStateTreeBindableStructSource : : StateParameter ,
SourceState - > Parameters . ID
} ;
const int32 SourceStructIndex = BindingsCompiler . AddSourceStruct ( SubtreeParamsDesc ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( SourceStructIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " SourceStructIndex " ) , SubtreeParamsDesc ) ;
return false ;
}
CompactState . ParameterDataViewIndex = FStateTreeIndex16 ( SourceStructIndex ) ;
}
else if ( SourceState - > Type = = EStateTreeStateType : : Linked )
{
// Binding target
FStateTreeBindableStructDesc LinkedParamsDesc = {
SourceState - > Name ,
SourceState - > Parameters . Parameters . GetPropertyBagStruct ( ) ,
EStateTreeBindableStructSource : : StateParameter ,
SourceState - > Parameters . ID
} ;
// Check that the bindings for this struct are still all valid.
TArray < FStateTreeEditorPropertyBinding > Bindings ;
if ( ! GetAndValidateBindings ( LinkedParamsDesc , Bindings ) )
{
return false ;
}
int32 BatchIndex = INDEX_NONE ;
if ( ! BindingsCompiler . CompileBatch ( LinkedParamsDesc , Bindings , BatchIndex ) )
{
return false ;
}
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( BatchIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " BatchIndex " ) , LinkedParamsDesc ) ;
return false ;
}
CompactParams . BindingsBatch = FStateTreeIndex16 ( BatchIndex ) ;
}
}
// Create tasks
const int32 TasksBegin = Nodes . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( TasksBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " TasksBegin " ) ) ;
return false ;
}
CompactState . TasksBegin = uint16 ( TasksBegin ) ;
for ( FStateTreeEditorNode & TaskNode : SourceState - > Tasks )
{
if ( ! CreateTask ( * SourceState , TaskNode ) )
{
return false ;
}
}
if ( ! CreateTask ( * SourceState , SourceState - > SingleTask ) )
{
return false ;
}
const int32 TasksNum = Nodes . Num ( ) - TasksBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( TasksNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " TasksNum " ) ) ;
return false ;
}
CompactState . TasksNum = uint8 ( TasksNum ) ;
}
return true ;
}
bool FStateTreeCompiler : : CreateStateTransitions ( )
{
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
FCompactStateTreeState & CompactState = StateTree - > States [ i ] ;
UStateTreeState * SourceState = SourceStates [ i ] ;
check ( SourceState ! = nullptr ) ;
FStateTreeCompilerLogStateScope LogStateScope ( SourceState , Log ) ;
// Enter conditions.
const int32 EnterConditionsBegin = Nodes . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( EnterConditionsBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " EnterConditionsBegin " ) ) ;
return false ;
}
CompactState . EnterConditionsBegin = uint16 ( EnterConditionsBegin ) ;
if ( ! CreateConditions ( * SourceState , SourceState - > EnterConditions ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to create state enter condition. " ) ) ;
return false ;
}
const int32 EnterConditionsNum = Nodes . Num ( ) - EnterConditionsBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( EnterConditionsNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " EnterConditionsNum " ) ) ;
return false ;
}
CompactState . EnterConditionsNum = uint8 ( EnterConditionsNum ) ;
// 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 ;
}
2022-07-21 08:19:56 -04:00
// The linked state must be a subtree.
const UStateTreeState * TargetState = GetState ( SourceState - > LinkedState . ID ) ;
if ( TargetState = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve linked state '%s'. " ) ,
* SourceState - > LinkedState . Name . ToString ( ) ) ;
return false ;
}
if ( TargetState - > Type ! = EStateTreeStateType : : Subtree )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " State '%s' is linked to state '%s', which is not a subtree. " ) ,
* SourceState - > Name . ToString ( ) , * TargetState - > Name . ToString ( ) ) ;
return false ;
}
2022-05-31 04:51:18 -04:00
CompactState . LinkedState = GetStateHandle ( SourceState - > LinkedState . ID ) ;
if ( ! CompactState . LinkedState . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve linked state '%s'. " ) ,
* SourceState - > LinkedState . Name . ToString ( ) ) ;
return false ;
}
}
// Transitions
const int32 TransitionsBegin = StateTree - > Transitions . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( TransitionsBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " TransitionsBegin " ) ) ;
return false ;
}
CompactState . TransitionsBegin = uint16 ( TransitionsBegin ) ;
for ( FStateTreeTransition & Transition : SourceState - > Transitions )
{
FCompactStateTransition & CompactTransition = StateTree - > Transitions . AddDefaulted_GetRef ( ) ;
2022-09-01 09:06:53 -04:00
CompactTransition . Trigger = Transition . Trigger ;
CompactTransition . EventTag = Transition . EventTag ;
2022-05-31 04:51:18 -04:00
CompactTransition . Type = Transition . State . Type ;
CompactTransition . GateDelay = ( uint8 ) FMath : : Clamp ( FMath : : CeilToInt ( Transition . GateDelay * 10.0f ) , 0 , 255 ) ;
CompactTransition . State = FStateTreeStateHandle : : Invalid ;
if ( ! ResolveTransitionState ( * SourceState , Transition . State , CompactTransition . State ) )
{
return false ;
}
// Note: Unset transition is allowed here. It can be used to mask a transition at parent.
const int32 ConditionsBegin = Nodes . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( ConditionsBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ConditionsBegin " ) ) ;
return false ;
}
CompactTransition . ConditionsBegin = uint16 ( ConditionsBegin ) ;
if ( ! CreateConditions ( * SourceState , Transition . Conditions ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to create condition for transition to '%s'. " ) ,
* Transition . State . Name . ToString ( ) ) ;
return false ;
}
const int32 ConditionsNum = Nodes . Num ( ) - ConditionsBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( ConditionsNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " ConditionsNum " ) ) ;
return false ;
}
CompactTransition . ConditionsNum = uint8 ( ConditionsNum ) ;
}
const int32 TransitionsNum = StateTree - > Transitions . Num ( ) - TransitionsBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( TransitionsNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " TransitionsNum " ) ) ;
return false ;
}
CompactState . TransitionsNum = uint8 ( TransitionsNum ) ;
}
// @todo: Add test to check that all success/failure transition is possible (see editor).
return true ;
}
bool FStateTreeCompiler : : ResolveTransitionState ( const UStateTreeState & SourceState , const FStateTreeStateLink & Link , FStateTreeStateHandle & OutTransitionHandle ) const
{
if ( Link . Type = = EStateTreeTransitionType : : GotoState )
{
OutTransitionHandle = GetStateHandle ( Link . ID ) ;
if ( ! OutTransitionHandle . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve transition to state '%s'. " ) ,
* 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 ,
TEXT ( " Failed to resolve transition next state, no handle found for '%s'. " ) ,
* NextState - > Name . ToString ( ) ) ;
return false ;
}
}
return true ;
}
bool FStateTreeCompiler : : CreateCondition ( UStateTreeState & State , const FStateTreeEditorNode & CondNode , const EStateTreeConditionOperand Operand , const int8 DeltaIndent )
{
if ( ! CondNode . Node . IsValid ( ) )
{
// Empty line in conditions array, just silently ignore.
return true ;
}
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = CondNode . ID ;
StructDesc . Name = CondNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
StructDesc . DataSource = EStateTreeBindableStructSource : : Condition ;
// Check that item has valid instance initialized.
if ( ! CondNode . Instance . IsValid ( ) & & CondNode . InstanceObject = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , StructDesc ,
TEXT ( " Malformed condition, missing instance value. " ) ) ;
return false ;
}
// Copy the condition
const FInstancedStruct & Node = Nodes . Add_GetRef ( CondNode . Node ) ;
FStateTreeConditionBase & Cond = Node . GetMutable < FStateTreeConditionBase > ( ) ;
Cond . Operand = Operand ;
Cond . DeltaIndent = DeltaIndent ;
if ( CondNode . Instance . IsValid ( ) )
{
// Struct instance
const int32 InstanceIndex = SharedInstanceStructs . Add ( CondNode . Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = CondNode . Instance . GetScriptStruct ( ) ;
StructDesc . Name = Cond . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Cond . InstanceIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Cond . bInstanceIsObject = false ;
}
else
{
// Object Instance
check ( CondNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( CondNode . InstanceObject , StateTree ) ;
const int32 InstanceIndex = SharedInstanceObjects . Add ( Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
StructDesc . Name = Cond . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Cond . InstanceIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Cond . bInstanceIsObject = true ;
}
// Mark the struct as binding source.
const int32 SourceStructIndex = BindingsCompiler . AddSourceStruct ( StructDesc ) ;
// Check that the bindings for this struct are still all valid.
TArray < FStateTreeEditorPropertyBinding > Bindings ;
if ( ! GetAndValidateBindings ( StructDesc , Bindings ) )
{
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 ;
}
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( BatchIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " BatchIndex " ) , StructDesc ) ;
return false ;
}
Cond . BindingsBatch = FStateTreeIndex16 ( BatchIndex ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( SourceStructIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " SourceStructIndex " ) , StructDesc ) ;
return false ;
}
Cond . DataViewIndex = FStateTreeIndex16 ( SourceStructIndex ) ;
return true ;
}
bool FStateTreeCompiler : : CreateTask ( UStateTreeState & State , const FStateTreeEditorNode & TaskNode )
{
// Silently ignore empty nodes.
if ( ! TaskNode . Node . IsValid ( ) )
{
return true ;
}
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = TaskNode . ID ;
StructDesc . Name = TaskNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
StructDesc . DataSource = EStateTreeBindableStructSource : : Task ;
// Check that node has valid instance initialized.
if ( ! TaskNode . Instance . IsValid ( ) & & TaskNode . InstanceObject = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , StructDesc ,
TEXT ( " Malformed task, missing instance value. " ) ) ;
return false ;
}
// Copy the task
const FInstancedStruct & Node = Nodes . Add_GetRef ( TaskNode . Node ) ;
FStateTreeTaskBase & Task = Node . GetMutable < FStateTreeTaskBase > ( ) ;
if ( TaskNode . Instance . IsValid ( ) )
{
// Struct Instance
const int32 InstanceIndex = InstanceStructs . Add ( TaskNode . Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = TaskNode . Instance . GetScriptStruct ( ) ;
StructDesc . Name = Task . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Task . InstanceIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Task . bInstanceIsObject = false ;
}
else
{
// Object Instance
check ( TaskNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( TaskNode . InstanceObject , StateTree ) ;
const int32 InstanceIndex = InstanceObjects . Add ( Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
StructDesc . Name = Task . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Task . InstanceIndex = FStateTreeIndex16 ( 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 ;
if ( ! GetAndValidateBindings ( StructDesc , Bindings ) )
{
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 ;
}
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( BatchIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " BatchIndex " ) , StructDesc ) ;
return false ;
}
Task . BindingsBatch = FStateTreeIndex16 ( BatchIndex ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( SourceStructIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " SourceStructIndex " ) , StructDesc ) ;
return false ;
}
Task . DataViewIndex = FStateTreeIndex16 ( SourceStructIndex ) ;
return true ;
}
bool FStateTreeCompiler : : CreateEvaluator ( const FStateTreeEditorNode & EvalNode )
{
// Silently ignore empty nodes.
if ( ! EvalNode . Node . IsValid ( ) )
{
return true ;
}
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = EvalNode . ID ;
StructDesc . Name = EvalNode . Node . GetScriptStruct ( ) - > GetFName ( ) ;
StructDesc . DataSource = EStateTreeBindableStructSource : : Evaluator ;
// Check that node 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
const FInstancedStruct & Node = Nodes . Add_GetRef ( EvalNode . Node ) ;
FStateTreeEvaluatorBase & Eval = Node . GetMutable < FStateTreeEvaluatorBase > ( ) ;
if ( EvalNode . Instance . IsValid ( ) )
{
// Struct Instance
const int32 InstanceIndex = InstanceStructs . Add ( EvalNode . Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = EvalNode . Instance . GetScriptStruct ( ) ;
StructDesc . Name = Eval . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Eval . InstanceIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Eval . bInstanceIsObject = false ;
}
else
{
// Object Instance
check ( EvalNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( EvalNode . InstanceObject , StateTree ) ;
const int32 InstanceIndex = InstanceObjects . Add ( Instance ) ;
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
StructDesc . Name = Eval . Name ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
Eval . InstanceIndex = FStateTreeIndex16 ( 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 ;
if ( ! GetAndValidateBindings ( StructDesc , Bindings ) )
{
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 ;
}
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( BatchIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " BatchIndex " ) , StructDesc ) ;
return false ;
}
Eval . BindingsBatch = FStateTreeIndex16 ( BatchIndex ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( SourceStructIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " SourceStructIndex " ) , StructDesc ) ;
return false ;
}
Eval . DataViewIndex = FStateTreeIndex16 ( SourceStructIndex ) ;
return true ;
}
bool FStateTreeCompiler : : IsPropertyAnyEnum ( const FStateTreeBindableStructDesc & Struct , FStateTreeEditorPropertyPath Path ) const
{
bool bIsAnyEnum = false ;
TArray < FStateTreePropertySegment > Segments ;
const FProperty * LeafProperty = nullptr ;
int32 LeafArrayIndex = INDEX_NONE ;
const bool bResolved = FStateTreePropertyBindingCompiler : : ResolvePropertyPath ( Struct , Path , Segments , LeafProperty , LeafArrayIndex ) ;
if ( bResolved & & LeafProperty )
{
if ( const FProperty * OwnerProperty = LeafProperty - > GetOwnerProperty ( ) )
{
if ( const FStructProperty * OwnerStructProperty = CastField < FStructProperty > ( OwnerProperty ) )
{
2022-06-29 04:52:18 -04:00
bIsAnyEnum = OwnerStructProperty - > Struct = = TBaseStructure < FStateTreeAnyEnum > : : Get ( ) ;
2022-05-31 04:51:18 -04:00
}
}
}
return bIsAnyEnum ;
}
2022-06-29 04:52:18 -04:00
bool FStateTreeCompiler : : ValidateStructRef ( const FStateTreeBindableStructDesc & SourceStruct , FStateTreeEditorPropertyPath SourcePath ,
const FStateTreeBindableStructDesc & TargetStruct , FStateTreeEditorPropertyPath TargetPath ) const
{
TArray < FStateTreePropertySegment > Segments ;
const FProperty * TargetLeafProperty = nullptr ;
int32 TargetLeafArrayIndex = INDEX_NONE ;
if ( FStateTreePropertyBindingCompiler : : ResolvePropertyPath ( TargetStruct , TargetPath , Segments , TargetLeafProperty , TargetLeafArrayIndex ) = = false )
{
// This will later be reported by the bindings compiler.
return true ;
}
// Early out if the target is not FStateTreeStructRef.
const FStructProperty * TargetStructProperty = CastField < FStructProperty > ( TargetLeafProperty ) ;
if ( TargetStructProperty = = nullptr | | TargetStructProperty - > Struct ! = TBaseStructure < FStateTreeStructRef > : : Get ( ) )
{
return true ;
}
FString TargetBaseStructName ;
const UScriptStruct * TargetBaseStruct = UE : : StateTree : : Compiler : : GetBaseStructFromMetaData ( TargetStructProperty , TargetBaseStructName ) ;
if ( TargetBaseStruct = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Could not find base struct '%s' for target '%s:%s'. " ) ,
* TargetBaseStructName , * TargetStruct . Name . ToString ( ) , * TargetPath . ToString ( ) ) ;
return false ;
}
const FProperty * SourceLeafProperty = nullptr ;
int32 SourceLeafArrayIndex = INDEX_NONE ;
if ( FStateTreePropertyBindingCompiler : : ResolvePropertyPath ( SourceStruct , SourcePath , Segments , SourceLeafProperty , SourceLeafArrayIndex ) = = false )
{
// This will later be reported by the bindings compiler.
return true ;
}
// Exit if the source is not a struct property.
const FStructProperty * SourceStructProperty = CastField < FStructProperty > ( SourceLeafProperty ) ;
if ( SourceStructProperty = = nullptr )
{
return true ;
}
if ( SourceStructProperty - > Struct = = TBaseStructure < FStateTreeStructRef > : : Get ( ) )
{
// Source is struct ref too, check the types match.
FString SourceBaseStructName ;
const UScriptStruct * SourceBaseStruct = UE : : StateTree : : Compiler : : GetBaseStructFromMetaData ( SourceStructProperty , SourceBaseStructName ) ;
if ( SourceBaseStruct = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Could not find base struct '%s' for bidning source '%s:%s'. " ) ,
* SourceBaseStructName , * SourceStruct . Name . ToString ( ) , * SourcePath . ToString ( ) ) ;
return false ;
}
if ( SourceBaseStruct - > IsChildOf ( TargetBaseStruct ) = = false )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Type mismatch between source '%s:%s' and target '%s:%s' types, '%s' is not child of '%s'. " ) ,
* SourceStruct . Name . ToString ( ) , * SourcePath . ToString ( ) ,
* TargetStruct . Name . ToString ( ) , * TargetPath . ToString ( ) ,
* GetNameSafe ( SourceBaseStruct ) , * GetNameSafe ( TargetBaseStruct ) ) ;
return false ;
}
}
else
{
if ( ! SourceStructProperty - > Struct | | SourceStructProperty - > Struct - > IsChildOf ( TargetBaseStruct ) = = false )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Type mismatch between source '%s:%s' and target '%s:%s' types, '%s' is not child of '%s'. " ) ,
* SourceStruct . Name . ToString ( ) , * SourcePath . ToString ( ) ,
* TargetStruct . Name . ToString ( ) , * TargetPath . ToString ( ) ,
* GetNameSafe ( SourceStructProperty - > Struct ) , * GetNameSafe ( TargetBaseStruct ) ) ;
return false ;
}
}
return true ;
}
2022-05-31 04:51:18 -04:00
bool FStateTreeCompiler : : GetAndValidateBindings ( const FStateTreeBindableStructDesc & TargetStruct , TArray < FStateTreeEditorPropertyBinding > & OutBindings ) const
{
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 )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Failed to find binding source property '%s' for target '%s:%s'. " ) ,
* Binding . SourcePath . ToString ( ) , * TargetStruct . Name . ToString ( ) , * Binding . TargetPath . ToString ( ) ) ;
return false ;
}
const FStateTreeBindableStructDesc & SourceStruct = BindingsCompiler . GetSourceStructDesc ( SourceStructIdx ) ;
// Source must be accessible by the target struct via all execution paths.
TArray < FStateTreeBindableStructDesc > AccessibleStructs ;
TreeData - > GetAccessibleStructs ( Binding . TargetPath . StructID , AccessibleStructs ) ;
const bool bSourceAccessible = AccessibleStructs . ContainsByPredicate ( [ SourceStructID ] ( const FStateTreeBindableStructDesc & Structs )
{
return ( Structs . ID = = SourceStructID ) ;
} ) ;
if ( ! bSourceAccessible )
{
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 ( ) ) ;
return false ;
}
// 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 ) ;
}
2022-06-29 04:52:18 -04:00
// Check if the bindings is for struct ref and validate the types.
if ( ! ValidateStructRef ( SourceStruct , Binding . SourcePath , TargetStruct , Binding . TargetPath ) )
{
return false ;
}
2022-05-31 04:51:18 -04:00
}
return true ;
}