2022-04-13 16:06:35 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ViewModel/MVVMViewModelBlueprintCompiler.h"
# include "ViewModel/MVVMViewModelBlueprint.h"
# include "ViewModel/MVVMViewModelBlueprintGeneratedClass.h"
# include "MVVMViewModelBase.h"
# include "FieldNotification/CustomizationHelper.h"
# include "FieldNotification/FieldNotificationHelpers.h"
# include "K2Node_FunctionEntry.h"
# include "K2Node_VariableSet.h"
# include "Kismet2/CompilerResultsLog.h"
# include "Kismet2/KismetReinstanceUtilities.h"
# include "MVVMFunctionGraphHelper.h"
# define LOCTEXT_NAMESPACE "ViewModelBlueprintCompiler"
namespace UE : : MVVM
{
static TAutoConsoleVariable < bool > CVarAutogenerateSetter (
TEXT ( " MVVM.Viewmodel.GenerateSetter " ) ,
true ,
TEXT ( " If true, the setter function will be always be autogenerated " ) ,
ECVF_Default ) ;
static TAutoConsoleVariable < bool > CVarAutogenerateOnRep (
TEXT ( " MVVM.Viewmodel.GenerateOnRep " ) ,
true ,
TEXT ( " If true, the OnRep function will be always be autogenerated " ) ,
ECVF_Default ) ;
//////////////////////////////////////////////////////////////////////////
// FViewModelBlueprintCompiler
bool FViewModelBlueprintCompiler : : CanCompile ( const UBlueprint * Blueprint )
{
return Cast < UMVVMViewModelBlueprint > ( Blueprint ) ! = nullptr ;
}
void FViewModelBlueprintCompiler : : Compile ( UBlueprint * Blueprint , const FKismetCompilerOptions & CompileOptions , FCompilerResultsLog & Results )
{
if ( UMVVMViewModelBlueprint * WidgetBlueprint = CastChecked < UMVVMViewModelBlueprint > ( Blueprint ) )
{
FViewModelBlueprintCompilerContext Compiler ( WidgetBlueprint , Results , CompileOptions ) ;
Compiler . Compile ( ) ;
check ( Compiler . NewClass ) ;
}
}
bool FViewModelBlueprintCompiler : : GetBlueprintTypesForClass ( UClass * ParentClass , UClass * & OutBlueprintClass , UClass * & OutBlueprintGeneratedClass ) const
{
if ( ParentClass = = UMVVMViewModelBase : : StaticClass ( ) | | ParentClass - > IsChildOf ( UMVVMViewModelBase : : StaticClass ( ) ) )
{
OutBlueprintClass = UMVVMViewModelBlueprint : : StaticClass ( ) ;
OutBlueprintGeneratedClass = UMVVMViewModelBlueprintGeneratedClass : : StaticClass ( ) ;
return true ;
}
return false ;
}
//////////////////////////////////////////////////////////////////////////
// FViewModelBlueprintCompilerContext
UMVVMViewModelBlueprint * FViewModelBlueprintCompilerContext : : GetViewModelBlueprint ( ) const
{
return Cast < UMVVMViewModelBlueprint > ( Blueprint ) ;
}
FProperty * FViewModelBlueprintCompilerContext : : FindPropertyByNameOnNewClass ( FName PropertyName ) const
{
for ( FField * CurrentField = NewClass - > ChildProperties ; CurrentField ! = nullptr ; CurrentField = CurrentField - > Next )
{
if ( CurrentField - > GetFName ( ) = = PropertyName )
{
2022-05-12 14:55:19 -04:00
return CastField < FProperty > ( CurrentField ) ;
2022-04-13 16:06:35 -04:00
}
}
return nullptr ;
}
void FViewModelBlueprintCompilerContext : : SaveSubObjectsFromCleanAndSanitizeClass ( FSubobjectCollection & SubObjectsToSave , UBlueprintGeneratedClass * ClassToClean )
{
Super : : SaveSubObjectsFromCleanAndSanitizeClass ( SubObjectsToSave , ClassToClean ) ;
// Make sure our typed pointer is set
check ( ClassToClean = = NewClass ) ;
NewViewModelBlueprintClass = CastChecked < UMVVMViewModelBlueprintGeneratedClass > ( ( UObject * ) NewClass ) ;
}
void FViewModelBlueprintCompilerContext : : CleanAndSanitizeClass ( UBlueprintGeneratedClass * ClassToClean , UObject * & InOutOldCDO )
{
Super : : CleanAndSanitizeClass ( ClassToClean , InOutOldCDO ) ;
NewViewModelBlueprintClass - > FieldNotifyNames . Empty ( ) ;
}
void FViewModelBlueprintCompilerContext : : CreateFunctionList ( )
{
for ( const FGeneratedFunction & Function : GeneratedFunctions )
{
2022-05-18 12:54:21 -04:00
if ( Function . Property = = nullptr & & Function . SkelProperty = = nullptr )
2022-04-13 16:06:35 -04:00
{
2022-05-18 12:54:21 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " Internal error. The property '%s' is invalid. Setter and net rep was not generated. " ) , * Function . PropertyName . ToString ( ) ) ) ;
2022-04-13 16:06:35 -04:00
continue ;
}
if ( Function . SetterFunction )
{
2022-05-18 12:54:21 -04:00
if ( ! FunctionGraphHelper : : GenerateViewModelFieldNotifySetter ( * this , Function . SetterFunction , Function . SkelProperty , TEXT ( " NewValue " ) ) )
2022-04-13 16:06:35 -04:00
{
2022-05-18 12:54:21 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " The setter function for '%s' could not be generated. " ) , * Function . PropertyName . ToString ( ) ) ) ;
2022-04-13 16:06:35 -04:00
continue ;
}
}
if ( Function . NetRepFunction )
{
2022-05-18 12:54:21 -04:00
if ( ! FunctionGraphHelper : : GenerateViewModelFielNotifyBroadcast ( * this , Function . NetRepFunction , Function . SkelProperty ) )
2022-04-13 16:06:35 -04:00
{
2022-05-18 12:54:21 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " The net rep function for '%s' could not be generated. " ) , * Function . PropertyName . ToString ( ) ) ) ;
2022-04-13 16:06:35 -04:00
continue ;
}
}
}
Super : : CreateFunctionList ( ) ;
}
void FViewModelBlueprintCompilerContext : : CreateClassVariablesFromBlueprint ( )
{
Super : : CreateClassVariablesFromBlueprint ( ) ;
const FName NAME_MetaDataBlueprintSetter = " BlueprintSetter " ;
const FName NAME_Category = " Category " ;
for ( TFieldIterator < FProperty > PropertyIt ( NewClass , EFieldIterationFlags : : None ) ; PropertyIt ; + + PropertyIt )
{
FProperty * Property = * PropertyIt ;
FName PropertyName = Property - > GetFName ( ) ;
bool bGenerateSetterFunction = CVarAutogenerateSetter . GetValueOnGameThread ( ) | | Property - > HasMetaData ( UE : : FieldNotification : : FCustomizationHelper : : MetaData_FieldNotify ) ;
bool bGenerateReplicatedFunction = CVarAutogenerateOnRep . GetValueOnGameThread ( ) ; // Property->RepNotifyFunc.IsNone();
if ( bGenerateSetterFunction | | bGenerateReplicatedFunction )
{
2022-05-18 12:54:21 -04:00
Property - > SetPropertyFlags ( CPF_DisableEditOnInstance ) ;
2022-04-13 16:06:35 -04:00
2022-05-12 14:55:19 -04:00
const FString SetterName = FString : : Printf ( TEXT ( " Set%s " ) , * Property - > GetName ( ) ) ;
const FString NetRepName = FString : : Printf ( TEXT ( " __RepNotify_%s " ) , * Property - > GetName ( ) ) ;
2022-04-13 16:06:35 -04:00
2022-05-12 14:55:19 -04:00
# if WITH_EDITOR
if ( bGenerateSetterFunction )
2022-04-13 16:06:35 -04:00
{
2022-05-12 14:55:19 -04:00
Property - > SetMetaData ( FBlueprintMetadata : : MD_PropertySetFunction , * SetterName ) ;
2022-04-13 16:06:35 -04:00
}
2022-05-12 14:55:19 -04:00
# endif
if ( CompileOptions . CompileType = = EKismetCompileType : : SkeletonOnly )
2022-04-13 16:06:35 -04:00
{
FGeneratedFunction GeneratedFunction ;
GeneratedFunction . PropertyName = PropertyName ;
GeneratedFunction . SkelProperty = Property ;
2022-05-12 14:55:19 -04:00
if ( GeneratedFunctions . ContainsByPredicate ( [ PropertyName ] ( const FGeneratedFunction & Other )
{
return Other . PropertyName = = PropertyName ;
} ) )
{
MessageLog . Error ( * FString : : Printf ( TEXT ( " Internal error. The functions are already generated for properpty. " ) , * PropertyName . ToString ( ) ) ) ;
continue ;
}
2022-04-13 16:06:35 -04:00
if ( bGenerateSetterFunction )
{
GeneratedFunction . SetterFunction = FunctionGraphHelper : : CreateIntermediateFunctionGraph (
* this
, * SetterName
, ( FUNC_BlueprintCallable | FUNC_Public )
, Property - > GetMetaData ( NAME_Category )
, false ) ;
2022-04-27 08:52:41 -04:00
if ( GeneratedFunction . SetterFunction = = nullptr )
2022-04-13 16:06:35 -04:00
{
2022-04-27 08:52:41 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " The setter name '%s' already exists and could not be autogenerated. " ) , * SetterName ) ) ;
continue ;
2022-04-13 16:06:35 -04:00
}
2022-05-18 12:54:21 -04:00
if ( GeneratedFunction . SetterFunction - > GetFName ( ) ! = * SetterName )
{
MessageLog . Error ( * FString : : Printf ( TEXT ( " The setter name '%s' was generated with a different name. " ) , * SetterName ) ) ;
continue ;
}
2022-04-13 16:06:35 -04:00
FunctionGraphHelper : : AddFunctionArgument ( GeneratedFunction . SetterFunction , Property , TEXT ( " NewValue " ) ) ;
}
if ( bGenerateReplicatedFunction )
{
GeneratedFunction . NetRepFunction = FunctionGraphHelper : : CreateIntermediateFunctionGraph (
* this
, * NetRepName
, ( FUNC_Private )
, Property - > GetMetaData ( NAME_Category )
, false ) ;
2022-04-27 08:52:41 -04:00
if ( GeneratedFunction . NetRepFunction = = nullptr )
2022-04-13 16:06:35 -04:00
{
2022-05-18 12:54:21 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " The netrep function name '%s' already exists and could not be autogenerated. " ) , * NetRepName ) ) ;
2022-04-13 16:06:35 -04:00
continue ;
}
2022-05-18 12:54:21 -04:00
if ( GeneratedFunction . NetRepFunction - > GetFName ( ) ! = * NetRepName )
2022-04-13 16:06:35 -04:00
{
2022-05-18 12:54:21 -04:00
MessageLog . Error ( * FString : : Printf ( TEXT ( " The netrep function name '%s' was generated with a different name. " ) , * NetRepName ) ) ;
continue ;
2022-04-13 16:06:35 -04:00
}
2022-05-18 12:54:21 -04:00
Property - > SetPropertyFlags ( CPF_Net ) ;
Property - > RepNotifyFunc = GeneratedFunction . NetRepFunction - > GetFName ( ) ;
2022-04-13 16:06:35 -04:00
FunctionGraphHelper : : AddFunctionArgument ( GeneratedFunction . SetterFunction , Property , TEXT ( " NewValue " ) ) ;
}
GeneratedFunctions . Add ( GeneratedFunction ) ;
}
NewViewModelBlueprintClass - > FieldNotifyNames . Emplace ( Property - > GetFName ( ) ) ;
}
}
}
void FViewModelBlueprintCompilerContext : : SpawnNewClass ( const FString & NewClassName )
{
NewViewModelBlueprintClass = FindObject < UMVVMViewModelBlueprintGeneratedClass > ( Blueprint - > GetOutermost ( ) , * NewClassName ) ;
if ( NewViewModelBlueprintClass = = nullptr )
{
NewViewModelBlueprintClass = NewObject < UMVVMViewModelBlueprintGeneratedClass > ( Blueprint - > GetOutermost ( ) , FName ( * NewClassName ) , RF_Public | RF_Transactional ) ;
}
else
{
// Already existed, but wasn't linked in the Blueprint yet due to load ordering issues
FBlueprintCompileReinstancer : : Create ( NewViewModelBlueprintClass ) ;
}
NewClass = NewViewModelBlueprintClass ;
}
void FViewModelBlueprintCompilerContext : : OnNewClassSet ( UBlueprintGeneratedClass * ClassToUse )
{
NewViewModelBlueprintClass = CastChecked < UMVVMViewModelBlueprintGeneratedClass > ( ClassToUse ) ;
}
void FViewModelBlueprintCompilerContext : : PostcompileFunction ( FKismetFunctionContext & Context )
{
Super : : PostcompileFunction ( Context ) ;
if ( Context . Function & & Context . Function - > HasMetaData ( UE : : FieldNotification : : FCustomizationHelper : : MetaData_FieldNotify ) )
{
if ( ! UE : : FieldNotification : : Helpers : : IsValidAsField ( Context . Function ) )
{
MessageLog . Error ( * LOCTEXT ( " FieldNotify_IsEventGraph " , " Function @@ cannot be a FieldNotify. A function needs to be const, returns a single properties, has no inputs, not be an event or a net function. " ) . ToString ( ) , Context . EntryPoint ) ;
}
NewViewModelBlueprintClass - > FieldNotifyNames . Emplace ( Context . Function - > GetFName ( ) ) ;
}
}
void FViewModelBlueprintCompilerContext : : FinishCompilingClass ( UClass * Class )
{
Super : : FinishCompilingClass ( Class ) ;
2022-05-18 12:54:21 -04:00
auto DeleteGraph = [ ] ( UEdGraph * Graph )
{
if ( Graph )
{
ensureMsgf ( Graph - > HasAnyFlags ( RF_Transient ) , TEXT ( " The graph should be temporary and should be generated automatically. " ) ) ;
// GC may not have clean the graph (GC doesn't run when bRegenerateSkeletonOnly is on)
Graph - > Rename ( nullptr , Graph - > GetOuter ( ) , REN_DoNotDirty | REN_ForceNoResetLoaders ) ;
}
} ;
2022-04-13 16:06:35 -04:00
const bool bIsSkeletonOnly = CompileOptions . CompileType = = EKismetCompileType : : SkeletonOnly ;
if ( ! bIsSkeletonOnly )
{
if ( UMVVMViewModelBlueprintGeneratedClass * BPGClass = CastChecked < UMVVMViewModelBlueprintGeneratedClass > ( Class ) )
{
if ( UMVVMViewModelBase * ViewModelBase = Cast < UMVVMViewModelBase > ( BPGClass - > GetDefaultObject ( ) ) )
{
BPGClass - > InitializeFieldNotification ( ViewModelBase ) ;
}
}
2022-05-18 12:54:21 -04:00
}
2022-05-12 14:55:19 -04:00
2022-05-18 12:54:21 -04:00
for ( FGeneratedFunction & GeneratedFunction : GeneratedFunctions )
{
DeleteGraph ( GeneratedFunction . SetterFunction ) ;
DeleteGraph ( GeneratedFunction . NetRepFunction ) ;
2022-04-13 16:06:35 -04:00
}
}
} //namespace
# undef LOCTEXT_NAMESPACE