Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_BaseAsyncTask.cpp
Marc Audy d3e1006533 Copying //UE4/Dev-Framework to Dev-Main (//UE4/Dev-Main) @ 2926658
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2821607 on 2016/01/08 by Mieszko.Zielinski

	Added a way to limit amount of information logged by vlog by discarding logs from classes from outside of class whitelist #UE4

	This feature was followed by refactoring of functions taking FVisualLogEntry pointers to use references instead.

	#rb Lukasz.Furman
	#codereview John.Abercrombie

Change 2828384 on 2016/01/14 by Mieszko.Zielinski

	Back out of visual log refactor done as part of CL#2821607 #UE4

Change 2910454 on 2016/03/15 by Zak.Middleton

	#ue4 - Properly exclude zero-distance MTD results in ComponentEncroachesBlockingGeometry_WithAdjustment() in the presense of multiple overlaps.

	#rb Jeff.Farris
	#jira UE-24327
	UDN: https://udn.unrealengine.com/questions/270574/jeff-farris-hack-for-physx-mtd.html

Change 2910548 on 2016/03/15 by Zak.Middleton

	#ue4 - Handle MTD computation returning NaN direction when there is a "contact" with zero distance.

Change 2912311 on 2016/03/16 by Marc.Audy

	Properly handle overlaps in C++ in documentation code and UE4 to Unity doc
	#rb Martin.Wilson

Change 2913086 on 2016/03/17 by Marc.Audy

	Adding ability to have 9 parameters to a dynamic delegate

Change 2913101 on 2016/03/17 by Marc.Audy

	Fix some of the loctext error messages

Change 2913102 on 2016/03/17 by Thomas.Sarkanen

	Console usability improvements

	Display console autocompletion commands from the lexicographically first element up to either the total number of commands or MAX_AUTOCOMPLETION_LINES, whichever is least. The previous behaviour started the list "in the middle" and hid the first elements if there were too many matches. Thus "[ab ac ad]" with "aa" hidden now becomes "[aa ab ac]" with "ad" hidden.
	To make scrolling work as expected, the input handling of the up and down arrow keys has been reversed so that the cursor iterates forward starting from the top with the down arrow key, and goes back up with the up arrow key. Command history is still accessed with the up arrow key.
	This commit also undoes one of the most evil uses of operator overloading I've ever seen, on par with "#define true false" but more subtle

	Color console autocomplete entries to denote their type: command, CVar or other (manual autocompletion entries). CVars are further divided into writeable and read-only variables.
	Assume that manual console autocompletion entries are commands. This makes the autocompletion list colors more consistent and less noisy

	Automatically select (but don't complete) a command on console character input. To prevent the autocomplete from becoming too trigger happy, no longer automatically complete commands for arbitrary key inputs that we happen to have a match for

	Allow cycling through console commands with the tab key
	Discriminate between first time and repeated tab presses and only scroll through autocomplete entries on the latter

	Fix off-by-one error in console: "x more matches" line was being shown when the number of autocomplete elements was equal to MAX_AUTOCOMPLETION_LINES
	Fix an off-by-one error that was causing the topmost console command to not be shown if there was an autocomplete scroll region

	Show the currently selected autocomplete entry faded out behind the user's typed input
	Slightly increase brightness of the normal input text colour to better distinguish between the typed and autocompleted parts of the input line

	Left-justify command descriptions in the console autocompletion box
	Detect overflow of console autocomplete lines on low resolutions and decrease the space used for description justification to compensate

	Make the console input, history and autocomplete colours user configurable

	Add console background transparency. Configurable, set to 15% by default

	Add missing closing quote to the console dump HTML template

	#github #2061: Console usability improvements from Mattiwatti

Change 2913104 on 2016/03/17 by Thomas.Sarkanen

	Added indicator displayed on animation nodes when they use the 'fast path'

	Added checkbox that can be used to audit Blueprint fast-path usage.
	Switched almost all animation node widgets to derive from new SAnimationGraphNode. This creates the overlay widget that indicates whether this node is using a more optimal path.

	#doc Also added documentation tooltips and UDN doc files/images for the fast path systems.
	#jira UE-24698 - Add icon to pins in anim graph to indicate 'fast mode' access
	#rb Martin.Wilson

Change 2913306 on 2016/03/17 by Marc.Audy

	Cleaning up GetResourceSize
	- Made many call Super::GetResourceSize
	- Removed trivial implementations
	- Fixed HierarchicalInstanceStaticMeshComponent double counting an array

Change 2913535 on 2016/03/17 by Lukasz.Furman

	fixed broken behavior tree graph data after subnode undo
	#ue4 UE-28198

Change 2913608 on 2016/03/17 by Lukasz.Furman

	fixed behavior tree execution indices after undoing move in editor
	#ue4 UE-26705

Change 2913847 on 2016/03/17 by Lukasz.Furman

	added new automation test for UE-28309
	#ue4

Change 2913849 on 2016/03/17 by Lukasz.Furman

	fixed behavior tree skipping over branch when restart request comes during AbortCurrentTask call
	#ue4 UE-28309

Change 2913895 on 2016/03/17 by Marc.Audy

	Added 'self' argument to Actor and PrimitiveComponent delegates that didn't already supply one
	Fixed up all C++ uses of these delegates
	#jira UE-23122
	#rb Zak.Middleton

Change 2914743 on 2016/03/18 by Thomas.Sarkanen

	Editing of primitive data in PhAT

[CL 2926677 by Marc Audy in Main branch]
2016-03-29 16:33:59 -04:00

515 lines
22 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "CompilerResultsLog.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "K2Node_BaseAsyncTask.h"
#include "K2Node_IfThenElse.h"
#include "Kismet/KismetSystemLibrary.h"
#define LOCTEXT_NAMESPACE "UK2Node_BaseAsyncTask"
UK2Node_BaseAsyncTask::UK2Node_BaseAsyncTask(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, ProxyFactoryFunctionName(NAME_None)
, ProxyFactoryClass(NULL)
, ProxyClass(NULL)
, ProxyActivateFunctionName(NAME_None)
{
}
FText UK2Node_BaseAsyncTask::GetTooltipText() const
{
const FString FunctionToolTipText = UK2Node_CallFunction::GetDefaultTooltipForFunction(GetFactoryFunction());
return FText::FromString(FunctionToolTipText);
}
FText UK2Node_BaseAsyncTask::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (GetFactoryFunction() == nullptr)
{
return FText(LOCTEXT("UK2Node_BaseAsyncTaskGetNodeTitle", "Async Task: Missing Function"));
}
const FText FunctionToolTipText = UK2Node_CallFunction::GetUserFacingFunctionName(GetFactoryFunction());
return FunctionToolTipText;
}
bool UK2Node_BaseAsyncTask::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
bool bIsCompatible = false;
// Can only place events in ubergraphs and macros (other code will help prevent macros with latents from ending up in functions), and basicasync task creates an event node:
EGraphType GraphType = TargetGraph->GetSchema()->GetGraphType(TargetGraph);
if (GraphType == EGraphType::GT_Ubergraph || GraphType == EGraphType::GT_Macro)
{
bIsCompatible = true;
}
return bIsCompatible && Super::IsCompatibleWithGraph(TargetGraph);
}
void UK2Node_BaseAsyncTask::AllocateDefaultPins()
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute);
CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then);
bool bExposeProxy = false;
for (const UStruct* TestStruct = ProxyClass; TestStruct && !bExposeProxy; TestStruct = TestStruct->GetSuperStruct())
{
bExposeProxy = TestStruct->HasMetaData(TEXT("ExposedAsyncProxy"));
}
if (bExposeProxy)
{
CreatePin(EGPD_Output, K2Schema->PC_Object, TEXT(""), ProxyClass, false, false, FBaseAsyncTaskHelper::GetAsyncTaskProxyName());
}
UFunction* DelegateSignatureFunction = NULL;
for (TFieldIterator<UProperty> PropertyIt(ProxyClass, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt)
{
if (UMulticastDelegateProperty* Property = Cast<UMulticastDelegateProperty>(*PropertyIt))
{
CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, *Property->GetName());
if (!DelegateSignatureFunction)
{
DelegateSignatureFunction = Property->SignatureFunction;
}
}
}
if (DelegateSignatureFunction)
{
for (TFieldIterator<UProperty> PropIt(DelegateSignatureFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* Param = *PropIt;
const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm);
if (bIsFunctionInput)
{
UEdGraphPin* Pin = CreatePin(EGPD_Output, TEXT(""), TEXT(""), NULL, false, false, Param->GetName());
K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType);
}
}
}
bool bAllPinsGood = true;
UFunction* Function = ProxyFactoryClass ? ProxyFactoryClass->FindFunctionByName(ProxyFactoryFunctionName) : nullptr;
if (Function)
{
TSet<FString> PinsToHide;
FBlueprintEditorUtils::GetHiddenPinsForFunction(GetGraph(), Function, PinsToHide);
for (TFieldIterator<UProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* Param = *PropIt;
const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm);
if (!bIsFunctionInput)
{
// skip function output, it's internal node data
continue;
}
const bool bIsRefParam = Param->HasAnyPropertyFlags(CPF_ReferenceParm) && bIsFunctionInput;
UEdGraphPin* Pin = CreatePin(EGPD_Input, TEXT(""), TEXT(""), NULL, false, bIsRefParam, Param->GetName());
const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType);
if (bPinGood)
{
//Flag pin as read only for const reference property
Pin->bDefaultValueIsIgnored = Param->HasAllPropertyFlags(CPF_ConstParm | CPF_ReferenceParm) && (!Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm) || Pin->PinType.bIsArray);
const bool bAdvancedPin = Param->HasAllPropertyFlags(CPF_AdvancedDisplay);
Pin->bAdvancedView = bAdvancedPin;
if(bAdvancedPin && (ENodeAdvancedPins::NoPins == AdvancedPinDisplay))
{
AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
}
K2Schema->SetPinDefaultValue(Pin, Function, Param);
if (PinsToHide.Contains(Pin->PinName))
{
Pin->bHidden = true;
Pin->DefaultValue = TEXT("0");
}
}
bAllPinsGood = bAllPinsGood && bPinGood;
}
}
Super::AllocateDefaultPins();
}
const FString& UK2Node_BaseAsyncTask::FBaseAsyncTaskHelper::GetAsyncTaskProxyName()
{
static const FString Name(TEXT("AsyncTaskProxy"));
return Name;
}
bool UK2Node_BaseAsyncTask::FBaseAsyncTaskHelper::ValidDataPin(const UEdGraphPin* Pin, EEdGraphPinDirection Direction, const UEdGraphSchema_K2* Schema)
{
check(Schema);
const bool bValidDataPin = Pin
&& (Pin->PinName != Schema->PN_Execute)
&& (Pin->PinName != Schema->PN_Then)
&& (Pin->PinType.PinCategory != Schema->PC_Exec);
const bool bProperDirection = Pin && (Pin->Direction == Direction);
return bValidDataPin && bProperDirection;
}
bool UK2Node_BaseAsyncTask::FBaseAsyncTaskHelper::CreateDelegateForNewFunction(UEdGraphPin* DelegateInputPin, FName FunctionName, UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext)
{
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
check(DelegateInputPin && Schema && CurrentNode && SourceGraph && (FunctionName != NAME_None));
bool bResult = true;
// WORKAROUND, so we can create delegate from nonexistent function by avoiding check at expanding step
// instead simply: Schema->TryCreateConnection(AddDelegateNode->GetDelegatePin(), CurrentCENode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName));
UK2Node_Self* SelfNode = CompilerContext.SpawnIntermediateNode<UK2Node_Self>(CurrentNode, SourceGraph);
SelfNode->AllocateDefaultPins();
UK2Node_CreateDelegate* CreateDelegateNode = CompilerContext.SpawnIntermediateNode<UK2Node_CreateDelegate>(CurrentNode, SourceGraph);
CreateDelegateNode->AllocateDefaultPins();
bResult &= Schema->TryCreateConnection(DelegateInputPin, CreateDelegateNode->GetDelegateOutPin());
bResult &= Schema->TryCreateConnection(SelfNode->FindPinChecked(Schema->PN_Self), CreateDelegateNode->GetObjectInPin());
CreateDelegateNode->SetFunction(FunctionName);
return bResult;
}
bool UK2Node_BaseAsyncTask::FBaseAsyncTaskHelper::CopyEventSignature(UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema)
{
check(CENode && Function && Schema);
bool bResult = true;
for (TFieldIterator<UProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
const UProperty* Param = *PropIt;
if (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm))
{
FEdGraphPinType PinType;
bResult &= Schema->ConvertPropertyToPinType(Param, /*out*/ PinType);
bResult &= (NULL != CENode->CreateUserDefinedPin(Param->GetName(), PinType, EGPD_Output));
}
}
return bResult;
}
bool UK2Node_BaseAsyncTask::FBaseAsyncTaskHelper::HandleDelegateImplementation(
UMulticastDelegateProperty* CurrentProperty, const TArray<FBaseAsyncTaskHelper::FOutputPinAndLocalVariable>& VariableOutputs,
UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin,
UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext)
{
bool bIsErrorFree = true;
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
check(CurrentProperty && ProxyObjectPin && InOutLastThenPin && CurrentNode && SourceGraph && Schema);
UEdGraphPin* PinForCurrentDelegateProperty = CurrentNode->FindPin(CurrentProperty->GetName());
if (!PinForCurrentDelegateProperty || (Schema->PC_Exec != PinForCurrentDelegateProperty->PinType.PinCategory))
{
FText ErrorMessage = FText::Format(LOCTEXT("WrongDelegateProperty", "BaseAsyncTask: Cannot find execution pin for delegate "), FText::FromString(CurrentProperty->GetName()));
CompilerContext.MessageLog.Error(*ErrorMessage.ToString(), CurrentNode);
return false;
}
UK2Node_CustomEvent* CurrentCENode = CompilerContext.SpawnIntermediateNode<UK2Node_CustomEvent>(CurrentNode, SourceGraph);
{
UK2Node_AddDelegate* AddDelegateNode = CompilerContext.SpawnIntermediateNode<UK2Node_AddDelegate>(CurrentNode, SourceGraph);
AddDelegateNode->SetFromProperty(CurrentProperty, false);
AddDelegateNode->AllocateDefaultPins();
bIsErrorFree &= Schema->TryCreateConnection(AddDelegateNode->FindPinChecked(Schema->PN_Self), ProxyObjectPin);
bIsErrorFree &= Schema->TryCreateConnection(InOutLastThenPin, AddDelegateNode->FindPinChecked(Schema->PN_Execute));
InOutLastThenPin = AddDelegateNode->FindPinChecked(Schema->PN_Then);
CurrentCENode->CustomFunctionName = *FString::Printf(TEXT("%s_%s"), *CurrentProperty->GetName(), *CompilerContext.GetGuid(CurrentNode));
CurrentCENode->AllocateDefaultPins();
bIsErrorFree &= FBaseAsyncTaskHelper::CreateDelegateForNewFunction(AddDelegateNode->GetDelegatePin(), CurrentCENode->GetFunctionName(), CurrentNode, SourceGraph, CompilerContext);
bIsErrorFree &= FBaseAsyncTaskHelper::CopyEventSignature(CurrentCENode, AddDelegateNode->GetDelegateSignature(), Schema);
}
UEdGraphPin* LastActivatedNodeThen = CurrentCENode->FindPinChecked(Schema->PN_Then);
for (auto OutputPair : VariableOutputs) // CREATE CHAIN OF ASSIGMENTS
{
UEdGraphPin* PinWithData = CurrentCENode->FindPin(OutputPair.OutputPin->PinName);
if (PinWithData == NULL)
{
/*FText ErrorMessage = FText::Format(LOCTEXT("MissingDataPin", "ICE: Pin @@ was expecting a data output pin named {0} on @@ (each delegate must have the same signature)"), FText::FromString(OutputPair.OutputPin->PinName));
CompilerContext.MessageLog.Error(*ErrorMessage.ToString(), OutputPair.OutputPin, CurrentCENode);
return false;*/
continue;
}
UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(CurrentNode, SourceGraph);
AssignNode->AllocateDefaultPins();
bIsErrorFree &= Schema->TryCreateConnection(LastActivatedNodeThen, AssignNode->GetExecPin());
bIsErrorFree &= Schema->TryCreateConnection(OutputPair.TempVar->GetVariablePin(), AssignNode->GetVariablePin());
AssignNode->NotifyPinConnectionListChanged(AssignNode->GetVariablePin());
bIsErrorFree &= Schema->TryCreateConnection(AssignNode->GetValuePin(), PinWithData);
AssignNode->NotifyPinConnectionListChanged(AssignNode->GetValuePin());
LastActivatedNodeThen = AssignNode->GetThenPin();
}
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*PinForCurrentDelegateProperty, *LastActivatedNodeThen).CanSafeConnect();
return bIsErrorFree;
}
void UK2Node_BaseAsyncTask::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
check(SourceGraph && Schema);
bool bIsErrorFree = true;
// Create a call to factory the proxy object
UK2Node_CallFunction* const CallCreateProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallCreateProxyObjectNode->FunctionReference.SetExternalMember(ProxyFactoryFunctionName, ProxyFactoryClass);
CallCreateProxyObjectNode->AllocateDefaultPins();
if (CallCreateProxyObjectNode->GetTargetFunction() == nullptr)
{
const FString ClassName = ProxyFactoryClass ? ProxyFactoryClass->GetName() : LOCTEXT("MissingClassString", "Unknown Class").ToString();
const FString RawMessage = LOCTEXT("AsyncTaskError", "BaseAsyncTask: Missing function %s from class %s for async task @@").ToString();
const FString FormattedMessage = FString::Printf(*RawMessage, *ProxyFactoryFunctionName.GetPlainNameString(), *ClassName);
CompilerContext.MessageLog.Error(*FormattedMessage, this);
return;
}
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Execute), *CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Execute)).CanSafeConnect();
for (auto CurrentPin : Pins)
{
if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema))
{
UEdGraphPin* DestPin = CallCreateProxyObjectNode->FindPin(CurrentPin->PinName); // match function inputs, to pass data to function from CallFunction node
bIsErrorFree &= DestPin && CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect();
}
}
UEdGraphPin* const ProxyObjectPin = CallCreateProxyObjectNode->GetReturnValuePin();
check(ProxyObjectPin);
UEdGraphPin* OutputAsyncTaskProxy = FindPin(FBaseAsyncTaskHelper::GetAsyncTaskProxyName());
bIsErrorFree &= !OutputAsyncTaskProxy || CompilerContext.MovePinLinksToIntermediate(*OutputAsyncTaskProxy, *ProxyObjectPin).CanSafeConnect();
// GATHER OUTPUT PARAMETERS AND PAIR THEM WITH LOCAL VARIABLES
TArray<FBaseAsyncTaskHelper::FOutputPinAndLocalVariable> VariableOutputs;
for (auto CurrentPin : Pins)
{
if ((OutputAsyncTaskProxy != CurrentPin) && FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Output, Schema))
{
const FEdGraphPinType& PinType = CurrentPin->PinType;
UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable(
this, PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get(), PinType.bIsArray);
bIsErrorFree &= TempVarOutput->GetVariablePin() && CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *TempVarOutput->GetVariablePin()).CanSafeConnect();
VariableOutputs.Add(FBaseAsyncTaskHelper::FOutputPinAndLocalVariable(CurrentPin, TempVarOutput));
}
}
// FOR EACH DELEGATE DEFINE EVENT, CONNECT IT TO DELEGATE AND IMPLEMENT A CHAIN OF ASSIGMENTS
UEdGraphPin* LastThenPin = CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then);
UK2Node_CallFunction* IsValidFuncNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
const FName IsValidFuncName = GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, IsValid);
IsValidFuncNode->FunctionReference.SetExternalMember(IsValidFuncName, UKismetSystemLibrary::StaticClass());
IsValidFuncNode->AllocateDefaultPins();
UEdGraphPin* IsValidInputPin = IsValidFuncNode->FindPinChecked(TEXT("Object"));
bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, IsValidInputPin);
UK2Node_IfThenElse* ValidateProxyNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);
ValidateProxyNode->AllocateDefaultPins();
bIsErrorFree &= Schema->TryCreateConnection(IsValidFuncNode->GetReturnValuePin(), ValidateProxyNode->GetConditionPin());
bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, ValidateProxyNode->GetExecPin());
LastThenPin = ValidateProxyNode->GetThenPin();
for (TFieldIterator<UMulticastDelegateProperty> PropertyIt(ProxyClass, EFieldIteratorFlags::ExcludeSuper); PropertyIt && bIsErrorFree; ++PropertyIt)
{
bIsErrorFree &= FBaseAsyncTaskHelper::HandleDelegateImplementation(*PropertyIt, VariableOutputs, ProxyObjectPin, LastThenPin, this, SourceGraph, CompilerContext);
}
if (CallCreateProxyObjectNode->FindPinChecked(Schema->PN_Then) == LastThenPin)
{
CompilerContext.MessageLog.Error(*LOCTEXT("MissingDelegateProperties", "BaseAsyncTask: Proxy has no delegates defined. @@").ToString(), this);
return;
}
// Create a call to activate the proxy object if necessary
if (ProxyActivateFunctionName != NAME_None)
{
UK2Node_CallFunction* const CallActivateProxyObjectNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallActivateProxyObjectNode->FunctionReference.SetExternalMember(ProxyActivateFunctionName, ProxyClass);
CallActivateProxyObjectNode->AllocateDefaultPins();
// Hook up the self connection
UEdGraphPin* ActivateCallSelfPin = Schema->FindSelfPin(*CallActivateProxyObjectNode, EGPD_Input);
check(ActivateCallSelfPin);
bIsErrorFree &= Schema->TryCreateConnection(ProxyObjectPin, ActivateCallSelfPin);
// Hook the activate node up in the exec chain
UEdGraphPin* ActivateExecPin = CallActivateProxyObjectNode->FindPinChecked(Schema->PN_Execute);
UEdGraphPin* ActivateThenPin = CallActivateProxyObjectNode->FindPinChecked(Schema->PN_Then);
bIsErrorFree &= Schema->TryCreateConnection(LastThenPin, ActivateExecPin);
LastThenPin = ActivateThenPin;
}
// Move the connections from the original node then pin to the last internal then pin
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(Schema->PN_Then), *LastThenPin).CanSafeConnect();
bIsErrorFree &= CompilerContext.CopyPinLinksToIntermediate(*LastThenPin, *ValidateProxyNode->GetElsePin()).CanSafeConnect();
if (!bIsErrorFree)
{
CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "BaseAsyncTask: Internal connection error. @@").ToString(), this);
}
// Make sure we caught everything
BreakAllNodeLinks();
}
bool UK2Node_BaseAsyncTask::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{
const UBlueprint* SourceBlueprint = GetBlueprint();
const bool bProxyFactoryResult = (ProxyFactoryClass != NULL) && (ProxyFactoryClass->ClassGeneratedBy != SourceBlueprint);
if (bProxyFactoryResult && OptionalOutput)
{
OptionalOutput->AddUnique(ProxyFactoryClass);
}
const bool bProxyResult = (ProxyClass != NULL) && (ProxyClass->ClassGeneratedBy != SourceBlueprint);
if (bProxyResult && OptionalOutput)
{
OptionalOutput->AddUnique(ProxyClass);
}
const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
return bProxyFactoryResult || bProxyResult || bSuperResult;
}
FName UK2Node_BaseAsyncTask::GetCornerIcon() const
{
return TEXT("Graph.Latent.LatentIcon");
}
FText UK2Node_BaseAsyncTask::GetMenuCategory() const
{
UFunction* TargetFunction = GetFactoryFunction();
return UK2Node_CallFunction::GetDefaultCategoryForFunction(TargetFunction, FText::GetEmpty());
}
void UK2Node_BaseAsyncTask::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
// actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is
// mutated (or removed)... here we use the node's class (so if the node
// type disappears, then the action should go with it)
UClass* ActionKey = GetClass();
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
// check to make sure that the registrar is looking for actions of this type
// (could be regenerating actions for a specific asset, and therefore the
// registrar would only accept actions corresponding to that asset)
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
UFunction* UK2Node_BaseAsyncTask::GetFactoryFunction() const
{
if (ProxyFactoryClass == nullptr)
{
UE_LOG(LogBlueprint, Error, TEXT("ProxyFactoryClass null in %s. Was a class deleted or saved on a non promoted build?"), *GetFullName());
return nullptr;
}
UFunction* FactoryFunction = ProxyFactoryClass->FindFunctionByName(ProxyFactoryFunctionName);
check(FactoryFunction);
return FactoryFunction;
}
void UK2Node_BaseAsyncTask::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
if(UObject const* SourceObject = MessageLog.FindSourceObject(this))
{
// Lets check if it's a result of macro expansion, to give a helpful error
if(UK2Node_MacroInstance const* MacroInstance = Cast<UK2Node_MacroInstance>(SourceObject))
{
// Since it's not possible to check the graph's type, just check if this is a ubergraph using the schema's name for it
if(!(GetGraph()->HasAnyFlags(RF_Transient) && GetGraph()->GetName().StartsWith(K2Schema->FN_ExecuteUbergraphBase.ToString())))
{
MessageLog.Error(*LOCTEXT("AsyncTaskInFunctionFromMacro", "@@ is being used in Function '@@' resulting from expansion of Macro '@@'").ToString(), this, GetGraph(), MacroInstance);
}
}
}
}
TMap<FString, FAsyncTaskPinRedirectMapInfo> UK2Node_BaseAsyncTask::AsyncTaskPinRedirectMap;
bool UK2Node_BaseAsyncTask::bAsyncTaskPinRedirectMapInitialized = false;
UK2Node::ERedirectType UK2Node_BaseAsyncTask::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
{
if (GConfig && ProxyClass)
{
// Initialize remap table from INI
if (!bAsyncTaskPinRedirectMapInitialized)
{
bAsyncTaskPinRedirectMapInitialized = true;
FConfigSection* PackageRedirects = GConfig->GetSectionPrivate(TEXT("/Script/Engine.Engine"), false, true, GEngineIni);
for (FConfigSection::TIterator It(*PackageRedirects); It; ++It)
{
if (It.Key() == TEXT("K2AsyncTaskPinRedirects"))
{
FString ProxyClassString;
FString OldPinString;
FString NewPinString;
FParse::Value(*It.Value().GetValue(), TEXT("ProxyClassName="), ProxyClassString);
FParse::Value(*It.Value().GetValue(), TEXT("OldPinName="), OldPinString);
FParse::Value(*It.Value().GetValue(), TEXT("NewPinName="), NewPinString);
UClass* RedirectProxyClass = FindObject<UClass>(ANY_PACKAGE, *ProxyClassString);
if (RedirectProxyClass)
{
FAsyncTaskPinRedirectMapInfo& PinRedirectInfo = AsyncTaskPinRedirectMap.FindOrAdd(OldPinString);
TArray<UClass*>& ProxyClassArray = PinRedirectInfo.OldPinToProxyClassMap.FindOrAdd(NewPinString);
ProxyClassArray.AddUnique(RedirectProxyClass);
}
}
}
}
// See if these pins need to be remapped.
if (FAsyncTaskPinRedirectMapInfo* PinRedirectInfo = AsyncTaskPinRedirectMap.Find(OldPin->PinName))
{
if (TArray<UClass*>* ProxyClassArray = PinRedirectInfo->OldPinToProxyClassMap.Find(NewPin->PinName))
{
for (UClass* RedirectedProxyClass : *ProxyClassArray)
{
if (ProxyClass->IsChildOf(RedirectedProxyClass))
{
return UK2Node::ERedirectType_Name;
}
}
}
}
}
return Super::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex);
}
#undef LOCTEXT_NAMESPACE