2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTree.h"
# include "StateTreeEvaluatorBase.h"
2021-10-27 06:11:44 -04:00
# include "StateTreeTaskBase.h"
2021-09-28 13:33:00 -04:00
# include "CoreMinimal.h"
2021-11-12 05:49:31 -05:00
# include "StateTreeConditionBase.h"
2021-09-28 13:33:00 -04:00
# include "StateTreeDelegates.h"
2021-11-24 04:26:29 -05:00
# if WITH_EDITOR
# include "Editor.h"
# endif
2021-09-28 13:33:00 -04:00
UStateTree : : UStateTree ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
2021-11-24 04:26:29 -05:00
# if WITH_EDITOR
FEditorDelegates : : PreBeginPIE . AddUObject ( this , & UStateTree : : OnPIEStarted ) ;
# endif
}
UStateTree : : ~ UStateTree ( )
{
# if WITH_EDITOR
FEditorDelegates : : PreBeginPIE . RemoveAll ( this ) ;
# endif
2021-09-28 13:33:00 -04:00
}
bool UStateTree : : IsValidStateTree ( ) const
{
// Valid tree must have at least one state.
return States . Num ( ) > 0 ;
}
# if WITH_EDITOR
2021-11-24 04:26:29 -05:00
void UStateTree : : OnPIEStarted ( const bool bIsSimulating )
{
PropertyBindings . ResolvePaths ( ) ;
Link ( ) ;
InitInstanceStorageType ( ) ;
}
2021-09-28 13:33:00 -04:00
void UStateTree : : ResetBaked ( )
{
States . Reset ( ) ;
Transitions . Reset ( ) ;
2021-11-12 05:49:31 -05:00
Items . Reset ( ) ;
Instances . Reset ( ) ;
2021-11-24 04:26:29 -05:00
InstanceObjects . Reset ( ) ;
2021-11-12 05:49:31 -05:00
InstanceStorageStruct = nullptr ;
InstanceStorageOffsets . Reset ( ) ;
InstanceStorageDefaultValue . Reset ( ) ;
ExternalDataDescs . Reset ( ) ;
2021-09-28 13:33:00 -04:00
PropertyBindings . Reset ( ) ;
2021-11-03 07:03:18 -04:00
2021-11-12 05:49:31 -05:00
NumDataViews = 0 ;
ExternalDataBaseIndex = 0 ;
2021-09-28 13:33:00 -04:00
}
void UStateTree : : PostEditChangeProperty ( struct FPropertyChangedEvent & PropertyChangedEvent )
{
if ( PropertyChangedEvent . MemberProperty & & PropertyChangedEvent . Property )
{
if ( PropertyChangedEvent . MemberProperty - > GetFName ( ) = = GET_MEMBER_NAME_CHECKED ( UStateTree , Schema ) )
{
2021-10-21 04:12:07 -04:00
UE : : StateTree : : Delegates : : OnSchemaChanged . Broadcast ( * this ) ;
2021-09-28 13:33:00 -04:00
}
}
}
void UStateTree : : GetAssetRegistryTags ( TArray < FAssetRegistryTag > & OutTags ) const
{
static const FName SchemaTag ( TEXT ( " Schema " ) ) ;
const FString SchemaClassName = Schema ? Schema - > GetClass ( ) - > GetName ( ) : TEXT ( " " ) ;
OutTags . Add ( FAssetRegistryTag ( SchemaTag , SchemaClassName , FAssetRegistryTag : : TT_Alphabetical ) ) ;
Super : : GetAssetRegistryTags ( OutTags ) ;
}
# endif // WITH_EDITOR
void UStateTree : : PostLoad ( )
{
Super : : PostLoad ( ) ;
2021-10-27 06:11:44 -04:00
2021-09-28 13:33:00 -04:00
PropertyBindings . ResolvePaths ( ) ;
2021-10-27 06:11:44 -04:00
Link ( ) ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-24 04:26:29 -05:00
InitInstanceStorageType ( ) ;
2021-09-28 13:33:00 -04:00
# else
// Item offsets still need to be calculated in non editor target since the struct sizes might be different.
2021-11-12 05:49:31 -05:00
checkf ( InstanceStorageOffsets . Num ( ) = = 0 , TEXT ( " RuntimeStorageOffsets is transient and should only be computed once. " ) ) ;
2021-09-28 13:33:00 -04:00
2021-11-12 05:49:31 -05:00
for ( TFieldIterator < FProperty > PropertyIt ( InstanceStorageStruct ) ; PropertyIt ; + + PropertyIt )
2021-09-28 13:33:00 -04:00
{
FStructProperty * StructProperty = CastField < FStructProperty > ( * PropertyIt ) ;
2021-11-12 05:49:31 -05:00
checkf ( StructProperty , TEXT ( " InstanceStorageStruct is expected to only contain Struct properties " ) ) ;
InstanceStorageOffsets . Emplace ( StructProperty - > Struct , StructProperty - > GetOffset_ForInternal ( ) ) ;
2021-09-28 13:33:00 -04:00
}
# endif // WITH_EDITOR
}
void UStateTree : : BeginDestroy ( )
{
Super : : BeginDestroy ( ) ;
// Destroy the runtime storage before the UScriptStruct it uses gets removed.
2021-11-12 05:49:31 -05:00
InstanceStorageDefaultValue . Reset ( ) ;
2021-09-28 13:33:00 -04:00
}
void UStateTree : : ResolvePropertyPaths ( )
{
// TODO: find better hook when to call this. Currently this gets called from StateTreeComponent, it should be called once PostLoad() or when entering SIE/PIE in editor.
PropertyBindings . ResolvePaths ( ) ;
}
2021-10-27 06:11:44 -04:00
void UStateTree : : Link ( )
{
FStateTreeLinker Linker ;
2021-11-12 05:49:31 -05:00
ExternalDataBaseIndex = PropertyBindings . GetSourceStructNum ( ) ;
Linker . SetExternalDataBaseIndex ( ExternalDataBaseIndex ) ;
2021-11-03 07:03:18 -04:00
2021-11-12 05:49:31 -05:00
for ( FInstancedStruct & Item : Items )
2021-10-27 06:11:44 -04:00
{
2021-11-12 05:49:31 -05:00
if ( FStateTreeEvaluatorBase * Eval = Item . GetMutablePtr < FStateTreeEvaluatorBase > ( ) )
2021-10-27 06:11:44 -04:00
{
2021-11-12 05:49:31 -05:00
Linker . SetCurrentInstanceDataType ( Eval - > GetInstanceDataType ( ) , Eval - > DataViewIndex ) ;
2021-10-27 06:11:44 -04:00
Eval - > Link ( Linker ) ;
}
2021-11-12 05:49:31 -05:00
else if ( FStateTreeTaskBase * Task = Item . GetMutablePtr < FStateTreeTaskBase > ( ) )
2021-10-27 06:11:44 -04:00
{
2021-11-12 05:49:31 -05:00
Linker . SetCurrentInstanceDataType ( Task - > GetInstanceDataType ( ) , Task - > DataViewIndex ) ;
2021-10-27 06:11:44 -04:00
Task - > Link ( Linker ) ;
}
2021-11-12 05:49:31 -05:00
else if ( FStateTreeConditionBase * Cond = Item . GetMutablePtr < FStateTreeConditionBase > ( ) )
{
Linker . SetCurrentInstanceDataType ( Cond - > GetInstanceDataType ( ) , Cond - > DataViewIndex ) ;
Cond - > Link ( Linker ) ;
}
2021-10-27 06:11:44 -04:00
}
2021-11-12 05:49:31 -05:00
ExternalDataDescs = Linker . GetExternalDataDescs ( ) ;
2021-11-03 07:03:18 -04:00
2021-11-12 05:49:31 -05:00
NumDataViews = ExternalDataBaseIndex + ExternalDataDescs . Num ( ) ;
2021-10-27 06:11:44 -04:00
}
2021-11-24 04:26:29 -05:00
void UStateTree : : InitInstanceStorageType ( )
2021-09-28 13:33:00 -04:00
{
2021-11-12 05:49:31 -05:00
InstanceStorageStruct = nullptr ;
InstanceStorageOffsets . Reset ( ) ;
InstanceStorageDefaultValue . Reset ( ) ;
2021-09-28 13:33:00 -04:00
// Check that the items are valid before trying to create the type.
// The structs can become invalid i.e. because of missing type.
bool bValid = true ;
2021-11-12 05:49:31 -05:00
for ( const FInstancedStruct & Item : Instances )
2021-09-28 13:33:00 -04:00
{
if ( ! Item . IsValid ( ) )
{
bValid = false ;
break ;
}
}
if ( ! bValid )
{
return ;
}
2021-11-12 05:49:31 -05:00
const FString StructName = GetName ( ) + TEXT ( " _InstanceStorage " ) ;
2021-09-28 13:33:00 -04:00
// Remove existing struct of same name.
UScriptStruct * OldStruct = FindObject < UScriptStruct > ( this , * StructName ) ;
if ( OldStruct )
{
const FString OldStructName = MakeUniqueObjectName ( OldStruct - > GetOuter ( ) , OldStruct - > GetClass ( ) , * FString : : Printf ( TEXT ( " %s_TRASH " ) , * OldStruct - > GetName ( ) ) ) . ToString ( ) ;
OldStruct - > SetFlags ( RF_NewerVersionExists ) ;
OldStruct - > ClearFlags ( RF_Public | RF_Standalone ) ;
OldStruct - > SetStructTrashed ( /*bIsTrash*/ true ) ;
OldStruct - > Rename ( * OldStructName , nullptr , REN_DontCreateRedirectors | REN_ForceNoResetLoaders ) ;
}
UScriptStruct * NewStruct = NewObject < UScriptStruct > ( this , * StructName , RF_Public ) ;
if ( Schema )
{
NewStruct - > SetSuperStruct ( Schema - > GetStorageSuperStruct ( ) ) ;
}
// Append all evaluators and tasks.
// Since properties are stored in linked list, add in reverse order so that we retain the correct order in output.
TArray < FStructProperty * > NewProperties ;
2021-11-12 05:49:31 -05:00
NewProperties . SetNumZeroed ( Instances . Num ( ) ) ;
for ( int32 ItemIndex = Instances . Num ( ) - 1 ; ItemIndex > = 0 ; ItemIndex - - )
2021-09-28 13:33:00 -04:00
{
2021-11-12 05:49:31 -05:00
const FInstancedStruct & ItemPtr = Instances [ ItemIndex ] ;
2021-09-28 13:33:00 -04:00
UScriptStruct * ItemStruct = const_cast < UScriptStruct * > ( ItemPtr . GetScriptStruct ( ) ) ;
FName PropName ( FString : : Printf ( TEXT ( " %s%d " ) , * ItemStruct - > GetName ( ) , NewProperties . Num ( ) ) ) ;
FStructProperty * NewStructProperty = new FStructProperty ( NewStruct , PropName , RF_Public ) ;
NewStructProperty - > Struct = ItemStruct ;
NewStruct - > AddCppProperty ( NewStructProperty ) ;
NewProperties [ ItemIndex ] = NewStructProperty ;
}
// Finalize the struct
NewStruct - > Bind ( ) ;
NewStruct - > StaticLink ( /*bRelinkExistingProperties*/ true ) ;
// Store item offsets for fast struct view creation at runtime.
2021-11-12 05:49:31 -05:00
TArray < FStateTreeInstanceStorageOffset > NewOffsets ;
2021-09-28 13:33:00 -04:00
for ( FStructProperty * StructProperty : NewProperties )
{
2021-11-12 05:49:31 -05:00
NewOffsets . Emplace ( StructProperty - > Struct , StructProperty - > GetOffset_ForInternal ( ) ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
check ( Instances . Num ( ) = = NewOffsets . Num ( ) ) ;
2021-09-28 13:33:00 -04:00
// Instantiate default value
2021-11-12 05:49:31 -05:00
InstanceStorageDefaultValue . InitializeAs ( NewStruct ) ;
for ( int i = 0 ; i < NewOffsets . Num ( ) ; i + + )
2021-09-28 13:33:00 -04:00
{
2021-11-12 05:49:31 -05:00
const FStateTreeInstanceStorageOffset & Item = NewOffsets [ i ] ;
uint8 * Dest = InstanceStorageDefaultValue . GetMutableMemory ( ) + Item . Offset ;
Item . Struct - > CopyScriptStruct ( Dest , Instances [ i ] . GetMemory ( ) ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
InstanceStorageOffsets = NewOffsets ;
InstanceStorageStruct = NewStruct ;
2021-09-28 13:33:00 -04:00
}