Ejemplo n.º 1
0
// 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();
}