// TODO: Refactor this (and some other stuff) into a preprocessing step for use by any compiler? bool FNiagaraCompiler::MergeInFunctionNodes() { struct FReconnectionInfo { public: UEdGraphPin* From; TArray<UEdGraphPin*> To; //Fallback default value if an input connection is not connected. FString FallbackDefault; FReconnectionInfo() : From(NULL) {} }; TMap<FName, FReconnectionInfo> InputConnections; TMap<FName, FReconnectionInfo> OutputConnections; TArray<class UEdGraphPin*> FuncCallInputPins; TArray<class UEdGraphPin*> FuncCallOutputPins; //Copies the function graph into the main graph. //Removes the Function call in the main graph and the input and output nodes in the function graph, reconnecting their pins appropriately. auto MergeFunctionIntoMainGraph = [&](UNiagaraNodeFunctionCall* InFunc, UNiagaraGraph* FuncGraph) { InputConnections.Empty(); OutputConnections.Empty(); FuncCallInputPins.Empty(); FuncCallOutputPins.Empty(); check(InFunc && FuncGraph); if (InFunc->FunctionScript) { //Get all the pins that are connected to the inputs of the function call node in the main graph. InFunc->GetInputPins(FuncCallInputPins); for (UEdGraphPin* FuncCallInputPin : FuncCallInputPins) { FName InputName(*FuncCallInputPin->PinName); FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputName); if (FuncCallInputPin->LinkedTo.Num() > 0) { check(FuncCallInputPin->LinkedTo.Num() == 1); UEdGraphPin* LinkFrom = FuncCallInputPin->LinkedTo[0]; check(LinkFrom->Direction == EGPD_Output); InputConnection.From = LinkFrom; } else { //This input has no link so we need the default value from the pin. InputConnection.FallbackDefault = FuncCallInputPin->GetDefaultAsString(); } } //Get all the pins that are connected to the outputs of the function call node in the main graph. InFunc->GetOutputPins(FuncCallOutputPins); for (UEdGraphPin* FuncCallOutputPin : FuncCallOutputPins) { FName OutputName(*FuncCallOutputPin->PinName); for (UEdGraphPin* LinkTo : FuncCallOutputPin->LinkedTo) { check(LinkTo->Direction == EGPD_Input); FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName); OutputConnection.To.Add(LinkTo); } } //Remove the function call node from the graph now that we have everything we need from it. SourceGraph->RemoveNode(InFunc); //Keep a list of the Input and Output nodes we see in the function graph so that we can remove (most of) them later. TArray<UEdGraphNode*, TInlineAllocator<64>> ToRemove; //Search the nodes in the function graph, finding any connections to input or output nodes. for (UEdGraphNode* FuncGraphNode : FuncGraph->Nodes) { if (UNiagaraNodeInput* InputNode = Cast<UNiagaraNodeInput>(FuncGraphNode)) { check(InputNode->Pins.Num() == 1); //Get an array of "To" pins from one or more input nodes referencing each named input. FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputNode->Input.Name); if (InputConnection.From) { //We have a connection from the function call so remove the input node and connect to that. ToRemove.Add(InputNode); } else { //This input has no connection from the function call so what do we do here? //For now we just leave the input node and connect back to it. //This will mean unconnected pins from the function call will look for constants or attributes. //In some cases we may want to just take the default value from the function call pin instead? //Maybe have some properties on the function call defining that. InputConnection.From = InputNode->Pins[0]; } TArray<UEdGraphPin*>& LinkToPins = InputNode->Pins[0]->LinkedTo; for (UEdGraphPin* ToPin : LinkToPins) { check(ToPin->Direction == EGPD_Input); InputConnection.To.Add(ToPin); } } else if (UNiagaraNodeOutput* OutputNode = Cast<UNiagaraNodeOutput>(FuncGraphNode)) { //Unlike the input nodes, we don't have the option of keeping these if there is no "From" pin. The default values from the node pins should be used. ToRemove.Add(OutputNode); //For each output, get the "From" pin to be reconnected later. for (int32 OutputIdx = 0; OutputIdx < OutputNode->Outputs.Num(); ++OutputIdx) { FName OutputName = OutputNode->Outputs[OutputIdx].Name; UEdGraphPin* OutputNodePin = OutputNode->Pins[OutputIdx]; check(OutputNodePin->LinkedTo.Num() <= 1); FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName); UEdGraphPin* LinkFromPin = OutputNodePin->LinkedTo.Num() == 1 ? OutputNodePin->LinkedTo[0] : NULL; if (LinkFromPin) { check(LinkFromPin->Direction == EGPD_Output); OutputConnection.From = LinkFromPin; } else { //This output is not connected so links to it in the main graph must use it's default value. OutputConnection.FallbackDefault = OutputNodePin->GetDefaultAsString(); } } } } //Remove all the In and Out nodes from the function graph. for (UEdGraphNode* Remove : ToRemove) { FuncGraph->RemoveNode(Remove); } //Copy the nodes from the function graph over into the main graph. FuncGraph->MoveNodesToAnotherGraph(SourceGraph, false); //Finally, do all the reconnection. auto MakeConnection = [&](FReconnectionInfo& Info) { for (UEdGraphPin* LinkTo : Info.To) { if (Info.From) { Info.From->MakeLinkTo(LinkTo); } else { LinkTo->DefaultValue = Info.FallbackDefault; } } }; for (TPair<FName, FReconnectionInfo>& ReconnectInfo : InputConnections){ MakeConnection(ReconnectInfo.Value); } for (TPair<FName, FReconnectionInfo>& ReconnectInfo : OutputConnections){ MakeConnection(ReconnectInfo.Value); } } }; //Helper struct for traversing nested function calls. struct FFunctionContext { //True if this context's function has been merged into the main graph. bool bProcessed; //The index of this context into the ContextPool. int32 PoolIdx; //Pointer back to the parent context for traversal. FFunctionContext* Parent; //The function call node for this function in the source/parent graph. UNiagaraNodeFunctionCall* Function; //The graph for this function that we are going to merge into the main graph. UNiagaraGraph* FunctionGraph; //The script from which the graph is copied. Used for re entrance check. UNiagaraScript* Script; //Contexts for function calls in this function graph. TArray<FFunctionContext*, TInlineAllocator<64>> SubFunctionCalls; FFunctionContext() : bProcessed(false) , PoolIdx(INDEX_NONE) , Parent(NULL) , Function(NULL) , FunctionGraph(NULL) , Script(NULL) { } /** We don't allow re-entrant functions as this would cause an infinite loop of merging in graphs. Maybe in the future if we allow branching in the VM we can allow this. */ bool CheckForReentrance()const { UNiagaraNodeFunctionCall* Func = Function; FFunctionContext* Curr = Parent; while (Curr) { if (Curr->Script == Script) return true; Curr = Curr->Parent; } return false; } FString GetCallstack()const { FString Ret; const FFunctionContext* Curr = this; while (Curr) { if (Curr->Script) { Ret.Append(*(Curr->Script->GetPathName())); } else { Ret.Append(TEXT("Unknown")); } Ret.Append(TEXT("\n")); Curr = Curr->Parent; } return Ret; } }; //A pool of contexts on the stack to avoid loads of needless, small heap allocations. TArray<FFunctionContext, TInlineAllocator<512>> ContextPool; ContextPool.Reserve(512); FFunctionContext RootContext; FFunctionContext* CurrentContext = &RootContext; CurrentContext->FunctionGraph = SourceGraph; CurrentContext->Script = Script; //Depth first traversal of all function calls. while (CurrentContext) { //Find any sub functions and process this function call. if (!CurrentContext->bProcessed) { CurrentContext->bProcessed = true; //Find any sub functions and check for re-entrance. if (CurrentContext->FunctionGraph) { for (UEdGraphNode* Node : CurrentContext->FunctionGraph->Nodes) { UNiagaraNodeFunctionCall* FuncNode = Cast<UNiagaraNodeFunctionCall>(Node); if (FuncNode) { int32 NewIdx = ContextPool.AddZeroed(); FFunctionContext* SubFuncContext = &ContextPool[NewIdx]; CurrentContext->SubFunctionCalls.Push(SubFuncContext); SubFuncContext->Parent = CurrentContext; SubFuncContext->Function = FuncNode; SubFuncContext->PoolIdx = NewIdx; SubFuncContext->Script = FuncNode->FunctionScript; if (SubFuncContext->CheckForReentrance()) { FString Callstack = SubFuncContext->GetCallstack(); MessageLog.Error(TEXT("Reentrant function call!\n%s"), *Callstack); return false; } //Copy the function graph as we'll be modifying it as we merge in with the main graph. UNiagaraScriptSource* FuncSource = CastChecked<UNiagaraScriptSource>(FuncNode->FunctionScript->Source); check(FuncSource); SubFuncContext->FunctionGraph = CastChecked<UNiagaraGraph>(FEdGraphUtilities::CloneGraph(FuncSource->NodeGraph, NULL, &MessageLog)); } } } //Merge this function into the main graph now. if (CurrentContext->Function && CurrentContext->FunctionGraph) { MergeFunctionIntoMainGraph(CurrentContext->Function, CurrentContext->FunctionGraph); } } if (CurrentContext->SubFunctionCalls.Num() > 0) { //Move to the next sub function. CurrentContext = CurrentContext->SubFunctionCalls.Pop(); } else { //Done processing this function so remove it and move back to the parent. if (CurrentContext->PoolIdx != INDEX_NONE) { CurrentContext->FunctionGraph->MarkPendingKill(); ContextPool.RemoveAtSwap(CurrentContext->PoolIdx); } CurrentContext = CurrentContext->Parent; } } return true; }
void UK2Node_EaseFunction::PinTypeChanged(UEdGraphPin* Pin) { const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>(); bool bChanged = false; if (Pin->PinName == FEaseFunctionNodeHelper::GetAPinName() || Pin->PinName == FEaseFunctionNodeHelper::GetBPinName() || Pin->PinName == FEaseFunctionNodeHelper::GetResultPinName()) { // Get pin refs UEdGraphPin* APin = FindPin(FEaseFunctionNodeHelper::GetAPinName()); UEdGraphPin* BPin = FindPin(FEaseFunctionNodeHelper::GetBPinName()); UEdGraphPin* ResultPin = FindPin(FEaseFunctionNodeHelper::GetResultPinName()); // Propagate the type change or reset to wildcard PinType if (Pin->LinkedTo.Num() > 0) { UEdGraphPin* InstigatorPin = Pin->LinkedTo[0]; bChanged |= UpdatePin(APin, InstigatorPin); bChanged |= UpdatePin(BPin, InstigatorPin); bChanged |= UpdatePin(ResultPin, InstigatorPin); if (bChanged) { // Just in case we switch to an invalid function clean it first EaseFunctionName = TEXT(""); // Generate the right function name if (InstigatorPin->PinType.PinCategory == Schema->PC_Float) { EaseFunctionName = TEXT("Ease"); } else if (InstigatorPin->PinType.PinCategory == Schema->PC_Struct) { if (InstigatorPin->PinType.PinSubCategoryObject.Get()->GetName() == TEXT("Vector")) { EaseFunctionName = TEXT("VEase"); } else if (InstigatorPin->PinType.PinSubCategoryObject.Get()->GetName() == TEXT("Rotator")) { EaseFunctionName = TEXT("REase"); } else if (InstigatorPin->PinType.PinSubCategoryObject.Get()->GetName() == TEXT("Transform")) { EaseFunctionName = TEXT("TEase"); } } } } else { if (APin->GetDefaultAsString().IsEmpty() && APin->LinkedTo.Num() == 0 && BPin->GetDefaultAsString().IsEmpty() && BPin->LinkedTo.Num() == 0 && ResultPin->LinkedTo.Num() == 0) { // Restore wild card pin APin->PinType.PinCategory = Schema->PC_Wildcard; APin->PinType.PinSubCategory = TEXT(""); APin->PinType.PinSubCategoryObject = NULL; // Propagate change UpdatePin(BPin, APin); UpdatePin(ResultPin, APin); // Make sure the function name is nulled out EaseFunctionName = TEXT(""); bChanged = true; } } // Pin connections and data changed in some way if (bChanged) { SetPinToolTip(*APin, LOCTEXT("APinDescription", "Easing start value")); SetPinToolTip(*BPin, LOCTEXT("BPinDescription", "Easing end value")); SetPinToolTip(*ResultPin, LOCTEXT("ResultPinDescription", "Easing result value")); // Let our subclasses generate some pins if required, this way we can add any aditional pins required by some types for examples GenerateExtraPins(); // Let the graph know to refresh GetGraph()->NotifyGraphChanged(); UBlueprint* Blueprint = GetBlueprint(); if (!Blueprint->bBeingCompiled) { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); Blueprint->BroadcastChanged(); } } } Super::PinTypeChanged(Pin); }
void UGameplayTagsK2Node_LiteralGameplayTag::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); // Get The input and output pins to our node UEdGraphPin* TagInPin = FindPin(TEXT("TagIn")); UEdGraphPin* TagOutPin = FindPinChecked(Schema->PN_ReturnValue); // Create a Make Struct UK2Node_MakeStruct* MakeStructNode = SourceGraph->CreateBlankNode<UK2Node_MakeStruct>(); MakeStructNode->StructType = FGameplayTagContainer::StaticStruct(); MakeStructNode->AllocateDefaultPins(); // Create a Make Array UK2Node_MakeArray* MakeArrayNode = SourceGraph->CreateBlankNode<UK2Node_MakeArray>(); MakeArrayNode->AllocateDefaultPins(); // Connect the output of our MakeArray to the Input of our MakeStruct so it sets the Array Type UEdGraphPin* InPin = MakeStructNode->FindPin( TEXT("GameplayTags") ); if( InPin ) { InPin->MakeLinkTo( MakeArrayNode->GetOutputPin() ); } // Add the FName Values to the MakeArray input pins UEdGraphPin* ArrayInputPin = NULL; FString TagString = TagInPin->GetDefaultAsString(); if( TagString.StartsWith( TEXT("(") ) && TagString.EndsWith( TEXT(")") ) ) { TagString = TagString.LeftChop(1); TagString = TagString.RightChop(1); TagString.Split("=", NULL, &TagString); TagString = TagString.LeftChop(1); TagString = TagString.RightChop(1); FString ReadTag; FString Remainder; int32 MakeIndex = 0; while( TagString.Split( TEXT(","), &ReadTag, &Remainder ) ) { TagString = Remainder; ArrayInputPin = MakeArrayNode->FindPin( FString::Printf( TEXT("[%d]"), MakeIndex ) ); ArrayInputPin->PinType.PinCategory = TEXT("struct"); ArrayInputPin->PinType.PinSubCategoryObject = FGameplayTag::StaticStruct(); ArrayInputPin->DefaultValue = ReadTag; MakeIndex++; MakeArrayNode->AddInputPin(); } if( Remainder.IsEmpty() ) { Remainder = TagString; } if( !Remainder.IsEmpty() ) { ArrayInputPin = MakeArrayNode->FindPin( FString::Printf( TEXT("[%d]"), MakeIndex ) ); ArrayInputPin->PinType.PinCategory = TEXT("struct"); ArrayInputPin->PinType.PinSubCategoryObject = FGameplayTag::StaticStruct(); ArrayInputPin->DefaultValue = Remainder; MakeArrayNode->PostReconstructNode(); } else { MakeArrayNode->RemoveInputPin(MakeArrayNode->FindPin(TEXT("[0]"))); MakeArrayNode->PostReconstructNode(); } } else { MakeArrayNode->RemoveInputPin(MakeArrayNode->FindPin(TEXT("[0]"))); MakeArrayNode->PostReconstructNode(); } // Move the Output of the MakeArray to the Output of our node UEdGraphPin* OutPin = MakeStructNode->FindPin( MakeStructNode->StructType->GetName() ); if( OutPin && TagOutPin ) { OutPin->PinType = TagOutPin->PinType; // Copy type so it uses the right actor subclass CompilerContext.MovePinLinksToIntermediate(*TagOutPin, *OutPin); } // Break any links to the expanded node BreakAllNodeLinks(); }
void FNodeHandlingFunctor::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) { for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) { UEdGraphPin* Pin = Node->Pins[PinIndex]; if (!CompilerContext.GetSchema()->IsMetaPin(*Pin) || (CompilerContext.GetSchema()->IsSelfPin(*Pin) && Pin->LinkedTo.Num() == 0 && Pin->DefaultObject) ) { UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(Pin); if (Context.NetMap.Find(Net) == NULL) { // New net, resolve the term that will be used to construct it FBPTerminal* Term = NULL; if ((Net->Direction == EGPD_Input) && (Net->LinkedTo.Num() == 0)) { // Make sure the default value is valid FString DefaultAllowedResult = CompilerContext.GetSchema()->IsCurrentPinDefaultValid(Net); if (DefaultAllowedResult != TEXT("")) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("InvalidDefaultValue_Error", "Default value '%s' for @@ is invalid: '%s'").ToString(), *(Net->GetDefaultAsString()), *DefaultAllowedResult), Net); // Skip over these properties if they are array or ref properties, because the backend can't emit valid code for them if( Pin->PinType.bIsArray || Pin->PinType.bIsReference ) { continue; } } Term = Context.RegisterLiteral(Net); Context.NetMap.Add(Net, Term); } else { RegisterNet(Context, Pin); } } } } }
void UK2Node_GetEnumeratorName::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); UEnum* Enum = GetEnum(); if(NULL == Enum) { CompilerContext.MessageLog.Error(*FString::Printf(*NSLOCTEXT("K2Node", "GetEnumeratorNam_Error_MustHaveValidName", "@@ must have a valid enum defined").ToString()), this); return; } const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); const UFunction* Function = UKismetNodeHelperLibrary::StaticClass()->FindFunctionByName( GetFunctionName() ); check(NULL != Function); UK2Node_CallFunction* CallGetName = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); CallGetName->SetFromFunction(Function); CallGetName->AllocateDefaultPins(); check(CallGetName->IsNodePure()); //OPUTPUT PIN UEdGraphPin* OrgReturnPin = FindPinChecked(Schema->PN_ReturnValue); UEdGraphPin* NewReturnPin = CallGetName->GetReturnValuePin(); check(NULL != NewReturnPin); CompilerContext.MovePinLinksToIntermediate(*OrgReturnPin, *NewReturnPin); //ENUM PIN UEdGraphPin* EnumPin = CallGetName->FindPinChecked(TEXT("Enum")); Schema->TrySetDefaultObject(*EnumPin, Enum); check(EnumPin->DefaultObject == Enum); //VALUE PIN UEdGraphPin* OrgInputPin = FindPinChecked(EnumeratorPinName); UEdGraphPin* IndexPin = CallGetName->FindPinChecked(TEXT("EnumeratorValue")); check(EGPD_Input == IndexPin->Direction && Schema->PC_Byte == IndexPin->PinType.PinCategory); CompilerContext.MovePinLinksToIntermediate(*OrgInputPin, *IndexPin); if (!IndexPin->LinkedTo.Num()) { //MAKE LITERAL BYTE FROM LITERAL ENUM const FString EnumLiteral = IndexPin->GetDefaultAsString(); const int32 NumericValue = Enum->GetValueByName(*EnumLiteral); if (NumericValue == INDEX_NONE) { CompilerContext.MessageLog.Error(*FString::Printf(*NSLOCTEXT("K2Node", "GetEnumeratorNam_Error_InvalidName", "@@ has invalid enum value '%s'").ToString(), *EnumLiteral), this); return; } const FString DefaultByteValue = FString::FromInt(NumericValue); // LITERAL BYTE FUNCTION const FName FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, MakeLiteralByte); UK2Node_CallFunction* MakeLiteralByte = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); MakeLiteralByte->SetFromFunction(UKismetSystemLibrary::StaticClass()->FindFunctionByName(FunctionName)); MakeLiteralByte->AllocateDefaultPins(); UEdGraphPin* MakeLiteralByteReturnPin = MakeLiteralByte->FindPinChecked(Schema->PN_ReturnValue); Schema->TryCreateConnection(MakeLiteralByteReturnPin, IndexPin); UEdGraphPin* MakeLiteralByteInputPin = MakeLiteralByte->FindPinChecked(TEXT("Value")); MakeLiteralByteInputPin->DefaultValue = DefaultByteValue; } BreakAllNodeLinks(); }