2022-05-31 04:51:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeCompiler.h"
# include "StateTree.h"
2023-01-19 00:48:07 -05:00
# include "StateTreeAnyEnum.h"
2023-01-25 02:42:36 -05:00
# include "StateTreeCompilerLog.h"
2022-05-31 04:51:18 -04:00
# include "StateTreeEditorData.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeTaskBase.h"
# include "StateTreeConditionBase.h"
2023-04-05 09:28:17 -04:00
# include "Serialization/ArchiveUObject.h"
2023-06-19 05:49:34 -04:00
# include "GameFramework/Actor.h"
2024-01-11 04:24:45 -05:00
# include "StateTreePropertyRef.h"
# include "StateTreePropertyRefHelpers.h"
# include "StateTreePropertyHelpers.h"
2023-04-05 09:28:17 -04:00
2022-05-31 04:51:18 -04:00
namespace UE : : StateTree : : Compiler
{
2023-04-05 09:28:17 -04:00
// Helper archive that checks that the all instanced sub-objects have correct outer.
class FCheckOutersArchive : public FArchiveUObject
{
using Super = FArchiveUObject ;
const UStateTree & StateTree ;
const UStateTreeEditorData & EditorData ;
FStateTreeCompilerLog & Log ;
public :
FCheckOutersArchive ( const UStateTree & InStateTree , const UStateTreeEditorData & InEditorData , FStateTreeCompilerLog & InLog )
: StateTree ( InStateTree )
, EditorData ( InEditorData )
, Log ( InLog )
{
Super : : SetIsSaving ( true ) ;
Super : : SetIsPersistent ( true ) ;
}
virtual bool ShouldSkipProperty ( const FProperty * InProperty ) const
{
// Skip editor data.
if ( const FObjectProperty * ObjectProperty = CastField < FObjectProperty > ( InProperty ) )
{
if ( ObjectProperty - > PropertyClass = = UStateTreeEditorData : : StaticClass ( ) )
{
return true ;
}
}
return false ;
}
virtual FArchive & operator < < ( UObject * & Object ) override
{
if ( Object )
{
if ( const FProperty * Property = GetSerializedProperty ( ) )
{
if ( Property - > HasAnyPropertyFlags ( CPF_InstancedReference ) )
{
if ( ! Object - > IsInOuter ( & StateTree ) )
{
Log . Reportf ( EMessageSeverity : : Error , TEXT ( " Compiled StateTree contains instanced object %s (%s), which does not belong to the StateTree. This is due to error in the State Tree node implementation. " ) ,
* GetFullNameSafe ( Object ) , * GetFullNameSafe ( Object - > GetClass ( ) ) ) ;
}
if ( Object - > IsInOuter ( & EditorData ) )
{
Log . Reportf ( EMessageSeverity : : Error , TEXT ( " Compiled StateTree contains instanced object %s (%s), which still belongs to the Editor data. This is due to error in the State Tree node implementation. " ) ,
* GetFullNameSafe ( Object ) , * GetFullNameSafe ( Object - > GetClass ( ) ) ) ;
}
}
}
}
return * this ;
}
} ;
2023-06-19 05:49:34 -04:00
/** Scans Data for actors that are tied to some level and returns them. */
void ScanLevelActorReferences ( FStateTreeDataView Data , TSet < const UObject * > & Visited , TArray < const AActor * > & OutActors )
{
if ( ! Data . IsValid ( ) )
{
return ;
}
for ( TPropertyValueIterator < FProperty > It ( Data . GetStruct ( ) , Data . GetMemory ( ) ) ; It ; + + It )
{
const FProperty * Property = It - > Key ;
const void * ValuePtr = It - > Value ;
if ( ! ValuePtr )
{
continue ;
}
if ( const FStructProperty * StructProperty = CastField < FStructProperty > ( Property ) )
{
if ( StructProperty - > Struct = = TBaseStructure < FInstancedStruct > : : Get ( ) )
{
const FInstancedStruct & InstancedStruct = * static_cast < const FInstancedStruct * > ( ValuePtr ) ;
if ( InstancedStruct . IsValid ( ) )
{
ScanLevelActorReferences ( FStateTreeDataView ( const_cast < FInstancedStruct & > ( InstancedStruct ) ) , Visited , OutActors ) ;
}
}
}
else if ( const FObjectPropertyBase * ObjectProperty = CastField < FObjectPropertyBase > ( Property ) )
{
if ( const UObject * Object = ObjectProperty - > GetObjectPropertyValue ( ValuePtr ) )
{
if ( const AActor * Actor = Cast < AActor > ( Object ) )
{
const ULevel * Level = Actor - > GetLevel ( ) ;
if ( Level ! = nullptr )
{
OutActors . Add ( Actor ) ;
}
}
// Recurse into instanced object
if ( Property - > HasAnyPropertyFlags ( CPF_InstancedReference ) )
{
if ( ! Visited . Contains ( Object ) )
{
Visited . Add ( Object ) ;
ScanLevelActorReferences ( FStateTreeDataView ( const_cast < UObject * > ( Object ) ) , Visited , OutActors ) ;
}
}
}
}
}
}
bool ValidateNoLevelActorReferences ( FStateTreeCompilerLog & Log , const FStateTreeBindableStructDesc & NodeDesc , FStateTreeDataView NodeView , FStateTreeDataView InstanceView )
{
TSet < const UObject * > Visited ;
TArray < const AActor * > LevelActors ;
UE : : StateTree : : Compiler : : ScanLevelActorReferences ( NodeView , Visited , LevelActors ) ;
UE : : StateTree : : Compiler : : ScanLevelActorReferences ( InstanceView , Visited , LevelActors ) ;
if ( ! LevelActors . IsEmpty ( ) )
{
FStringBuilderBase AllActorsString ;
for ( const AActor * Actor : LevelActors )
{
if ( AllActorsString . Len ( ) > 0 )
{
AllActorsString + = TEXT ( " , " ) ;
}
AllActorsString + = * GetNameSafe ( Actor ) ;
}
Log . Reportf ( EMessageSeverity : : Error , NodeDesc ,
TEXT ( " Level Actor references were found: %s. Direct Actor references are not allowed. " ) ,
* AllActorsString ) ;
return false ;
}
return true ;
}
2022-05-31 04:51:18 -04:00
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 ;
2022-09-19 19:47:11 -04:00
EditorData = Cast < UStateTreeEditorData > ( StateTree - > EditorData ) ;
if ( ! EditorData )
2022-05-31 04:51:18 -04:00
{
return false ;
}
// Cleanup existing state
StateTree - > ResetCompiled ( ) ;
2024-03-28 05:18:19 -04:00
if ( ! EditorData - > Schema )
{
Log . Reportf ( EMessageSeverity : : Error , TEXT ( " Missing Schema. Please set valid schema in the State Tree Asset settings. " ) ) ;
return false ;
}
Schema = EditorData - > Schema ;
2022-05-31 04:51:18 -04:00
if ( ! BindingsCompiler . Init ( StateTree - > PropertyBindings , Log ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2023-06-07 09:02:19 -04:00
EditorData - > GetAllStructValues ( IDToStructValue ) ;
2022-05-31 04:51:18 -04:00
// Copy schema the EditorData
2022-09-19 19:47:11 -04:00
StateTree - > Schema = DuplicateObject ( EditorData - > Schema , StateTree ) ;
2022-05-31 04:51:18 -04:00
// Copy parameters from EditorData
2022-09-19 19:47:11 -04:00
StateTree - > Parameters = EditorData - > RootParameters . Parameters ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
// Mark parameters as binding source
const FStateTreeBindableStructDesc ParametersDesc = {
TEXT ( " Parameters " ) ,
StateTree - > Parameters . GetPropertyBagStruct ( ) ,
2024-01-22 05:38:03 -05:00
FStateTreeDataHandle ( EStateTreeDataSourceType : : GlobalParameterData ) ,
2022-09-19 19:47:11 -04:00
EStateTreeBindableStructSource : : Parameter ,
EditorData - > RootParameters . ID
2022-05-31 04:51:18 -04:00
} ;
2023-11-22 04:08:33 -05:00
BindingsCompiler . AddSourceStruct ( ParametersDesc ) ;
2022-05-31 04:51:18 -04:00
2023-06-19 05:49:34 -04:00
if ( ! UE : : StateTree : : Compiler : : ValidateNoLevelActorReferences ( Log , ParametersDesc , FStateTreeDataView ( ) , FStateTreeDataView ( EditorData - > RootParameters . Parameters . GetMutableValue ( ) ) ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2024-01-22 05:38:03 -05:00
int32 ContextDataIndex = 0 ;
2022-05-31 04:51:18 -04:00
// Mark all named external values as binding source
if ( StateTree - > Schema )
{
2022-09-19 19:47:11 -04:00
StateTree - > ContextDataDescs = StateTree - > Schema - > GetContextDataDescs ( ) ;
for ( FStateTreeExternalDataDesc & Desc : StateTree - > ContextDataDescs )
2022-05-31 04:51:18 -04:00
{
const FStateTreeBindableStructDesc ExtDataDesc = {
Desc . Name ,
Desc . Struct ,
2023-11-22 04:08:33 -05:00
FStateTreeDataHandle ( EStateTreeDataSourceType : : ContextData , ContextDataIndex + + ) ,
2022-09-19 19:47:11 -04:00
EStateTreeBindableStructSource : : Context ,
2022-05-31 04:51:18 -04:00
Desc . ID
} ;
2023-11-22 04:08:33 -05:00
BindingsCompiler . AddSourceStruct ( ExtDataDesc ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( ContextDataIndex ) ; Validation . DidFail ( ) )
2022-05-31 04:51:18 -04:00
{
Validation . Log ( Log , TEXT ( " ExternalStructIndex " ) , ParametersDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Desc . Handle . DataHandle = ExtDataDesc . DataHandle ;
2022-05-31 04:51:18 -04:00
}
}
2023-11-22 04:08:33 -05:00
2024-03-21 09:44:11 -04:00
if ( const UE : : StateTree : : Compiler : : FValidationResult Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( ContextDataIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " NumContextData " ) , ParametersDesc ) ;
return false ;
}
StateTree - > NumContextData = static_cast < uint16 > ( ContextDataIndex ) ;
2022-05-31 04:51:18 -04:00
if ( ! CreateStates ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
// Eval and Global task methods use InstanceStructs.Num() as ID generator.
check ( InstanceStructs . Num ( ) = = 0 ) ;
2022-05-31 04:51:18 -04:00
if ( ! CreateEvaluators ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2023-01-10 15:44:28 -05:00
if ( ! CreateGlobalTasks ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
const int32 NumGlobalInstanceData = InstanceStructs . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( NumGlobalInstanceData ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " NumGlobalInstanceData " ) , ParametersDesc ) ;
return false ;
}
StateTree - > NumGlobalInstanceData = uint16 ( NumGlobalInstanceData ) ;
2022-05-31 04:51:18 -04:00
if ( ! CreateStateTasksAndParameters ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
if ( ! CreateStateTransitions ( ) )
{
StateTree - > ResetCompiled ( ) ;
return false ;
}
StateTree - > Nodes = Nodes ;
2023-11-22 04:08:33 -05:00
StateTree - > DefaultInstanceData . Init ( * StateTree , InstanceStructs ) ;
StateTree - > SharedInstanceData . Init ( * StateTree , SharedInstanceStructs ) ;
2022-05-31 04:51:18 -04:00
BindingsCompiler . Finalize ( ) ;
if ( ! StateTree - > Link ( ) )
{
StateTree - > ResetCompiled ( ) ;
2023-02-10 07:22:48 -05:00
Log . Reportf ( EMessageSeverity : : Error , TEXT ( " Unexpected failure to link the StateTree asset. See log for more info. " ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-03-14 13:35:46 -04:00
2023-06-15 11:38:28 -04:00
// Store mapping between node unique ID and their compiled index. Used for debugging purposes.
for ( const TPair < FGuid , int32 > & ToNode : IDToNode )
{
StateTree - > IDToNodeMappings . Emplace ( ToNode . Key , FStateTreeIndex16 ( ToNode . Value ) ) ;
}
2023-03-14 13:35:46 -04:00
// Store mapping between state unique ID and state handle. Used for debugging purposes.
2023-06-15 11:38:28 -04:00
for ( const TPair < FGuid , int32 > & ToState : IDToState )
2023-03-14 13:35:46 -04:00
{
StateTree - > IDToStateMappings . Emplace ( ToState . Key , FStateTreeStateHandle ( ToState . Value ) ) ;
}
2023-06-20 13:49:25 -04:00
// Store mapping between state transition identifier and compact transition index. Used for debugging purposes.
for ( const TPair < FGuid , int32 > & ToTransition : IDToTransition )
{
StateTree - > IDToTransitionMappings . Emplace ( ToTransition . Key , FStateTreeIndex16 ( ToTransition . Value ) ) ;
}
2023-04-05 09:28:17 -04:00
UE : : StateTree : : Compiler : : FCheckOutersArchive CheckOuters ( * StateTree , * EditorData , Log ) ;
StateTree - > Serialize ( CheckOuters ) ;
2022-05-31 04:51:18 -04:00
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 ) ) ;
}
2023-03-03 11:22:29 -05:00
UStateTreeState * FStateTreeCompiler : : GetState ( const FGuid & StateID ) const
2022-05-31 04:51:18 -04:00
{
const int32 * Idx = IDToState . Find ( StateID ) ;
if ( Idx = = nullptr )
{
return nullptr ;
}
return SourceStates [ * Idx ] ;
}
bool FStateTreeCompiler : : CreateStates ( )
{
2023-01-23 12:48:04 -05:00
check ( EditorData ) ;
2022-05-31 04:51:18 -04:00
// Create main tree (omit subtrees)
2022-09-19 19:47:11 -04:00
for ( UStateTreeState * SubTree : EditorData - > SubTrees )
2022-05-31 04:51:18 -04:00
{
2023-02-09 04:23:32 -05:00
if ( SubTree ! = nullptr
& & SubTree - > Type ! = EStateTreeStateType : : Subtree )
2022-05-31 04:51:18 -04:00
{
if ( ! CreateStateRecursive ( * SubTree , FStateTreeStateHandle : : Invalid ) )
{
return false ;
}
}
}
// Create Subtrees
2022-09-19 19:47:11 -04:00
for ( UStateTreeState * SubTree : EditorData - > SubTrees )
2022-05-31 04:51:18 -04:00
{
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 )
{
2023-01-23 12:48:04 -05:00
check ( StateTree ) ;
2022-05-31 04:51:18 -04:00
FStateTreeCompilerLogStateScope LogStateScope ( & State , Log ) ;
2024-04-12 07:21:04 -04:00
if ( State . Type = = EStateTreeStateType : : LinkedAsset
| | State . Type = = EStateTreeStateType : : Linked )
{
Log . Reportf ( EMessageSeverity : : Warning ,
TEXT ( " Linked State cannot have child states, because the state selection will enter to the linked state on activation. " ) ) ;
}
2022-05-31 04:51:18 -04:00
const int32 StateIdx = StateTree - > States . AddDefaulted ( ) ;
FCompactStateTreeState & CompactState = StateTree - > States [ StateIdx ] ;
CompactState . Name = State . Name ;
2024-03-28 05:18:19 -04:00
CompactState . Tag = State . Tag ;
2022-05-31 04:51:18 -04:00
CompactState . Parent = Parent ;
2023-06-05 13:12:19 -04:00
CompactState . bEnabled = State . bEnabled ;
2024-04-05 02:16:32 -04:00
CompactState . bCheckPrerequisitesWhenActivatingChildDirectly = State . bCheckPrerequisitesWhenActivatingChildDirectly ;
2022-05-31 04:51:18 -04:00
2023-04-12 07:59:16 -04:00
CompactState . Type = State . Type ;
CompactState . SelectionBehavior = State . SelectionBehavior ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
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 ) ;
2023-04-12 07:59:16 -04:00
2022-05-31 04:51:18 -04:00
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 ;
}
2023-04-12 07:59:16 -04:00
StateTree - > States [ StateIdx ] . ChildrenEnd = uint16 ( ChildrenEnd ) ; // Not using CompactState here because the array may have changed.
2022-05-31 04:51:18 -04:00
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.
2023-04-04 08:25:50 -04:00
const int32 NextIndent = Conditions . IsValidIndex ( Index + 1 ) ? FMath : : Clamp ( ( int32 ) Conditions [ Index + 1 ] . ConditionIndent , 0 , UE : : StateTree : : MaxConditionIndent ) : 0 ;
2022-05-31 04:51:18 -04:00
const int32 DeltaIndent = NextIndent - CurrIndent ;
if ( ! CreateCondition ( State , CondNode , Operand , ( int8 ) DeltaIndent ) )
{
return false ;
}
}
return true ;
}
bool FStateTreeCompiler : : CreateEvaluators ( )
{
2023-01-23 12:48:04 -05:00
check ( EditorData ) ;
check ( StateTree ) ;
2022-05-31 04:51:18 -04:00
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 ) ;
2022-09-19 19:47:11 -04:00
for ( FStateTreeEditorNode & EvalNode : EditorData - > Evaluators )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const int32 GlobalInstanceIndex = InstanceStructs . Num ( ) ;
const FStateTreeDataHandle EvalDataHandle ( EStateTreeDataSourceType : : GlobalInstanceData , GlobalInstanceIndex ) ;
if ( ! CreateEvaluator ( EvalNode , EvalDataHandle ) )
2022-05-31 04:51:18 -04:00
{
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 ;
}
2023-01-10 15:44:28 -05:00
bool FStateTreeCompiler : : CreateGlobalTasks ( )
{
2023-01-23 12:48:04 -05:00
check ( EditorData ) ;
check ( StateTree ) ;
2023-01-10 15:44:28 -05:00
const int32 GlobalTasksBegin = Nodes . Num ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( GlobalTasksBegin ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " GlobalTasksBegin " ) ) ;
return false ;
}
StateTree - > GlobalTasksBegin = uint16 ( GlobalTasksBegin ) ;
2023-01-23 12:48:04 -05:00
StateTree - > bHasGlobalTransitionTasks = false ;
2023-01-10 15:44:28 -05:00
for ( FStateTreeEditorNode & TaskNode : EditorData - > GlobalTasks )
{
2023-06-22 08:35:24 -04:00
// Silently ignore empty nodes.
if ( ! TaskNode . Node . IsValid ( ) )
{
continue ;
}
2023-11-22 04:08:33 -05:00
const int32 GlobalInstanceIndex = InstanceStructs . Num ( ) ;
const FStateTreeDataHandle TaskDataHandle ( EStateTreeDataSourceType : : GlobalInstanceData , GlobalInstanceIndex ) ;
if ( ! CreateTask ( nullptr , TaskNode , TaskDataHandle ) )
2023-01-10 15:44:28 -05:00
{
return false ;
}
2023-06-22 05:26:39 -04:00
const FStateTreeTaskBase & LastAddedTask = Nodes . Last ( ) . Get < FStateTreeTaskBase > ( ) ;
StateTree - > bHasGlobalTransitionTasks | = LastAddedTask . bShouldAffectTransitions ;
2023-01-10 15:44:28 -05:00
}
const int32 GlobalTasksNum = Nodes . Num ( ) - GlobalTasksBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount16 ( GlobalTasksNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " GlobalTasksNum " ) ) ;
return false ;
}
StateTree - > GlobalTasksNum = uint16 ( GlobalTasksNum ) ;
return true ;
}
2022-05-31 04:51:18 -04:00
bool FStateTreeCompiler : : CreateStateTasksAndParameters ( )
{
2023-01-23 12:48:04 -05:00
check ( StateTree ) ;
2023-11-22 04:08:33 -05:00
// Index of the first instance data per state. Accumulated depth first.
TArray < int32 > FirstInstanceDataIndex ;
FirstInstanceDataIndex . Init ( 0 , StateTree - > States . Num ( ) ) ;
2022-05-31 04:51:18 -04:00
for ( int32 i = 0 ; i < StateTree - > States . Num ( ) ; i + + )
{
FCompactStateTreeState & CompactState = StateTree - > States [ i ] ;
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CompactStateHandle ( i ) ;
UStateTreeState * State = SourceStates [ i ] ;
check ( State ! = nullptr ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
// Carry over instance data count from parent.
if ( CompactState . Parent . IsValid ( ) )
{
const FCompactStateTreeState & ParentCompactState = StateTree - > States [ CompactState . Parent . Index ] ;
const int32 InstanceDataBegin = FirstInstanceDataIndex [ CompactState . Parent . Index ] + ( int32 ) ParentCompactState . InstanceDataNum ;
FirstInstanceDataIndex [ i ] = InstanceDataBegin ;
2024-03-11 04:54:44 -04:00
CompactState . Depth = ParentCompactState . Depth + 1 ;
2023-11-22 04:08:33 -05:00
}
int32 InstanceDataIndex = FirstInstanceDataIndex [ i ] ;
FStateTreeCompilerLogStateScope LogStateScope ( State , Log ) ;
2022-05-31 04:51:18 -04:00
// Create parameters
2023-12-05 06:59:39 -05:00
// Each state has their parameters as instance data.
FInstancedStruct & Instance = InstanceStructs . AddDefaulted_GetRef ( ) ;
Instance . InitializeAs < FCompactStateTreeParameters > ( State - > Parameters . Parameters ) ;
FCompactStateTreeParameters & CompactStateTreeParameters = Instance . GetMutable < FCompactStateTreeParameters > ( ) ;
const int32 InstanceIndex = InstanceStructs . Num ( ) - 1 ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
2022-05-31 04:51:18 -04:00
{
2023-12-05 06:59:39 -05:00
Validation . Log ( Log , TEXT ( " InstanceIndex " ) ) ;
return false ;
2022-05-31 04:51:18 -04:00
}
2023-12-05 06:59:39 -05:00
CompactState . ParameterTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
if ( State - > Type = = EStateTreeStateType : : Subtree )
{
CompactState . ParameterDataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : SubtreeParameterData , InstanceDataIndex + + , CompactStateHandle ) ;
}
else
{
CompactState . ParameterDataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : StateParameterData , InstanceDataIndex + + , CompactStateHandle ) ;
}
// @todo: We should be able to skip empty parameter data.
// Binding target
FStateTreeBindableStructDesc LinkedParamsDesc = {
State - > Name ,
State - > Parameters . Parameters . GetPropertyBagStruct ( ) ,
CompactState . ParameterDataHandle ,
2024-03-11 04:54:44 -04:00
EStateTreeBindableStructSource : : StateParameter ,
2023-12-05 06:59:39 -05:00
State - > Parameters . ID
} ;
if ( ! UE : : StateTree : : Compiler : : ValidateNoLevelActorReferences ( Log , LinkedParamsDesc , FStateTreeDataView ( ) , FStateTreeDataView ( CompactStateTreeParameters . Parameters . GetMutableValue ( ) ) ) )
{
return false ;
}
// Add as binding source.
BindingsCompiler . AddSourceStruct ( LinkedParamsDesc ) ;
2024-03-11 04:54:44 -04:00
if ( State - > bHasRequiredEventToEnter )
{
CompactState . EventDataIndex = FStateTreeIndex16 ( InstanceDataIndex + + ) ;
CompactState . RequiredEventToEnter . Tag = State - > RequiredEventToEnter . Tag ;
CompactState . RequiredEventToEnter . PayloadStruct = State - > RequiredEventToEnter . PayloadStruct ;
2024-04-12 06:04:01 -04:00
CompactState . bConsumeEventOnSelect = State - > RequiredEventToEnter . bConsumeEventOnSelect ;
2024-03-11 04:54:44 -04:00
FStateTreeBindableStructDesc Desc ;
Desc . Struct = FStateTreeEvent : : StaticStruct ( ) ;
Desc . Name = CompactState . Name ;
Desc . ID = State - > GetEventID ( ) ;
Desc . DataSource = EStateTreeBindableStructSource : : StateEvent ;
Desc . DataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : StateEvent , CompactState . EventDataIndex . Get ( ) , CompactStateHandle ) ;
BindingsCompiler . AddSourceStruct ( Desc ) ;
if ( ! CompactState . RequiredEventToEnter . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error , Desc ,
TEXT ( " Event is marked as required, but isn't set up. " ) ) ;
return false ;
}
}
if ( CompactState . Depth > = FStateTreeActiveStates : : MaxStates )
{
Log . Reportf ( EMessageSeverity : : Error , LinkedParamsDesc ,
TEXT ( " Exceeds the maximum depth of execution (%u) " ) , FStateTreeActiveStates : : MaxStates ) ;
return false ;
}
2023-12-05 06:59:39 -05:00
int32 BatchIndex = INDEX_NONE ;
2024-01-11 04:24:45 -05:00
2024-02-14 09:04:52 -05:00
// Subtrees parameters cannot have bindings
if ( State - > Type ! = EStateTreeStateType : : Subtree )
2023-12-05 06:59:39 -05:00
{
2024-02-14 09:04:52 -05:00
// Check that the bindings for this struct are still all valid.
TArray < FStateTreePropertyPathBinding > CopyBindings ;
TArray < FStateTreePropertyPathBinding > ReferenceBindings ;
if ( ! GetAndValidateBindings ( LinkedParamsDesc , FStateTreeDataView ( CompactStateTreeParameters . Parameters . GetMutableValue ( ) ) , CopyBindings , ReferenceBindings ) )
{
return false ;
}
if ( ! BindingsCompiler . CompileBatch ( LinkedParamsDesc , CopyBindings , BatchIndex ) )
{
return false ;
}
if ( ! BindingsCompiler . CompileReferences ( LinkedParamsDesc , ReferenceBindings , FStateTreeDataView ( CompactStateTreeParameters . Parameters . GetMutableValue ( ) ) , IDToStructValue ) )
{
return false ;
}
2023-12-05 06:59:39 -05:00
}
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( BatchIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " BatchIndex " ) , LinkedParamsDesc ) ;
return false ;
}
CompactState . ParameterBindingsBatch = FStateTreeIndex16 ( BatchIndex ) ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
// 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 ) ;
2023-01-23 12:48:04 -05:00
2023-11-17 11:42:17 -05:00
TArrayView < FStateTreeEditorNode > Tasks ;
2023-11-22 04:08:33 -05:00
if ( State - > Tasks . Num ( ) )
2023-06-22 05:26:39 -04:00
{
2023-11-22 04:08:33 -05:00
Tasks = State - > Tasks ;
2023-11-17 11:42:17 -05:00
}
2023-11-22 04:08:33 -05:00
else if ( State - > SingleTask . Node . IsValid ( ) )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
Tasks = TArrayView < FStateTreeEditorNode > ( & State - > SingleTask , 1 ) ;
2023-06-22 05:26:39 -04:00
}
bool bStateHasTransitionTasks = false ;
for ( FStateTreeEditorNode & TaskNode : Tasks )
{
2023-06-22 08:35:24 -04:00
// Silently ignore empty nodes.
if ( ! TaskNode . Node . IsValid ( ) )
{
continue ;
}
2023-11-22 04:08:33 -05:00
const FStateTreeDataHandle TaskDataHandle ( EStateTreeDataSourceType : : ActiveInstanceData , InstanceDataIndex + + , CompactStateHandle ) ;
if ( ! CreateTask ( State , TaskNode , TaskDataHandle ) )
2022-05-31 04:51:18 -04:00
{
return false ;
}
2023-06-22 05:26:39 -04:00
const FStateTreeTaskBase & LastAddedTask = Nodes . Last ( ) . Get < FStateTreeTaskBase > ( ) ;
bStateHasTransitionTasks | = LastAddedTask . bShouldAffectTransitions ;
2022-05-31 04:51:18 -04:00
}
2023-06-22 05:26:39 -04:00
CompactState . bHasTransitionTasks = bStateHasTransitionTasks ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
const int32 TasksNum = Nodes . Num ( ) - TasksBegin ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( TasksNum ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " TasksNum " ) ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
2024-02-29 05:37:32 -05:00
const int32 InstanceDataNum = InstanceDataIndex - FirstInstanceDataIndex [ i ] ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidCount8 ( InstanceDataNum ) ; Validation . DidFail ( ) )
2023-06-22 05:26:39 -04:00
{
2024-02-29 05:37:32 -05:00
Validation . Log ( Log , TEXT ( " InstanceDataNum " ) ) ;
2023-06-22 05:26:39 -04:00
return false ;
}
2022-05-31 04:51:18 -04:00
CompactState . TasksNum = uint8 ( TasksNum ) ;
2024-02-29 05:37:32 -05:00
CompactState . InstanceDataNum = uint8 ( InstanceDataNum ) ;
2022-05-31 04:51:18 -04:00
}
return true ;
}
bool FStateTreeCompiler : : CreateStateTransitions ( )
{
2023-01-23 12:48:04 -05:00
check ( StateTree ) ;
2022-05-31 04:51:18 -04:00
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 )
{
2022-09-30 11:31:57 -04:00
if ( State - > ID = = SourceState - > LinkedSubtree . ID )
2022-05-31 04:51:18 -04:00
{
LinkedParentState = State ;
break ;
}
}
if ( LinkedParentState ! = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-09-30 11:31:57 -04:00
TEXT ( " State is linked to it's parent subtree '%s', which will create infinite loop. " ) ,
2022-05-31 04:51:18 -04:00
* LinkedParentState - > Name . ToString ( ) ) ;
return false ;
}
2022-07-21 08:19:56 -04:00
// The linked state must be a subtree.
2022-09-30 11:31:57 -04:00
const UStateTreeState * TargetState = GetState ( SourceState - > LinkedSubtree . ID ) ;
2022-07-21 08:19:56 -04:00
if ( TargetState = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-09-30 11:31:57 -04:00
TEXT ( " Failed to resolve linked subtree '%s'. " ) ,
* SourceState - > LinkedSubtree . Name . ToString ( ) ) ;
2022-07-21 08:19:56 -04:00
return false ;
}
if ( TargetState - > Type ! = EStateTreeStateType : : Subtree )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-09-30 11:31:57 -04:00
TEXT ( " State '%s' is linked to subtree '%s', which is not a subtree. " ) ,
2022-07-21 08:19:56 -04:00
* SourceState - > Name . ToString ( ) , * TargetState - > Name . ToString ( ) ) ;
return false ;
}
2022-05-31 04:51:18 -04:00
2022-09-30 11:31:57 -04:00
CompactState . LinkedState = GetStateHandle ( SourceState - > LinkedSubtree . ID ) ;
2022-05-31 04:51:18 -04:00
if ( ! CompactState . LinkedState . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
2022-09-30 11:31:57 -04:00
TEXT ( " Failed to resolve linked subtree '%s'. " ) ,
* SourceState - > LinkedSubtree . Name . ToString ( ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
}
2023-11-30 07:03:20 -05:00
else if ( SourceState - > Type = = EStateTreeStateType : : LinkedAsset )
{
// Do not allow to link to the same asset (might create recursion)
if ( SourceState - > LinkedAsset = = StateTree )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " It is not allowed to link to the same tree, as it might create infinite loop. " ) ) ;
return false ;
}
2024-03-28 05:18:19 -04:00
// Linked asset must have same schema.
const UStateTreeSchema * LinkedAssetSchema = SourceState - > LinkedAsset ? SourceState - > LinkedAsset - > GetSchema ( ) : nullptr ;
if ( ! LinkedAssetSchema )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Linked State Tree asset must have valid schema. " ) ) ;
return false ;
}
check ( Schema ) ;
if ( LinkedAssetSchema - > GetClass ( ) ! = Schema - > GetClass ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Linked State Tree asset '%s' must have same schema class as this asset. Linked asset has '%s', expected '%s'. " ) ,
* GetFullNameSafe ( SourceState - > LinkedAsset ) ,
* LinkedAssetSchema - > GetClass ( ) - > GetDisplayNameText ( ) . ToString ( ) ,
* Schema - > GetClass ( ) - > GetDisplayNameText ( ) . ToString ( )
) ;
return false ;
}
2023-11-30 07:03:20 -05:00
CompactState . LinkedAsset = SourceState - > LinkedAsset ;
}
2022-05-31 04:51:18 -04:00
// 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 )
{
2024-03-11 04:54:44 -04:00
const int32 TransitionIndex = StateTree - > Transitions . Num ( ) ;
IDToTransition . Add ( Transition . ID , TransitionIndex ) ;
2023-06-20 13:49:25 -04:00
2022-05-31 04:51:18 -04:00
FCompactStateTransition & CompactTransition = StateTree - > Transitions . AddDefaulted_GetRef ( ) ;
2022-09-01 09:06:53 -04:00
CompactTransition . Trigger = Transition . Trigger ;
2023-01-23 12:48:04 -05:00
CompactTransition . Priority = Transition . Priority ;
2024-03-11 04:54:44 -04:00
2023-06-20 13:49:25 -04:00
CompactTransition . bTransitionEnabled = Transition . bTransitionEnabled ;
2023-10-17 16:15:53 -04:00
if ( Transition . State . LinkType = = EStateTreeTransitionType : : NextSelectableState )
{
CompactTransition . Fallback = EStateTreeSelectionFallback : : NextSelectableSibling ;
}
2022-12-02 07:57:31 -05:00
if ( Transition . bDelayTransition )
{
CompactTransition . Delay . Set ( Transition . DelayDuration , Transition . DelayRandomVariance ) ;
}
2023-04-12 07:59:16 -04:00
if ( CompactState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TryFollowTransitions
& & Transition . bDelayTransition )
{
Log . Reportf ( EMessageSeverity : : Warning ,
TEXT ( " Transition to '%s' with delay will be ignored during state selection. " ) ,
* Transition . State . Name . ToString ( ) ) ;
}
2022-12-02 07:57:31 -05:00
if ( EnumHasAnyFlags ( Transition . Trigger , EStateTreeTransitionTrigger : : OnStateCompleted ) )
{
2023-01-23 12:48:04 -05:00
// Completion transitions dont have priority.
CompactTransition . Priority = EStateTreeTransitionPriority : : None ;
2022-12-02 07:57:31 -05:00
// Completion transitions cannot have delay.
CompactTransition . Delay . Reset ( ) ;
// Completion transitions must have valid target state.
2023-01-23 12:48:04 -05:00
if ( Transition . State . LinkType = = EStateTreeTransitionType : : None )
2022-12-02 07:57:31 -05:00
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " State completion transition to '%s' must have transition to valid state, 'None' not accepted. " ) ,
* Transition . State . Name . ToString ( ) ) ;
2024-04-11 07:09:40 -04:00
return false ;
2022-12-02 07:57:31 -05:00
}
}
2022-05-31 04:51:18 -04:00
CompactTransition . State = FStateTreeStateHandle : : Invalid ;
2023-01-23 12:48:04 -05:00
if ( ! ResolveTransitionState ( SourceState , Transition . State , CompactTransition . State ) )
2022-05-31 04:51:18 -04:00
{
return false ;
}
2024-03-11 04:54:44 -04:00
if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent )
{
CompactTransition . RequiredEvent . Tag = Transition . RequiredEvent . Tag ;
CompactTransition . RequiredEvent . PayloadStruct = Transition . RequiredEvent . PayloadStruct ;
2024-04-12 06:04:01 -04:00
CompactTransition . bConsumeEventOnSelect = Transition . RequiredEvent . bConsumeEventOnSelect ;
2024-03-11 04:54:44 -04:00
FStateTreeBindableStructDesc Desc ;
Desc . Struct = FStateTreeEvent : : StaticStruct ( ) ;
Desc . Name = FName ( TEXT ( " Transition " ) ) ;
Desc . ID = Transition . GetEventID ( ) ;
Desc . DataSource = EStateTreeBindableStructSource : : TransitionEvent ;
Desc . DataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : TransitionEvent , TransitionIndex ) ;
if ( ! Transition . RequiredEvent . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error , Desc ,
TEXT ( " On Event Transition requires at least tag or payload to be set up. " ) ,
* Transition . State . Name . ToString ( ) ) ;
return false ;
}
if ( ! CompactTransition . State . IsCompletionState ( ) )
{
FCompactStateTreeState & TransitionTargetState = StateTree - > States [ CompactTransition . State . Index ] ;
if ( TransitionTargetState . RequiredEventToEnter . IsValid ( ) & & ! TransitionTargetState . RequiredEventToEnter . IsSubsetOfAnotherDesc ( CompactTransition . RequiredEvent ) )
{
Log . Reportf ( EMessageSeverity : : Error , Desc ,
TEXT ( " On Event transition to %s will never succeed as transition and state required events are incompatible. " ) ,
* TransitionTargetState . Name . ToString ( ) ) ;
return false ;
}
}
BindingsCompiler . AddSourceStruct ( Desc ) ;
}
2022-05-31 04:51:18 -04:00
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 ;
}
2023-01-23 12:48:04 -05:00
bool FStateTreeCompiler : : ResolveTransitionState ( const UStateTreeState * SourceState , const FStateTreeStateLink & Link , FStateTreeStateHandle & OutTransitionHandle ) const
2022-05-31 04:51:18 -04:00
{
2023-01-23 12:48:04 -05:00
if ( Link . LinkType = = EStateTreeTransitionType : : GotoState )
2022-05-31 04:51:18 -04:00
{
2023-03-03 11:22:29 -05:00
// Warn if goto state points to another subtree.
if ( const UStateTreeState * TargetState = GetState ( Link . ID ) )
{
2023-09-01 08:57:41 -04:00
if ( SourceState & & TargetState - > GetRootState ( ) ! = SourceState - > GetRootState ( ) )
2023-03-03 11:22:29 -05:00
{
Log . Reportf ( EMessageSeverity : : Warning ,
TEXT ( " Target state '%s' is in different subtree. Verify that this is intentional. " ) ,
* Link . Name . ToString ( ) ) ;
2023-04-12 07:59:16 -04:00
}
if ( TargetState - > SelectionBehavior = = EStateTreeStateSelectionBehavior : : None )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " The target State '%s' is not selectable, it's selection behavior is set to None. " ) ,
* Link . Name . ToString ( ) ) ;
return false ;
}
2023-03-03 11:22:29 -05:00
}
2022-05-31 04:51:18 -04:00
OutTransitionHandle = GetStateHandle ( Link . ID ) ;
if ( ! OutTransitionHandle . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error ,
TEXT ( " Failed to resolve transition to state '%s'. " ) ,
* Link . Name . ToString ( ) ) ;
return false ;
}
}
2023-10-17 16:15:53 -04:00
else if ( Link . LinkType = = EStateTreeTransitionType : : NextState | | Link . LinkType = = EStateTreeTransitionType : : NextSelectableState )
2022-05-31 04:51:18 -04:00
{
// Find next state.
2023-04-12 07:59:16 -04:00
const UStateTreeState * NextState = SourceState ? SourceState - > GetNextSelectableSiblingState ( ) : nullptr ;
2022-05-31 04:51:18 -04:00
if ( NextState = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error ,
2023-04-12 07:59:16 -04:00
TEXT ( " Failed to resolve transition, there's no selectable next state. " ) ) ;
2022-05-31 04:51:18 -04:00
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 ;
}
}
2023-01-23 12:48:04 -05:00
else if ( Link . LinkType = = EStateTreeTransitionType : : Failed )
{
OutTransitionHandle = FStateTreeStateHandle : : Failed ;
return true ;
}
else if ( Link . LinkType = = EStateTreeTransitionType : : Succeeded )
{
OutTransitionHandle = FStateTreeStateHandle : : Succeeded ;
return true ;
}
else if ( Link . LinkType = = EStateTreeTransitionType : : None )
{
OutTransitionHandle = FStateTreeStateHandle : : Invalid ;
return true ;
}
2022-05-31 04:51:18 -04:00
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 ;
2024-04-05 03:37:02 -04:00
StructDesc . Name = CondNode . GetName ( ) ;
2022-05-31 04:51:18 -04:00
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
2023-06-15 11:38:28 -04:00
IDToNode . Add ( CondNode . ID , Nodes . Num ( ) ) ;
2023-01-10 15:44:28 -05:00
FInstancedStruct & Node = Nodes . Add_GetRef ( CondNode . Node ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( Node ) ;
2022-05-31 04:51:18 -04:00
FStateTreeConditionBase & Cond = Node . GetMutable < FStateTreeConditionBase > ( ) ;
Cond . Operand = Operand ;
Cond . DeltaIndent = DeltaIndent ;
2023-02-10 07:22:48 -05:00
2024-04-05 03:37:02 -04:00
// Update condition name as description for runtime.
Cond . Name = CondNode . GetName ( ) ;
2023-02-10 07:22:48 -05:00
FStateTreeDataView InstanceDataView ;
2022-05-31 04:51:18 -04:00
if ( CondNode . Instance . IsValid ( ) )
{
// Struct instance
const int32 InstanceIndex = SharedInstanceStructs . Add ( CondNode . Instance ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( SharedInstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = CondNode . Instance . GetScriptStruct ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Cond . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Cond . InstanceDataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : SharedInstanceData , InstanceIndex ) ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( SharedInstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
}
else
{
// Object Instance
check ( CondNode . InstanceObject ! = nullptr ) ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
UObject * Instance = DuplicateObject ( CondNode . InstanceObject , StateTree ) ;
2023-11-22 04:08:33 -05:00
FInstancedStruct Wrapper ;
Wrapper . InitializeAs < FStateTreeInstanceObjectWrapper > ( Instance ) ;
const int32 InstanceIndex = SharedInstanceStructs . Add ( MoveTemp ( Wrapper ) ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Cond . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Cond . InstanceDataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : SharedInstanceDataObject , InstanceIndex ) ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( Instance ) ;
}
2022-11-18 08:38:31 -05:00
2023-11-22 04:08:33 -05:00
StructDesc . DataHandle = Cond . InstanceDataHandle ;
2023-02-10 07:22:48 -05:00
if ( ! CompileAndValidateNode ( & State , StructDesc , Node , InstanceDataView ) )
{
return false ;
2022-05-31 04:51:18 -04:00
}
// Mark the struct as binding source.
2023-11-22 04:08:33 -05:00
BindingsCompiler . AddSourceStruct ( StructDesc ) ;
2022-05-31 04:51:18 -04:00
// Check that the bindings for this struct are still all valid.
2024-01-11 04:24:45 -05:00
TArray < FStateTreePropertyPathBinding > CopyBindings ;
TArray < FStateTreePropertyPathBinding > ReferenceBindings ;
if ( ! GetAndValidateBindings ( StructDesc , InstanceDataView , CopyBindings , ReferenceBindings ) )
2022-05-31 04:51:18 -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 ;
2024-01-11 04:24:45 -05:00
if ( ! BindingsCompiler . CompileBatch ( StructDesc , CopyBindings , BatchIndex ) )
{
return false ;
}
2024-02-14 09:04:52 -05:00
if ( ! BindingsCompiler . CompileReferences ( StructDesc , ReferenceBindings , InstanceDataView , IDToStructValue ) )
2022-05-31 04:51:18 -04:00
{
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 ) ;
return true ;
}
2023-02-10 07:22:48 -05:00
bool FStateTreeCompiler : : CompileAndValidateNode ( const UStateTreeState * SourceState , const FStateTreeBindableStructDesc & NodeDesc , FStructView NodeView , const FStateTreeDataView InstanceData )
2022-11-18 08:38:31 -05:00
{
2023-01-23 12:48:04 -05:00
if ( ! NodeView . IsValid ( ) )
{
return false ;
}
2022-11-18 08:38:31 -05:00
2023-02-13 20:06:02 -05:00
FStateTreeNodeBase & Node = NodeView . Get < FStateTreeNodeBase > ( ) ;
2023-01-23 12:48:04 -05:00
check ( InstanceData . IsValid ( ) ) ;
auto ValidateStateLinks = [ this , SourceState ] ( TPropertyValueIterator < FStructProperty > It )
{
for ( ; It ; + + It )
{
if ( It - > Key - > Struct = = TBaseStructure < FStateTreeStateLink > : : Get ( ) )
{
FStateTreeStateLink & StateLink = * static_cast < FStateTreeStateLink * > ( const_cast < void * > ( It - > Value ) ) ;
if ( ! ResolveTransitionState ( SourceState , StateLink , StateLink . StateHandle ) )
{
return false ;
}
}
}
return true ;
} ;
// Validate any state links.
if ( ! ValidateStateLinks ( TPropertyValueIterator < FStructProperty > ( InstanceData . GetStruct ( ) , InstanceData . GetMutableMemory ( ) ) ) )
{
return false ;
}
2023-02-13 20:06:02 -05:00
if ( ! ValidateStateLinks ( TPropertyValueIterator < FStructProperty > ( NodeView . GetScriptStruct ( ) , NodeView . GetMemory ( ) ) ) )
2023-01-23 12:48:04 -05:00
{
return false ;
}
2022-11-18 08:38:31 -05:00
TArray < FText > ValidationErrors ;
const EDataValidationResult Result = Node . Compile ( InstanceData , ValidationErrors ) ;
if ( Result = = EDataValidationResult : : Invalid & & ValidationErrors . IsEmpty ( ) )
{
Log . Report ( EMessageSeverity : : Error , NodeDesc , TEXT ( " Node validation failed. " ) ) ;
}
else
{
const EMessageSeverity : : Type Severity = Result = = EDataValidationResult : : Invalid ? EMessageSeverity : : Error : EMessageSeverity : : Warning ;
for ( const FText & Error : ValidationErrors )
{
Log . Report ( Severity , NodeDesc , Error . ToString ( ) ) ;
}
}
2023-06-19 05:49:34 -04:00
// Make sure there's no level actor references in the data.
if ( ! UE : : StateTree : : Compiler : : ValidateNoLevelActorReferences ( Log , NodeDesc , NodeView , InstanceData ) )
{
return false ;
}
2022-11-18 08:38:31 -05:00
return Result ! = EDataValidationResult : : Invalid ;
}
2023-11-22 04:08:33 -05:00
bool FStateTreeCompiler : : CreateTask ( UStateTreeState * State , const FStateTreeEditorNode & TaskNode , const FStateTreeDataHandle TaskDataHandle )
2022-05-31 04:51:18 -04:00
{
if ( ! TaskNode . Node . IsValid ( ) )
{
2023-06-22 08:35:24 -04:00
return false ;
2022-05-31 04:51:18 -04:00
}
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = TaskNode . ID ;
2024-04-05 03:37:02 -04:00
StructDesc . Name = TaskNode . GetName ( ) ;
2022-05-31 04:51:18 -04:00
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
2023-06-15 11:38:28 -04:00
IDToNode . Add ( TaskNode . ID , Nodes . Num ( ) ) ;
2023-01-10 15:44:28 -05:00
FInstancedStruct & Node = Nodes . Add_GetRef ( TaskNode . Node ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( Node ) ;
2022-05-31 04:51:18 -04:00
FStateTreeTaskBase & Task = Node . GetMutable < FStateTreeTaskBase > ( ) ;
2023-02-10 07:22:48 -05:00
FStateTreeDataView InstanceDataView ;
2022-05-31 04:51:18 -04:00
2024-04-05 03:37:02 -04:00
// Update task name as description for runtime.
Task . Name = TaskNode . GetName ( ) ;
2022-05-31 04:51:18 -04:00
if ( TaskNode . Instance . IsValid ( ) )
{
// Struct Instance
const int32 InstanceIndex = InstanceStructs . Add ( TaskNode . Instance ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( InstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = TaskNode . Instance . GetScriptStruct ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Task . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Task . InstanceDataHandle = TaskDataHandle ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( InstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
}
else
{
// Object Instance
check ( TaskNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( TaskNode . InstanceObject , StateTree ) ;
2023-11-22 04:08:33 -05:00
FInstancedStruct Wrapper ;
Wrapper . InitializeAs < FStateTreeInstanceObjectWrapper > ( Instance ) ;
const int32 InstanceIndex = InstanceStructs . Add ( MoveTemp ( Wrapper ) ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Task . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Task . InstanceDataHandle = TaskDataHandle . ToObjectSource ( ) ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( Instance ) ;
}
2022-11-18 08:38:31 -05:00
2023-11-22 04:08:33 -05:00
StructDesc . DataHandle = Task . InstanceDataHandle ;
2023-02-10 07:22:48 -05:00
if ( ! CompileAndValidateNode ( State , StructDesc , Node , InstanceDataView ) )
{
return false ;
2022-05-31 04:51:18 -04:00
}
// Mark the instance as binding source.
2023-11-22 04:08:33 -05:00
BindingsCompiler . AddSourceStruct ( StructDesc ) ;
2023-02-10 07:22:48 -05:00
2022-05-31 04:51:18 -04:00
// Check that the bindings for this struct are still all valid.
2024-01-11 04:24:45 -05:00
TArray < FStateTreePropertyPathBinding > CopyBindings ;
TArray < FStateTreePropertyPathBinding > ReferenceBindings ;
if ( ! GetAndValidateBindings ( StructDesc , InstanceDataView , CopyBindings , ReferenceBindings ) )
2022-05-31 04:51:18 -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 ;
2024-01-11 04:24:45 -05:00
if ( ! BindingsCompiler . CompileBatch ( StructDesc , CopyBindings , BatchIndex ) )
{
return false ;
}
2024-02-14 09:04:52 -05:00
if ( ! BindingsCompiler . CompileReferences ( StructDesc , ReferenceBindings , InstanceDataView , IDToStructValue ) )
2022-05-31 04:51:18 -04:00
{
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 ) ;
2023-01-23 12:48:04 -05:00
2022-05-31 04:51:18 -04:00
return true ;
}
2023-11-22 04:08:33 -05:00
bool FStateTreeCompiler : : CreateEvaluator ( const FStateTreeEditorNode & EvalNode , const FStateTreeDataHandle EvalDataHandle )
2022-05-31 04:51:18 -04:00
{
// Silently ignore empty nodes.
if ( ! EvalNode . Node . IsValid ( ) )
{
return true ;
}
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
FStateTreeBindableStructDesc StructDesc ;
StructDesc . ID = EvalNode . ID ;
2024-04-05 03:37:02 -04:00
StructDesc . Name = EvalNode . GetName ( ) ;
2022-05-31 04:51:18 -04:00
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
2023-06-15 11:38:28 -04:00
IDToNode . Add ( EvalNode . ID , Nodes . Num ( ) ) ;
2023-01-10 15:44:28 -05:00
FInstancedStruct & Node = Nodes . Add_GetRef ( EvalNode . Node ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( Node ) ;
2022-05-31 04:51:18 -04:00
FStateTreeEvaluatorBase & Eval = Node . GetMutable < FStateTreeEvaluatorBase > ( ) ;
2023-02-10 07:22:48 -05:00
FStateTreeDataView InstanceDataView ;
2024-04-05 03:37:02 -04:00
// Update eval name as description for runtime.
Eval . Name = EvalNode . GetName ( ) ;
2022-05-31 04:51:18 -04:00
if ( EvalNode . Instance . IsValid ( ) )
{
// Struct Instance
const int32 InstanceIndex = InstanceStructs . Add ( EvalNode . Instance ) ;
2023-04-05 09:28:17 -04:00
InstantiateStructSubobjects ( InstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = EvalNode . Instance . GetScriptStruct ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Eval . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Eval . InstanceDataHandle = EvalDataHandle ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( InstanceStructs [ InstanceIndex ] ) ;
2022-05-31 04:51:18 -04:00
}
else
{
// Object Instance
check ( EvalNode . InstanceObject ! = nullptr ) ;
UObject * Instance = DuplicateObject ( EvalNode . InstanceObject , StateTree ) ;
2023-11-22 04:08:33 -05:00
FInstancedStruct Wrapper ;
Wrapper . InitializeAs < FStateTreeInstanceObjectWrapper > ( Instance ) ;
const int32 InstanceIndex = InstanceStructs . Add ( MoveTemp ( Wrapper ) ) ;
2022-05-31 04:51:18 -04:00
// Create binding source struct descriptor.
StructDesc . Struct = Instance - > GetClass ( ) ;
if ( const auto Validation = UE : : StateTree : : Compiler : : IsValidIndex16 ( InstanceIndex ) ; Validation . DidFail ( ) )
{
Validation . Log ( Log , TEXT ( " InstanceIndex " ) , StructDesc ) ;
return false ;
}
2023-11-22 04:08:33 -05:00
Eval . InstanceTemplateIndex = FStateTreeIndex16 ( InstanceIndex ) ;
Eval . InstanceDataHandle = EvalDataHandle . ToObjectSource ( ) ;
2023-02-10 07:22:48 -05:00
InstanceDataView = FStateTreeDataView ( Instance ) ;
2022-05-31 04:51:18 -04:00
}
2023-02-10 07:22:48 -05:00
2023-11-22 04:08:33 -05:00
StructDesc . DataHandle = Eval . InstanceDataHandle ;
2023-02-10 07:22:48 -05:00
if ( ! CompileAndValidateNode ( nullptr , StructDesc , Node , InstanceDataView ) )
{
return false ;
}
2022-05-31 04:51:18 -04:00
// Mark the instance as binding source.
2023-11-22 04:08:33 -05:00
BindingsCompiler . AddSourceStruct ( StructDesc ) ;
2022-05-31 04:51:18 -04:00
// Check that the bindings for this struct are still all valid.
2024-01-11 04:24:45 -05:00
TArray < FStateTreePropertyPathBinding > CopyBindings ;
TArray < FStateTreePropertyPathBinding > ReferenceBindings ;
if ( ! GetAndValidateBindings ( StructDesc , InstanceDataView , CopyBindings , ReferenceBindings ) )
2022-05-31 04:51:18 -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 ;
2024-01-11 04:24:45 -05:00
if ( ! BindingsCompiler . CompileBatch ( StructDesc , CopyBindings , BatchIndex ) )
{
return false ;
}
2024-02-14 09:04:52 -05:00
if ( ! BindingsCompiler . CompileReferences ( StructDesc , ReferenceBindings , InstanceDataView , IDToStructValue ) )
2022-05-31 04:51:18 -04:00
{
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 ) ;
return true ;
}
2024-02-14 09:04:52 -05:00
bool FStateTreeCompiler : : IsPropertyOfTypeOrChild ( UScriptStruct & Type , const FStateTreeBindableStructDesc & Struct , FStateTreePropertyPath Path ) const
2022-05-31 04:51:18 -04:00
{
2023-02-10 07:22:48 -05:00
TArray < FStateTreePropertyPathIndirection > Indirection ;
const bool bResolved = Path . ResolveIndirections ( Struct . Struct , Indirection ) ;
if ( bResolved & & Indirection . Num ( ) > 0 )
2022-05-31 04:51:18 -04:00
{
2023-02-10 07:22:48 -05:00
check ( Indirection . Last ( ) . GetProperty ( ) ) ;
if ( const FProperty * OwnerProperty = Indirection . Last ( ) . GetProperty ( ) - > GetOwnerProperty ( ) )
2022-05-31 04:51:18 -04:00
{
if ( const FStructProperty * OwnerStructProperty = CastField < FStructProperty > ( OwnerProperty ) )
{
2024-02-14 09:04:52 -05:00
return OwnerStructProperty - > Struct - > IsChildOf ( & Type ) ;
2022-05-31 04:51:18 -04:00
}
}
}
2024-01-11 04:24:45 -05:00
return false ;
2022-05-31 04:51:18 -04:00
}
2023-02-10 07:22:48 -05:00
bool FStateTreeCompiler : : ValidateStructRef ( const FStateTreeBindableStructDesc & SourceStruct , FStateTreePropertyPath SourcePath ,
const FStateTreeBindableStructDesc & TargetStruct , FStateTreePropertyPath TargetPath ) const
2022-06-29 04:52:18 -04:00
{
2023-02-10 07:22:48 -05:00
FString ResolveError ;
TArray < FStateTreePropertyPathIndirection > TargetIndirection ;
if ( ! TargetPath . ResolveIndirections ( TargetStruct . Struct , TargetIndirection , & ResolveError ) )
2022-06-29 04:52:18 -04:00
{
// This will later be reported by the bindings compiler.
2023-06-26 06:34:49 -04:00
Log . Reportf ( EMessageSeverity : : Error , TargetStruct , TEXT ( " Failed to resolve binding path in %s: %s " ) , * TargetStruct . ToString ( ) , * ResolveError ) ;
2023-02-10 07:22:48 -05:00
return false ;
2022-06-29 04:52:18 -04:00
}
2023-02-10 07:22:48 -05:00
const FProperty * TargetLeafProperty = TargetIndirection . Num ( ) > 0 ? TargetIndirection . Last ( ) . GetProperty ( ) : nullptr ;
2022-06-29 04:52:18 -04:00
// 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 ,
2023-06-26 06:34:49 -04:00
TEXT ( " Could not find base struct type '%s' for target %s'. " ) ,
* TargetBaseStructName , * UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , TargetPath ) ) ;
2022-06-29 04:52:18 -04:00
return false ;
}
2023-02-10 07:22:48 -05:00
TArray < FStateTreePropertyPathIndirection > SourceIndirection ;
if ( ! SourcePath . ResolveIndirections ( SourceStruct . Struct , SourceIndirection , & ResolveError ) )
2022-06-29 04:52:18 -04:00
{
// This will later be reported by the bindings compiler.
2023-06-26 06:34:49 -04:00
Log . Reportf ( EMessageSeverity : : Error , SourceStruct , TEXT ( " Failed to resolve binding path in %s: %s " ) , * SourceStruct . ToString ( ) , * ResolveError ) ;
2023-02-10 07:22:48 -05:00
return false ;
2022-06-29 04:52:18 -04:00
}
2023-02-10 07:22:48 -05:00
const FProperty * SourceLeafProperty = SourceIndirection . Num ( ) > 0 ? SourceIndirection . Last ( ) . GetProperty ( ) : nullptr ;
2022-06-29 04:52:18 -04:00
// 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 ,
2023-06-26 06:34:49 -04:00
TEXT ( " Could not find base struct '%s' for binding source %s. " ) ,
* SourceBaseStructName , * UE : : StateTree : : GetDescAndPathAsString ( SourceStruct , SourcePath ) ) ;
2022-06-29 04:52:18 -04:00
return false ;
}
if ( SourceBaseStruct - > IsChildOf ( TargetBaseStruct ) = = false )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Type mismatch between source %s and target %s types, '%s' is not child of '%s'. " ) ,
* UE : : StateTree : : GetDescAndPathAsString ( SourceStruct , SourcePath ) ,
* UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , TargetPath ) ,
2022-06-29 04:52:18 -04:00
* GetNameSafe ( SourceBaseStruct ) , * GetNameSafe ( TargetBaseStruct ) ) ;
return false ;
}
}
else
{
if ( ! SourceStructProperty - > Struct | | SourceStructProperty - > Struct - > IsChildOf ( TargetBaseStruct ) = = false )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Type mismatch between source %s and target %s types, '%s' is not child of '%s'. " ) ,
* UE : : StateTree : : GetDescAndPathAsString ( SourceStruct , SourcePath ) ,
* UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , TargetPath ) ,
2022-06-29 04:52:18 -04:00
* GetNameSafe ( SourceStructProperty - > Struct ) , * GetNameSafe ( TargetBaseStruct ) ) ;
return false ;
}
}
return true ;
}
2023-02-10 07:22:48 -05:00
2024-01-11 04:24:45 -05:00
bool FStateTreeCompiler : : GetAndValidateBindings ( const FStateTreeBindableStructDesc & TargetStruct , FStateTreeDataView TargetValue , TArray < FStateTreePropertyPathBinding > & OutCopyBindings , TArray < FStateTreePropertyPathBinding > & OutReferenceBindings ) const
2022-05-31 04:51:18 -04:00
{
2023-01-23 12:48:04 -05:00
check ( EditorData ) ;
2024-01-11 04:24:45 -05:00
OutCopyBindings . Reset ( ) ;
OutReferenceBindings . Reset ( ) ;
2023-11-22 04:08:33 -05:00
// If target struct is not set, nothing to do.
2022-09-23 19:58:36 -04:00
if ( TargetStruct . Struct = = nullptr )
{
2023-11-22 04:08:33 -05:00
return true ;
2022-09-23 19:58:36 -04:00
}
2023-11-22 04:08:33 -05:00
2023-02-10 07:22:48 -05:00
for ( FStateTreePropertyPathBinding & Binding : EditorData - > EditorBindings . GetMutableBindings ( ) )
2022-05-31 04:51:18 -04:00
{
2023-02-10 07:22:48 -05:00
if ( Binding . GetTargetPath ( ) . GetStructID ( ) ! = TargetStruct . ID )
2022-05-31 04:51:18 -04:00
{
continue ;
}
// Source must be one of the source structs we have discovered in the tree.
2023-02-10 07:22:48 -05:00
const FGuid SourceStructID = Binding . GetSourcePath ( ) . GetStructID ( ) ;
2023-11-22 04:08:33 -05:00
const FStateTreeBindableStructDesc * SourceStruct = BindingsCompiler . GetSourceStructDescByID ( SourceStructID ) ;
if ( ! SourceStruct )
2022-05-31 04:51:18 -04:00
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Failed to find binding source property '%s' for target %s. " ) ,
* Binding . GetSourcePath ( ) . ToString ( ) , * UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , Binding . GetTargetPath ( ) ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-02-10 07:22:48 -05:00
// Update path instance types from latest data. E.g. binding may have been created for instanced object of type FooB, and changed to FooA.
// @todo: not liking how this mutates the Binding.TargetPath, but currently we dont track well the instanced object changes.
2023-06-21 10:25:47 -04:00
if ( ! Binding . GetMutableTargetPath ( ) . UpdateSegmentsFromValue ( TargetValue ) )
2023-02-10 07:22:48 -05:00
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Malformed target property path for binding source property '%s' for target %s. " ) ,
* Binding . GetSourcePath ( ) . ToString ( ) , * UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , Binding . GetTargetPath ( ) ) ) ;
2023-02-10 07:22:48 -05:00
return false ;
}
2022-05-31 04:51:18 -04:00
// Source must be accessible by the target struct via all execution paths.
TArray < FStateTreeBindableStructDesc > AccessibleStructs ;
2023-02-10 07:22:48 -05:00
EditorData - > GetAccessibleStructs ( Binding . GetTargetPath ( ) . GetStructID ( ) , AccessibleStructs ) ;
2022-05-31 04:51:18 -04:00
const bool bSourceAccessible = AccessibleStructs . ContainsByPredicate ( [ SourceStructID ] ( const FStateTreeBindableStructDesc & Structs )
{
return ( Structs . ID = = SourceStructID ) ;
} ) ;
if ( ! bSourceAccessible )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Property at %s cannot be bound to %s, because the binding source %s is not updated before %s in the tree. " ) ,
2023-11-22 04:08:33 -05:00
* UE : : StateTree : : GetDescAndPathAsString ( * SourceStruct , Binding . GetSourcePath ( ) ) ,
2023-06-26 06:34:49 -04:00
* UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , Binding . GetTargetPath ( ) ) ,
2023-11-22 04:08:33 -05:00
* SourceStruct - > ToString ( ) , * TargetStruct . ToString ( ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-02-10 07:22:48 -05:00
2023-06-07 09:02:19 -04:00
if ( ! IDToStructValue . Contains ( SourceStructID ) )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Failed to find value for binding source property '%s' for target %s. " ) ,
* Binding . GetSourcePath ( ) . ToString ( ) , * UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , Binding . GetTargetPath ( ) ) ) ;
2023-06-07 09:02:19 -04:00
return false ;
}
2023-06-08 08:57:12 -04:00
// Update the source structs only if we have value for it. For some sources (e.g. context structs) we know only type, and in that case there are no instance structs.
const FStateTreeDataView SourceValue = IDToStructValue [ SourceStructID ] ;
if ( SourceValue . IsValid ( ) )
2023-06-07 09:02:19 -04:00
{
2023-06-21 10:25:47 -04:00
if ( ! Binding . GetMutableSourcePath ( ) . UpdateSegmentsFromValue ( SourceValue ) )
2023-06-08 08:57:12 -04:00
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2023-06-26 06:34:49 -04:00
TEXT ( " Malformed target property path for binding source property '%s' for source %s. " ) ,
* Binding . GetSourcePath ( ) . ToString ( ) , * UE : : StateTree : : GetDescAndPathAsString ( TargetStruct , Binding . GetTargetPath ( ) ) ) ;
2023-06-08 08:57:12 -04:00
return false ;
}
2023-06-07 09:02:19 -04:00
}
2023-11-22 04:08:33 -05:00
if ( ! SourceStruct - > DataHandle . IsValid ( ) )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Malformed source'%s for property binding property '%s'. " ) ,
* UE : : StateTree : : GetDescAndPathAsString ( * SourceStruct , Binding . GetSourcePath ( ) ) , * Binding . GetSourcePath ( ) . ToString ( ) ) ;
return false ;
}
FStateTreePropertyPathBinding BindingCopy ( Binding ) ;
BindingCopy . SetSourceDataHandle ( SourceStruct - > DataHandle ) ;
2022-05-31 04:51:18 -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.
2024-02-14 09:04:52 -05:00
const bool bSourceIsAnyEnum = IsPropertyOfTypeOrChild ( * TBaseStructure < FStateTreeAnyEnum > : : Get ( ) , * SourceStruct , Binding . GetSourcePath ( ) ) ;
const bool bTargetIsAnyEnum = IsPropertyOfTypeOrChild ( * TBaseStructure < FStateTreeAnyEnum > : : Get ( ) , TargetStruct , Binding . GetTargetPath ( ) ) ;
2022-05-31 04:51:18 -04:00
if ( bSourceIsAnyEnum | | bTargetIsAnyEnum )
{
if ( bSourceIsAnyEnum )
{
2023-11-22 04:08:33 -05:00
BindingCopy . GetMutableSourcePath ( ) . AddPathSegment ( GET_MEMBER_NAME_STRING_CHECKED ( FStateTreeAnyEnum , Value ) ) ;
2022-05-31 04:51:18 -04:00
}
if ( bTargetIsAnyEnum )
{
2023-11-22 04:08:33 -05:00
BindingCopy . GetMutableTargetPath ( ) . AddPathSegment ( GET_MEMBER_NAME_STRING_CHECKED ( FStateTreeAnyEnum , Value ) ) ;
2022-05-31 04:51:18 -04:00
}
}
2022-06-29 04:52:18 -04:00
2024-02-14 09:04:52 -05:00
if ( IsPropertyOfTypeOrChild ( * FStateTreePropertyRef : : StaticStruct ( ) , TargetStruct , Binding . GetTargetPath ( ) ) )
2024-01-11 04:24:45 -05:00
{
OutReferenceBindings . Add ( BindingCopy ) ;
}
else
{
OutCopyBindings . Add ( BindingCopy ) ;
}
2022-06-29 04:52:18 -04:00
// Check if the bindings is for struct ref and validate the types.
2023-11-22 04:08:33 -05:00
if ( ! ValidateStructRef ( * SourceStruct , Binding . GetSourcePath ( ) , TargetStruct , Binding . GetTargetPath ( ) ) )
2022-06-29 04:52:18 -04:00
{
return false ;
}
2022-05-31 04:51:18 -04:00
}
2022-09-19 19:47:11 -04:00
2024-01-11 04:24:45 -05:00
auto IsPropertyBound = [ ] ( const FName & PropertyName , TConstArrayView < FStateTreePropertyPathBinding > Bindings )
2022-09-19 19:47:11 -04:00
{
2024-01-11 04:24:45 -05:00
return Bindings . ContainsByPredicate ( [ & PropertyName ] ( const FStateTreePropertyPathBinding & Binding )
2022-09-19 19:47:11 -04:00
{
2022-10-26 13:01:21 -04:00
// We're looping over just the first level of properties on the struct, so we assume that the path is just one item
// (or two in case of AnyEnum, because we expand the path to Property.Value, see code above).
2023-02-10 07:22:48 -05:00
return Binding . GetTargetPath ( ) . GetSegments ( ) . Num ( ) > = 1 & & Binding . GetTargetPath ( ) . GetSegments ( ) [ 0 ] . GetName ( ) = = PropertyName ;
2022-09-19 19:47:11 -04:00
} ) ;
} ;
bool bResult = true ;
// Validate that Input and Context bindings
2023-12-08 07:31:01 -05:00
for ( TFieldIterator < FProperty > It ( TargetStruct . Struct ) ; It ; + + It )
2022-09-19 19:47:11 -04:00
{
const FProperty * Property = * It ;
2022-09-21 10:45:14 -04:00
check ( Property ) ;
2023-02-10 07:22:48 -05:00
const FName PropertyName = Property - > GetFName ( ) ;
2024-01-11 04:24:45 -05:00
if ( UE : : StateTree : : PropertyRefHelpers : : IsPropertyRef ( * Property ) )
2022-09-19 19:47:11 -04:00
{
2024-02-14 09:04:52 -05:00
TArray < FStateTreePropertyPathIndirection > TargetIndirections ;
FStateTreePropertyPath TargetPath ( TargetStruct . ID , PropertyName ) ;
if ( ! TargetPath . ResolveIndirectionsWithValue ( TargetValue , TargetIndirections ) )
2022-09-19 19:47:11 -04:00
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2024-02-14 09:04:52 -05:00
TEXT ( " Couldn't resolve path to '%s' for target %s. " ) ,
* PropertyName . ToString ( ) , * TargetStruct . ToString ( ) ) ;
bResult = false ;
}
else
{
const void * PropertyRef = TargetIndirections . Last ( ) . GetPropertyAddress ( ) ;
const bool bIsOptional = UE : : StateTree : : PropertyRefHelpers : : IsPropertyRefMarkedAsOptional ( * Property , PropertyRef ) ;
if ( bIsOptional = = false & & ! IsPropertyBound ( PropertyName , OutReferenceBindings ) )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
2024-01-11 04:24:45 -05:00
TEXT ( " Property reference '%s' on % s is expected to have a binding. " ) ,
2023-06-26 06:34:49 -04:00
* PropertyName . ToString ( ) , * TargetStruct . ToString ( ) ) ;
2022-09-19 19:47:11 -04:00
bResult = false ;
2024-02-14 09:04:52 -05:00
}
2024-01-11 04:24:45 -05:00
}
}
else
{
2024-02-14 09:04:52 -05:00
const bool bIsOptional = UE : : StateTree : : PropertyHelpers : : HasOptionalMetadata ( * Property ) ;
2024-01-11 04:24:45 -05:00
const EStateTreePropertyUsage Usage = UE : : StateTree : : GetUsageFromMetaData ( Property ) ;
if ( Usage = = EStateTreePropertyUsage : : Input )
{
// Make sure that an Input property is bound unless marked optional.
if ( bIsOptional = = false & & ! IsPropertyBound ( PropertyName , OutCopyBindings ) )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Input property '%s' on %s is expected to have a binding. " ) ,
* PropertyName . ToString ( ) , * TargetStruct . ToString ( ) ) ;
bResult = false ;
}
}
else if ( Usage = = EStateTreePropertyUsage : : Context )
{
// Make sure that an Context property is manually or automatically bound.
const UStruct * ContextObjectType = nullptr ;
if ( const FStructProperty * StructProperty = CastField < FStructProperty > ( Property ) )
{
ContextObjectType = StructProperty - > Struct ;
}
else if ( const FObjectPropertyBase * ObjectProperty = CastField < FObjectPropertyBase > ( Property ) )
{
ContextObjectType = ObjectProperty - > PropertyClass ;
}
if ( ContextObjectType = = nullptr )
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " The type of Context property '%s' on %s is expected to be Object Reference or Struct. " ) ,
* PropertyName . ToString ( ) , * TargetStruct . ToString ( ) ) ;
bResult = false ;
continue ;
}
const bool bIsBound = IsPropertyBound ( PropertyName , OutCopyBindings ) ;
if ( ! bIsBound )
{
const FStateTreeBindableStructDesc Desc = EditorData - > FindContextData ( ContextObjectType , PropertyName . ToString ( ) ) ;
if ( Desc . IsValid ( ) )
{
// Add automatic binding to Context data.
OutCopyBindings . Emplace ( FStateTreePropertyPath ( Desc . ID ) , FStateTreePropertyPath ( TargetStruct . ID , PropertyName ) ) ;
}
else
{
Log . Reportf ( EMessageSeverity : : Error , TargetStruct ,
TEXT ( " Could not find matching Context object for Context property '%s' on '%s'. Property must have manual binding. " ) ,
* PropertyName . ToString ( ) , * TargetStruct . ToString ( ) ) ;
bResult = false ;
}
2022-09-19 19:47:11 -04:00
}
}
}
}
return bResult ;
2022-05-31 04:51:18 -04:00
}
2023-04-05 09:28:17 -04:00
void FStateTreeCompiler : : InstantiateStructSubobjects ( FStructView Struct )
{
check ( StateTree ) ;
check ( EditorData ) ;
// Empty struct, nothing to do.
if ( ! Struct . IsValid ( ) )
{
return ;
}
for ( TPropertyValueIterator < FProperty > It ( Struct . GetScriptStruct ( ) , Struct . GetMemory ( ) ) ; It ; + + It )
{
if ( const FObjectProperty * ObjectProperty = CastField < FObjectProperty > ( It - > Key ) )
{
// Duplicate instanced objects.
if ( ObjectProperty - > HasAnyPropertyFlags ( CPF_InstancedReference ) )
{
if ( UObject * Object = ObjectProperty - > GetObjectPropertyValue ( It - > Value ) )
{
UObject * OuterObject = Object - > GetOuter ( ) ;
// If the instanced object was created as Editor Data as outer,
// change the outer to State Tree to prevent references to editor only data.
if ( Object - > IsInOuter ( EditorData ) )
{
OuterObject = StateTree ;
}
UObject * DuplicatedObject = DuplicateObject ( Object , OuterObject ) ;
ObjectProperty - > SetObjectPropertyValue ( const_cast < void * > ( It - > Value ) , DuplicatedObject ) ;
}
}
}
if ( const FStructProperty * StructProperty = CastField < FStructProperty > ( It - > Key ) )
{
// If we encounter instanced struct, recursively handle it too.
if ( StructProperty - > Struct = = TBaseStructure < FInstancedStruct > : : Get ( ) )
{
FInstancedStruct & InstancedStruct = * static_cast < FInstancedStruct * > ( const_cast < void * > ( It - > Value ) ) ;
InstantiateStructSubobjects ( InstancedStruct ) ;
}
}
}
}