// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "BlueprintGraphPrivatePCH.h" #include "CompilerResultsLog.h" #include "KismetCompiler.h" #define LOCTEXT_NAMESPACE "UK2Node_BaseAsyncTask" UK2Node_BaseAsyncTask::UK2Node_BaseAsyncTask(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) , ProxyFactoryFunctionName(NAME_None) , ProxyFactoryClass(NULL) , ProxyClass(NULL) { } FName UK2Node_BaseAsyncTask::GetProxyFactoryFunctionName() { return ProxyFactoryFunctionName; } UClass* UK2Node_BaseAsyncTask::GetProxyFactoryClass() { return ProxyFactoryClass; } UClass* UK2Node_BaseAsyncTask::GetProxyClass() { return ProxyClass; } FString UK2Node_BaseAsyncTask::GetCategoryName() { return TEXT("Latent Execution"); } void UK2Node_BaseAsyncTask::AllocateDefaultPins() { const UEdGraphSchema_K2* K2Schema = GetDefault(); 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); UFunction* DelegateSignatureFunction = NULL; for (TFieldIterator PropertyIt(ProxyClass, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { UMulticastDelegateProperty* Property = Cast(*PropertyIt); if(!Property) continue; CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, *Property->GetName()); if (!DelegateSignatureFunction) { DelegateSignatureFunction = Property->SignatureFunction; } } if (DelegateSignatureFunction) { for (TFieldIterator 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->FindFunctionByName(ProxyFactoryFunctionName); if (Function) { TSet PinsToHide; FBlueprintEditorUtils::GetHiddenPinsForFunction(GetBlueprint(), Function, PinsToHide); for (TFieldIterator 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->HasAnyPropertyFlags(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; } FString MetadataDefaultValue = Function->GetMetaData(*Param->GetName()); if (!MetadataDefaultValue.IsEmpty()) { // Specified default value in the metadata Pin->DefaultValue = Pin->AutogeneratedDefaultValue = MetadataDefaultValue; } else { const FName MetadataCppDefaultValueKey( *(FString(TEXT("CPP_Default_")) + Param->GetName()) ); const FString MetadataCppDefaultValue = Function->GetMetaData(MetadataCppDefaultValueKey); if(!MetadataCppDefaultValue.IsEmpty()) { Pin->DefaultValue = Pin->AutogeneratedDefaultValue = MetadataCppDefaultValue; } else { // Set the default value to (T)0 K2Schema->SetPinDefaultValueBasedOnType(Pin); } } if (PinsToHide.Contains(Pin->PinName)) { Pin->bHidden = true; Pin->DefaultValue = TEXT("0"); } } bAllPinsGood = bAllPinsGood && bPinGood; } } Super::AllocateDefaultPins(); } struct FBaseAsyncTaskHelper { static bool 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; } static bool 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(CurrentNode, SourceGraph); SelfNode->AllocateDefaultPins(); UK2Node_CreateDelegate* CreateDelegateNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); CreateDelegateNode->AllocateDefaultPins(); bResult &= Schema->TryCreateConnection(DelegateInputPin, CreateDelegateNode->GetDelegateOutPin()); bResult &= Schema->TryCreateConnection(SelfNode->FindPinChecked(Schema->PN_Self), CreateDelegateNode->GetObjectInPin()); CreateDelegateNode->SelectedFunctionName = FunctionName; return bResult; } struct FOutputPinAndLocalVariable { UEdGraphPin* OutputPin; UK2Node_TemporaryVariable* TempVar; FOutputPinAndLocalVariable(UEdGraphPin* Pin, UK2Node_TemporaryVariable* Var) : OutputPin(Pin), TempVar(Var) {} }; static bool CopyEventSignature(UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema) { check(CENode && Function && Schema); bool bResult = true; for (TFieldIterator 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)); } } return bResult; } static bool HandleDelegateImplementation( UMulticastDelegateProperty* CurrentProperty, const TArray& 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(CurrentNode, SourceGraph); { UK2Node_AddDelegate* AddDelegateNode = CompilerContext.SpawnIntermediateNode(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(), *CurrentCENode->NodeGuid.ToString()); 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; } UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode(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) { if (!CompilerContext.bIsFullCompile) { return; } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UK2Node_BaseAsyncTask* const CurrentNode = this; check(SourceGraph && Schema && CurrentNode); bool bIsErrorFree = true; UK2Node_CallFunction* const CallCreateMoveToProxyObjectNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); CallCreateMoveToProxyObjectNode->FunctionReference.SetExternalMember(CurrentNode->GetProxyFactoryFunctionName(), CurrentNode->GetProxyFactoryClass()); CallCreateMoveToProxyObjectNode->AllocateDefaultPins(); bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*CurrentNode->FindPinChecked(Schema->PN_Execute), *CallCreateMoveToProxyObjectNode->FindPinChecked(Schema->PN_Execute)).CanSafeConnect(); for (auto CurrentPin : CurrentNode->Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Input, Schema)) { UEdGraphPin* DestPin = CallCreateMoveToProxyObjectNode->FindPin(CurrentPin->PinName); // match function inputs, to pass data to function from CallFunction node bIsErrorFree &= DestPin && CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *DestPin).CanSafeConnect(); } } // GATHER OUTPUT PARAMETERS AND PAIR THEM WITH LOCAL VARIABLES TArray VariableOutputs; for (auto CurrentPin : CurrentNode->Pins) { if (FBaseAsyncTaskHelper::ValidDataPin(CurrentPin, EGPD_Output, Schema)) { const FEdGraphPinType& PinType = CurrentPin->PinType; UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable( CurrentNode, 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 = CallCreateMoveToProxyObjectNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* const ProxyObjectPin = CallCreateMoveToProxyObjectNode->GetReturnValuePin(); for (TFieldIterator PropertyIt(CurrentNode->GetProxyClass(), EFieldIteratorFlags::ExcludeSuper); PropertyIt && bIsErrorFree; ++PropertyIt) { bIsErrorFree &= FBaseAsyncTaskHelper::HandleDelegateImplementation(*PropertyIt, VariableOutputs, ProxyObjectPin, LastThenPin, CurrentNode, SourceGraph, CompilerContext); } if (CallCreateMoveToProxyObjectNode->FindPinChecked(Schema->PN_Then) == LastThenPin) { CompilerContext.MessageLog.Error(*LOCTEXT("MissingDelegateProperties", "BaseAsyncTask: Proxy has no delegates defined. @@").ToString(), CurrentNode); return; } bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*CurrentNode->FindPinChecked(Schema->PN_Then), *LastThenPin).CanSafeConnect(); if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "BaseAsyncTask: Internal connection error. @@").ToString(), CurrentNode); } CurrentNode->BreakAllNodeLinks(); } bool UK2Node_BaseAsyncTask::HasExternalBlueprintDependencies(TArray* OptionalOutput) const { const UBlueprint* SourceBlueprint = GetBlueprint(); const bool bProxyFactoryResult = (ProxyFactoryClass != NULL) && (ProxyFactoryClass->ClassGeneratedBy != NULL) && (ProxyFactoryClass->ClassGeneratedBy != SourceBlueprint); if (bProxyFactoryResult && OptionalOutput) { OptionalOutput->Add(ProxyFactoryClass); } const bool bProxyResult = (ProxyClass != NULL) && (ProxyClass->ClassGeneratedBy != NULL) && (ProxyClass->ClassGeneratedBy != SourceBlueprint); if (bProxyResult && OptionalOutput) { OptionalOutput->Add(ProxyClass); } return bProxyFactoryResult || bProxyResult; } #undef LOCTEXT_NAMESPACE