2016-01-07 08:17:16 -05:00
|
|
|
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
#include "ScriptEditorPluginPrivatePCH.h"
|
|
|
|
|
#include "ScriptBlueprint.h"
|
|
|
|
|
#include "ScriptBlueprintGeneratedClass.h"
|
|
|
|
|
#include "ScriptBlueprintCompiler.h"
|
|
|
|
|
#include "Kismet2NameValidators.h"
|
|
|
|
|
#include "KismetReinstanceUtilities.h"
|
2014-07-11 10:19:48 -04:00
|
|
|
#include "ScriptContext.h"
|
|
|
|
|
#include "ScriptContextComponent.h"
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
///-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
FScriptBlueprintCompiler::FScriptBlueprintCompiler(UScriptBlueprint* SourceSketch, FCompilerResultsLog& InMessageLog, const FKismetCompilerOptions& InCompilerOptions, TArray<UObject*>* InObjLoaded)
|
|
|
|
|
: Super(SourceSketch, InMessageLog, InCompilerOptions, InObjLoaded)
|
2014-07-11 10:19:48 -04:00
|
|
|
, NewScriptBlueprintClass(NULL)
|
|
|
|
|
, ContextProperty(NULL)
|
2014-07-03 03:22:03 -04:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FScriptBlueprintCompiler::~FScriptBlueprintCompiler()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::CleanAndSanitizeClass(UBlueprintGeneratedClass* ClassToClean, UObject*& OldCDO)
|
|
|
|
|
{
|
|
|
|
|
Super::CleanAndSanitizeClass(ClassToClean, OldCDO);
|
|
|
|
|
|
|
|
|
|
// Make sure our typed pointer is set
|
2014-07-11 10:19:48 -04:00
|
|
|
check(ClassToClean == NewClass);
|
2014-07-03 03:22:03 -04:00
|
|
|
NewScriptBlueprintClass = CastChecked<UScriptBlueprintGeneratedClass>((UObject*)NewClass);
|
2014-07-11 10:19:48 -04:00
|
|
|
ContextProperty = NULL;
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::CreateClassVariablesFromBlueprint()
|
|
|
|
|
{
|
|
|
|
|
Super::CreateClassVariablesFromBlueprint();
|
|
|
|
|
|
2016-05-10 16:00:39 -04:00
|
|
|
UScriptBlueprint* ScriptBP = ScriptBlueprint();
|
2014-07-03 06:53:09 -04:00
|
|
|
UScriptBlueprintGeneratedClass* NewScripClass = CastChecked<UScriptBlueprintGeneratedClass>(NewClass);
|
|
|
|
|
NewScripClass->ScriptProperties.Empty();
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
for (auto& Field : ScriptDefinedFields)
|
|
|
|
|
{
|
2014-07-11 10:19:48 -04:00
|
|
|
UClass* InnerType = Field.Class;
|
|
|
|
|
if (Field.Class->IsChildOf(UProperty::StaticClass()))
|
2014-07-03 03:22:03 -04:00
|
|
|
{
|
|
|
|
|
FString PinCategory;
|
|
|
|
|
if (Field.Class->IsChildOf(UStrProperty::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
PinCategory = Schema->PC_String;
|
|
|
|
|
}
|
|
|
|
|
else if (Field.Class->IsChildOf(UFloatProperty::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
PinCategory = Schema->PC_Float;
|
|
|
|
|
}
|
|
|
|
|
else if (Field.Class->IsChildOf(UIntProperty::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
PinCategory = Schema->PC_Int;
|
|
|
|
|
}
|
|
|
|
|
else if (Field.Class->IsChildOf(UBoolProperty::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
PinCategory = Schema->PC_Boolean;
|
|
|
|
|
}
|
|
|
|
|
else if (Field.Class->IsChildOf(UObjectProperty::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
PinCategory = Schema->PC_Object;
|
2014-07-11 10:19:48 -04:00
|
|
|
// @todo: some scripting extensions (that are strongly typed) can handle this better
|
|
|
|
|
InnerType = UObject::StaticClass();
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
if (!PinCategory.IsEmpty())
|
2014-07-11 10:19:48 -04:00
|
|
|
{
|
|
|
|
|
FEdGraphPinType ScriptPinType(PinCategory, TEXT(""), InnerType, false, false);
|
2014-07-03 03:22:03 -04:00
|
|
|
UProperty* ScriptProperty = CreateVariable(Field.Name, ScriptPinType);
|
|
|
|
|
if (ScriptProperty != NULL)
|
|
|
|
|
{
|
2016-05-10 16:00:39 -04:00
|
|
|
ScriptProperty->SetMetaData(TEXT("Category"), *ScriptBP->GetName());
|
2014-07-11 10:19:48 -04:00
|
|
|
ScriptProperty->SetPropertyFlags(CPF_BlueprintVisible | CPF_Edit);
|
2014-07-03 06:53:09 -04:00
|
|
|
NewScripClass->ScriptProperties.Add(ScriptProperty);
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-11 10:19:48 -04:00
|
|
|
|
|
|
|
|
CreateScriptContextProperty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::CreateScriptContextProperty()
|
|
|
|
|
{
|
2015-02-06 10:41:43 -05:00
|
|
|
// The only case we don't need a script context is if the script class derives form UScriptPluginComponent
|
2014-07-11 10:19:48 -04:00
|
|
|
UClass* ContextClass = nullptr;
|
|
|
|
|
if (Blueprint->ParentClass->IsChildOf(AActor::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
ContextClass = UScriptContextComponent::StaticClass();
|
|
|
|
|
}
|
2015-02-06 10:41:43 -05:00
|
|
|
else if (!Blueprint->ParentClass->IsChildOf(UScriptPluginComponent::StaticClass()))
|
2014-07-11 10:19:48 -04:00
|
|
|
{
|
|
|
|
|
ContextClass = UScriptContext::StaticClass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ContextClass)
|
|
|
|
|
{
|
|
|
|
|
FEdGraphPinType ScriptContextPinType(Schema->PC_Object, TEXT(""), ContextClass, false, false);
|
|
|
|
|
ContextProperty = CastChecked<UObjectProperty>(CreateVariable(TEXT("Generated_ScriptContext"), ScriptContextPinType));
|
|
|
|
|
ContextProperty->SetPropertyFlags(CPF_ContainsInstancedReference | CPF_InstancedReference);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::CreateFunctionList()
|
|
|
|
|
{
|
|
|
|
|
Super::CreateFunctionList();
|
|
|
|
|
|
2015-02-06 10:41:43 -05:00
|
|
|
if (!Blueprint->ParentClass->IsChildOf(UScriptPluginComponent::StaticClass()))
|
2014-07-11 10:19:48 -04:00
|
|
|
{
|
|
|
|
|
for (auto& Field : ScriptDefinedFields)
|
|
|
|
|
{
|
|
|
|
|
if (Field.Class->IsChildOf(UFunction::StaticClass()))
|
|
|
|
|
{
|
|
|
|
|
CreateScriptDefinedFunction(Field);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::CreateScriptDefinedFunction(FScriptField& Field)
|
|
|
|
|
{
|
|
|
|
|
check(ContextProperty);
|
|
|
|
|
|
2016-05-10 16:00:39 -04:00
|
|
|
UScriptBlueprint* ScriptBP = ScriptBlueprint();
|
2014-07-11 10:19:48 -04:00
|
|
|
const FString FunctionName = Field.Name.ToString();
|
|
|
|
|
|
|
|
|
|
// Create Blueprint Graph which consists of 3 nodes: 'Entry', 'Get Script Context' and 'Call Function'
|
|
|
|
|
// @todo: once we figure out how to get parameter lists for functions we can add suport for that here
|
|
|
|
|
|
2016-05-10 16:00:39 -04:00
|
|
|
UEdGraph* ScriptFunctionGraph = NewObject<UEdGraph>(ScriptBP, *FString::Printf(TEXT("%s_Graph"), *FunctionName));
|
2014-07-11 10:19:48 -04:00
|
|
|
ScriptFunctionGraph->Schema = UEdGraphSchema_K2::StaticClass();
|
|
|
|
|
ScriptFunctionGraph->SetFlags(RF_Transient);
|
|
|
|
|
|
|
|
|
|
FKismetFunctionContext* FunctionContext = CreateFunctionContext();
|
|
|
|
|
FunctionContext->SourceGraph = ScriptFunctionGraph;
|
|
|
|
|
FunctionContext->bCreateDebugData = false;
|
|
|
|
|
|
|
|
|
|
UK2Node_FunctionEntry* EntryNode = SpawnIntermediateNode<UK2Node_FunctionEntry>(NULL, ScriptFunctionGraph);
|
|
|
|
|
EntryNode->CustomGeneratedFunctionName = Field.Name;
|
|
|
|
|
EntryNode->AllocateDefaultPins();
|
|
|
|
|
|
|
|
|
|
UK2Node_VariableGet* GetVariableNode = SpawnIntermediateNode<UK2Node_VariableGet>(NULL, ScriptFunctionGraph);
|
|
|
|
|
GetVariableNode->VariableReference.SetSelfMember(ContextProperty->GetFName());
|
|
|
|
|
GetVariableNode->AllocateDefaultPins();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UK2Node_CallFunction* CallFunctionNode = SpawnIntermediateNode<UK2Node_CallFunction>(NULL, ScriptFunctionGraph);
|
|
|
|
|
CallFunctionNode->FunctionReference.SetExternalMember(TEXT("CallScriptFunction"), ContextProperty->PropertyClass);
|
|
|
|
|
CallFunctionNode->AllocateDefaultPins();
|
|
|
|
|
UEdGraphPin* FunctionNamePin = CallFunctionNode->FindPinChecked(TEXT("FunctionName"));
|
|
|
|
|
FunctionNamePin->DefaultValue = FunctionName;
|
|
|
|
|
|
|
|
|
|
// Link nodes together
|
|
|
|
|
UEdGraphPin* ExecPin = Schema->FindExecutionPin(*EntryNode, EGPD_Output);
|
|
|
|
|
UEdGraphPin* GetVariableOutPin = GetVariableNode->FindPinChecked(ContextProperty->GetName());
|
|
|
|
|
UEdGraphPin* CallFunctionPin = Schema->FindExecutionPin(*CallFunctionNode, EGPD_Input);
|
|
|
|
|
UEdGraphPin* FunctionTargetPin = CallFunctionNode->FindPinChecked(TEXT("self"));
|
|
|
|
|
ExecPin->MakeLinkTo(CallFunctionPin);
|
|
|
|
|
GetVariableOutPin->MakeLinkTo(FunctionTargetPin);
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::FinishCompilingClass(UClass* Class)
|
|
|
|
|
{
|
2016-05-10 16:00:39 -04:00
|
|
|
UScriptBlueprint* ScriptBP = ScriptBlueprint();
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
UScriptBlueprintGeneratedClass* ScriptClass = CastChecked<UScriptBlueprintGeneratedClass>(Class);
|
2016-05-10 16:00:39 -04:00
|
|
|
ScriptClass->SourceCode = ScriptBP->SourceCode;
|
|
|
|
|
ScriptClass->ByteCode = ScriptBP->ByteCode;
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
// Allow Blueprint Components to be used in Blueprints
|
2016-05-10 16:00:39 -04:00
|
|
|
if (ScriptBP->ParentClass->IsChildOf(UScriptPluginComponent::StaticClass()) && Class != ScriptBP->SkeletonGeneratedClass)
|
2014-07-03 03:22:03 -04:00
|
|
|
{
|
|
|
|
|
Class->SetMetaData(TEXT("BlueprintSpawnableComponent"), TEXT("true"));
|
|
|
|
|
}
|
2014-07-11 10:19:48 -04:00
|
|
|
|
|
|
|
|
Super::FinishCompilingClass(Class);
|
|
|
|
|
|
|
|
|
|
// Ff context property has been created, create a DSO and set it on the CDO
|
|
|
|
|
if (ContextProperty)
|
|
|
|
|
{
|
|
|
|
|
UObject* CDO = Class->GetDefaultObject();
|
2015-02-09 05:43:45 -05:00
|
|
|
UObject* ContextDefaultSubobject = NewObject<UObject>(CDO, ContextProperty->PropertyClass, "ScriptContext", RF_DefaultSubObject | RF_Public);
|
2014-07-11 10:19:48 -04:00
|
|
|
ContextProperty->SetObjectPropertyValue(ContextProperty->ContainerPtrToValuePtr<UObject*>(CDO), ContextDefaultSubobject);
|
|
|
|
|
}
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::Compile()
|
|
|
|
|
{
|
2014-07-11 10:19:48 -04:00
|
|
|
ScriptBlueprint()->UpdateSourceCodeIfChanged();
|
|
|
|
|
ScriptContext = FScriptContextBase::CreateContext(ScriptBlueprint()->SourceCode, NULL, NULL);
|
2014-07-03 03:22:03 -04:00
|
|
|
bool Result = true;
|
|
|
|
|
if (ScriptContext.IsValid())
|
|
|
|
|
{
|
|
|
|
|
ScriptDefinedFields.Empty();
|
|
|
|
|
ScriptContext->GetScriptDefinedFields(ScriptDefinedFields);
|
|
|
|
|
}
|
2014-07-11 10:19:48 -04:00
|
|
|
ContextProperty = NULL;
|
2014-07-03 03:22:03 -04:00
|
|
|
|
|
|
|
|
Super::Compile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::EnsureProperGeneratedClass(UClass*& TargetUClass)
|
|
|
|
|
{
|
|
|
|
|
if ( TargetUClass && !( (UObject*)TargetUClass )->IsA(UScriptBlueprintGeneratedClass::StaticClass()) )
|
|
|
|
|
{
|
|
|
|
|
FKismetCompilerUtilities::ConsignToOblivion(TargetUClass, Blueprint->bIsRegeneratingOnLoad);
|
|
|
|
|
TargetUClass = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FScriptBlueprintCompiler::SpawnNewClass(const FString& NewClassName)
|
|
|
|
|
{
|
|
|
|
|
NewScriptBlueprintClass = FindObject<UScriptBlueprintGeneratedClass>(Blueprint->GetOutermost(), *NewClassName);
|
|
|
|
|
|
|
|
|
|
if ( NewScriptBlueprintClass == NULL )
|
|
|
|
|
{
|
2015-02-03 05:40:57 -05:00
|
|
|
NewScriptBlueprintClass = NewObject<UScriptBlueprintGeneratedClass>(Blueprint->GetOutermost(), FName(*NewClassName), RF_Public | RF_Transactional);
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Already existed, but wasn't linked in the Blueprint yet due to load ordering issues
|
2015-02-19 13:32:52 -05:00
|
|
|
FBlueprintCompileReinstancer::Create(NewScriptBlueprintClass);
|
2014-07-03 03:22:03 -04:00
|
|
|
}
|
|
|
|
|
NewClass = NewScriptBlueprintClass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FScriptBlueprintCompiler::ValidateGeneratedClass(UBlueprintGeneratedClass* Class)
|
|
|
|
|
{
|
|
|
|
|
bool SuperResult = Super::ValidateGeneratedClass(Class);
|
|
|
|
|
bool Result = UScriptBlueprint::ValidateGeneratedClass(Class);
|
|
|
|
|
return SuperResult && Result;
|
|
|
|
|
}
|