290 lines
9.4 KiB
C++
290 lines
9.4 KiB
C++
#include "CSCompilerContext.h"
|
|
|
|
#include "BlueprintActionDatabase.h"
|
|
#include "ISettingsModule.h"
|
|
#include "BehaviorTree/Tasks/BTTask_BlueprintBase.h"
|
|
#include "Blueprint/StateTreeTaskBlueprintBase.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "Engine/SimpleConstructionScript.h"
|
|
#include "TypeGenerator/CSBlueprint.h"
|
|
#include "TypeGenerator/CSClass.h"
|
|
#include "TypeGenerator/CSSkeletonClass.h"
|
|
#include "TypeGenerator/Factories/CSFunctionFactory.h"
|
|
#include "TypeGenerator/Factories/CSPropertyFactory.h"
|
|
#include "TypeGenerator/Factories/PropertyGenerators/CSPropertyGenerator.h"
|
|
#include "TypeGenerator/Register/CSGeneratedClassBuilder.h"
|
|
#include "TypeGenerator/Register/CSMetaDataUtils.h"
|
|
#include "TypeGenerator/Register/CSSimpleConstructionScriptBuilder.h"
|
|
#include "TypeGenerator/Register/MetaData/CSClassMetaData.h"
|
|
#include "TypeGenerator/Register/TypeInfo/CSClassInfo.h"
|
|
#include "UnrealSharpEditor/CSUnrealSharpEditorSettings.h"
|
|
#include "UnrealSharpUtilities/UnrealSharpUtils.h"
|
|
#include "Utils/CSClassUtilities.h"
|
|
|
|
FCSCompilerContext::FCSCompilerContext(UCSBlueprint* Blueprint, FCompilerResultsLog& InMessageLog, const FKismetCompilerOptions& InCompilerOptions):
|
|
FKismetCompilerContext(Blueprint, InMessageLog, InCompilerOptions)
|
|
{
|
|
|
|
}
|
|
|
|
void FCSCompilerContext::FinishCompilingClass(UClass* Class)
|
|
{
|
|
bool bIsSkeletonClass = FCSClassUtilities::IsSkeletonType(Class);
|
|
|
|
if (!bIsSkeletonClass)
|
|
{
|
|
// The skeleton class shouldn't be using the managed constructor since it's not tied to an assembly
|
|
Class->ClassConstructor = &UCSGeneratedClassBuilder::ManagedObjectConstructor;
|
|
}
|
|
|
|
Super::FinishCompilingClass(Class);
|
|
|
|
TSharedPtr<FCSClassMetaData> TypeMetaData = GetClassInfo()->GetTypeMetaData<FCSClassMetaData>();
|
|
|
|
// Super call overrides the class flags, so we need to set after that
|
|
Class->ClassFlags |= TypeMetaData->ClassFlags;
|
|
|
|
if (NeedsToFakeNativeClass(Class) && !bIsSkeletonClass)
|
|
{
|
|
// There are systems in Unreal (BehaviorTree, StateTree) which uses the AssetRegistry to find BP classes, since our C# classes are not assets,
|
|
// we need to fake that they're native classes in editor in order to be able to find them.
|
|
|
|
// The functions that are used to find classes are:
|
|
// FGraphNodeClassHelper::BuildClassGraph()
|
|
// FStateTreeNodeClassCache::CacheClasses()
|
|
Class->ClassFlags |= CLASS_Native;
|
|
}
|
|
|
|
UCSGeneratedClassBuilder::SetConfigName(Class, TypeMetaData);
|
|
TryInitializeAsDeveloperSettings(Class);
|
|
ApplyMetaData();
|
|
}
|
|
|
|
void FCSCompilerContext::OnPostCDOCompiled(const UObject::FPostCDOCompiledContext& Context)
|
|
{
|
|
FKismetCompilerContext::OnPostCDOCompiled(Context);
|
|
|
|
UCSGeneratedClassBuilder::SetupDefaultTickSettings(NewClass->GetDefaultObject(), NewClass);
|
|
|
|
UCSClass* Class = GetMainClass();
|
|
if (Class == NewClass)
|
|
{
|
|
UCSGeneratedClassBuilder::TryRegisterDynamicSubsystem(Class);
|
|
|
|
if (GEditor)
|
|
{
|
|
FBlueprintActionDatabase::Get().RefreshClassActions(Class);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FCSCompilerContext::CreateClassVariablesFromBlueprint()
|
|
{
|
|
TSharedPtr<FCSClassInfo> ClassInfo = GetMainClass()->GetManagedTypeInfo<FCSClassInfo>();
|
|
const TArray<FCSPropertyMetaData>& Properties = ClassInfo->GetTypeMetaData<FCSClassMetaData>()->Properties;
|
|
|
|
NewClass->PropertyGuids.Empty(Properties.Num());
|
|
TryValidateSimpleConstructionScript(ClassInfo);
|
|
|
|
FCSPropertyFactory::CreateAndAssignProperties(NewClass, Properties, [this](const FProperty* NewProperty)
|
|
{
|
|
FName PropertyName = NewProperty->GetFName();
|
|
FGuid PropertyGuid = FCSUnrealSharpUtils::ConstructGUIDFromName(PropertyName);
|
|
NewClass->PropertyGuids.Add(PropertyName, PropertyGuid);
|
|
});
|
|
|
|
// Create dummy variables for the blueprint.
|
|
// They should not get compiled, just there for metadata for different Unreal modules.
|
|
CreateDummyBlueprintVariables(Properties);
|
|
}
|
|
|
|
void FCSCompilerContext::CleanAndSanitizeClass(UBlueprintGeneratedClass* ClassToClean, UObject*& InOldCDO)
|
|
{
|
|
FKismetCompilerContext::CleanAndSanitizeClass(ClassToClean, InOldCDO);
|
|
NewClass->FieldNotifies.Reset();
|
|
|
|
TryDeinitializeAsDeveloperSettings(InOldCDO);
|
|
|
|
// Too late to generate functions in CreateFunctionList for child blueprints
|
|
GenerateFunctions();
|
|
}
|
|
|
|
void FCSCompilerContext::SpawnNewClass(const FString& NewClassName)
|
|
{
|
|
UCSClass* MainClass = GetMainClass();
|
|
UCSSkeletonClass* NewSkeletonClass = NewObject<UCSSkeletonClass>(Blueprint->GetOutermost(), FName(*NewClassName), RF_Public | RF_Transactional);
|
|
NewSkeletonClass->SetGeneratedClass(MainClass);
|
|
NewClass = NewSkeletonClass;
|
|
|
|
// Skeleton class doesn't generate functions on the first pass.
|
|
// It's done in CleanAndSanitizeClass which doesn't run when the skeleton class is created
|
|
GenerateFunctions();
|
|
}
|
|
|
|
void FCSCompilerContext::AddInterfacesFromBlueprint(UClass* Class)
|
|
{
|
|
UCSGeneratedClassBuilder::ImplementInterfaces(Class, GetTypeMetaData()->Interfaces);
|
|
}
|
|
|
|
void FCSCompilerContext::TryValidateSimpleConstructionScript(const TSharedPtr<const FCSClassInfo>& ClassInfo) const
|
|
{
|
|
const TArray<FCSPropertyMetaData>& Properties = GetTypeMetaData()->Properties;
|
|
FCSSimpleConstructionScriptBuilder::BuildSimpleConstructionScript(Blueprint->GeneratedClass, &Blueprint->SimpleConstructionScript, Properties);
|
|
|
|
if (!Blueprint->SimpleConstructionScript)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<USCS_Node*> Nodes;
|
|
for (const FCSPropertyMetaData& Property : Properties)
|
|
{
|
|
if (Property.Type->PropertyType != ECSPropertyType::DefaultComponent)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript;
|
|
USCS_Node* Node = SCS->FindSCSNode(Property.Name);
|
|
Nodes.Add(Node);
|
|
}
|
|
|
|
// Remove all nodes that are not part of the class anymore.
|
|
int32 NumNodes = Blueprint->SimpleConstructionScript->GetAllNodes().Num();
|
|
TArray<USCS_Node*> AllNodes = Blueprint->SimpleConstructionScript->GetAllNodes();
|
|
for (int32 i = NumNodes - 1; i >= 0; --i)
|
|
{
|
|
USCS_Node* Node = AllNodes[i];
|
|
if (!Nodes.Contains(Node))
|
|
{
|
|
Blueprint->SimpleConstructionScript->RemoveNode(Node);
|
|
}
|
|
}
|
|
|
|
Blueprint->SimpleConstructionScript->ValidateNodeTemplates(MessageLog);
|
|
Blueprint->SimpleConstructionScript->ValidateNodeVariableNames(MessageLog);
|
|
}
|
|
|
|
void FCSCompilerContext::GenerateFunctions() const
|
|
{
|
|
TSharedPtr<const FCSClassMetaData> TypeMetaData = GetTypeMetaData();
|
|
|
|
if (TypeMetaData->VirtualFunctions.IsEmpty() && TypeMetaData->Functions.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FCSFunctionFactory::GenerateVirtualFunctions(NewClass, TypeMetaData);
|
|
FCSFunctionFactory::GenerateFunctions(NewClass, TypeMetaData->Functions);
|
|
}
|
|
|
|
UCSClass* FCSCompilerContext::GetMainClass() const
|
|
{
|
|
return CastChecked<UCSClass>(Blueprint->GeneratedClass);
|
|
}
|
|
|
|
TSharedPtr<const FCSClassInfo> FCSCompilerContext::GetClassInfo() const
|
|
{
|
|
return GetMainClass()->GetManagedTypeInfo<FCSClassInfo>();
|
|
}
|
|
|
|
TSharedPtr<const FCSClassMetaData> FCSCompilerContext::GetTypeMetaData() const
|
|
{
|
|
return GetClassInfo()->GetTypeMetaData<FCSClassMetaData>();
|
|
}
|
|
|
|
bool FCSCompilerContext::IsDeveloperSettings() const
|
|
{
|
|
return Blueprint->GeneratedClass == NewClass && NewClass->IsChildOf<UDeveloperSettings>();
|
|
}
|
|
|
|
void FCSCompilerContext::TryInitializeAsDeveloperSettings(const UClass* Class) const
|
|
{
|
|
if (!IsDeveloperSettings())
|
|
{
|
|
return;
|
|
}
|
|
|
|
UDeveloperSettings* Settings = static_cast<UDeveloperSettings*>(Class->GetDefaultObject());
|
|
ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked<ISettingsModule>("Settings");
|
|
|
|
SettingsModule.RegisterSettings(Settings->GetContainerName(), Settings->GetCategoryName(), Settings->GetSectionName(),
|
|
Settings->GetSectionText(),
|
|
Settings->GetSectionDescription(),
|
|
Settings);
|
|
|
|
Settings->LoadConfig();
|
|
}
|
|
|
|
void FCSCompilerContext::TryDeinitializeAsDeveloperSettings(UObject* Settings) const
|
|
{
|
|
if (!IsValid(Settings) || !IsDeveloperSettings())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked<ISettingsModule>("Settings");
|
|
UDeveloperSettings* DeveloperSettings = static_cast<UDeveloperSettings*>(Settings);
|
|
SettingsModule.UnregisterSettings(DeveloperSettings->GetContainerName(), DeveloperSettings->GetCategoryName(), DeveloperSettings->GetSectionName());
|
|
}
|
|
|
|
void FCSCompilerContext::ApplyMetaData()
|
|
{
|
|
TSharedPtr<const FCSClassMetaData> TypeMetaData = GetTypeMetaData();
|
|
|
|
static FString DisplayNameKey = TEXT("DisplayName");
|
|
if (!NewClass->HasMetaData(*DisplayNameKey))
|
|
{
|
|
NewClass->SetMetaData(*DisplayNameKey, *Blueprint->GetName());
|
|
}
|
|
|
|
if (GetDefault<UCSUnrealSharpEditorSettings>()->bSuffixGeneratedTypes)
|
|
{
|
|
FString DisplayName = NewClass->GetMetaData(*DisplayNameKey);
|
|
DisplayName += TEXT(" (C#)");
|
|
NewClass->SetMetaData(*DisplayNameKey, *DisplayName);
|
|
}
|
|
|
|
FCSMetaDataUtils::ApplyMetaData(TypeMetaData->MetaData, NewClass);
|
|
}
|
|
|
|
bool FCSCompilerContext::NeedsToFakeNativeClass(UClass* Class)
|
|
{
|
|
static TArray ParentClasses =
|
|
{
|
|
UBTTask_BlueprintBase::StaticClass(),
|
|
UStateTreeTaskBlueprintBase::StaticClass(),
|
|
};
|
|
|
|
for (UClass* ParentClass : ParentClasses)
|
|
{
|
|
if (Class->IsChildOf(ParentClass))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FCSCompilerContext::CreateDummyBlueprintVariables(const TArray<FCSPropertyMetaData>& Properties) const
|
|
{
|
|
Blueprint->NewVariables.Empty(Properties.Num());
|
|
|
|
for (const FCSPropertyMetaData& PropertyMetaData : Properties)
|
|
{
|
|
FBPVariableDescription VariableDescription;
|
|
VariableDescription.FriendlyName = PropertyMetaData.Name.ToString();
|
|
VariableDescription.VarName = PropertyMetaData.Name;
|
|
|
|
for (const TTuple<FString, FString>& MetaData : PropertyMetaData.MetaData)
|
|
{
|
|
VariableDescription.SetMetaData(*MetaData.Key, MetaData.Value);
|
|
}
|
|
|
|
Blueprint->NewVariables.Add(VariableDescription);
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|