You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This will occur with every structural modification to any BP, node titles refresh only when visible. [CL 2499923 by Michael Schoell in Main branch]
303 lines
12 KiB
C++
303 lines
12 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BlueprintGraphPrivatePCH.h"
|
|
#include "KismetCompiler.h"
|
|
#include "K2Node_CallArrayFunction.h"
|
|
#include "K2Node_Message.h"
|
|
#include "Kismet/KismetArrayLibrary.h"
|
|
#include "K2Node_TemporaryVariable.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "K2Node_Message"
|
|
|
|
UK2Node_Message::UK2Node_Message(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
FText UK2Node_Message::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
|
{
|
|
FText NodeName;
|
|
if (UFunction* Function = GetTargetFunction())
|
|
{
|
|
if (!CachedNodeTitles.IsTitleCached(TitleType, this))
|
|
{
|
|
FText NodeNameText = UK2Node_CallFunction::GetUserFacingFunctionName(Function);
|
|
if (TitleType == ENodeTitleType::MenuTitle)
|
|
{
|
|
// FText::Format() is slow, so we cache this to save on performance
|
|
CachedNodeTitles.SetCachedTitle(TitleType, FText::Format(LOCTEXT("ListTitle", "{0} (Message)"), NodeNameText), this);
|
|
}
|
|
else
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeName"), NodeNameText);
|
|
Args.Add(TEXT("OuterClassName"), FText::FromString(Function->GetOuterUClass()->GetName()));
|
|
|
|
FText NodeTitle = FText::Format(NSLOCTEXT("K2Node", "CallInterfaceContext", "{NodeName}\nUsing Interface {OuterClassName}"), Args);
|
|
// FText::Format() is slow, so we cache this to save on performance
|
|
CachedNodeTitles.SetCachedTitle(TitleType, NodeTitle, this);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NSLOCTEXT("K2Node", "InvalidMessageNode", "Invalid Message Node");
|
|
}
|
|
return CachedNodeTitles.GetCachedTitle(TitleType);
|
|
}
|
|
|
|
void UK2Node_Message::AllocateDefaultPins()
|
|
{
|
|
UFunction* MessageNodeFunction = GetTargetFunction();
|
|
// since we have branching logic in ExpandNode(), this has to be an impure
|
|
// node with exec pins
|
|
//
|
|
// @TODO: make it so we can have impure message nodes using a custom
|
|
// FNodeHandlingFunctor, instead of ExpandNode()
|
|
if (MessageNodeFunction && MessageNodeFunction->HasAnyFunctionFlags(FUNC_BlueprintPure))
|
|
{
|
|
// Input - Execution Pin
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, TEXT(""), NULL, false, false, UEdGraphSchema_K2::PN_Execute);
|
|
// Output - Execution Pin
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, TEXT(""), NULL, false, false, UEdGraphSchema_K2::PN_Then);
|
|
}
|
|
|
|
Super::AllocateDefaultPins();
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_Message::CreateSelfPin(const UFunction* Function)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
UEdGraphPin* SelfPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), UObject::StaticClass(), false, false, K2Schema->PN_Self);
|
|
SelfPin->bDefaultValueIsIgnored = true;
|
|
return SelfPin;
|
|
}
|
|
|
|
void UK2Node_Message::EnsureFunctionIsInBlueprint()
|
|
{
|
|
// Do nothing; the function either exists and works, or doesn't and doesn't
|
|
}
|
|
|
|
FName UK2Node_Message::GetCornerIcon() const
|
|
{
|
|
return TEXT("Graph.Message.MessageIcon");
|
|
}
|
|
|
|
FNodeHandlingFunctor* UK2Node_Message::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
|
|
{
|
|
return new FNodeHandlingFunctor(CompilerContext);
|
|
}
|
|
|
|
void UK2Node_Message::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
|
{
|
|
Super::ExpandNode(CompilerContext, SourceGraph);
|
|
|
|
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
|
|
|
|
UEdGraphPin* ExecPin = Schema->FindExecutionPin(*this, EGPD_Input);
|
|
const bool bExecPinConnected = ExecPin && (ExecPin->LinkedTo.Num() > 0);
|
|
UEdGraphPin* ThenPin = Schema->FindExecutionPin(*this, EGPD_Output);
|
|
const bool bThenPinConnected = ThenPin && (ThenPin->LinkedTo.Num() > 0);
|
|
|
|
// Skip ourselves if our exec isn't wired up
|
|
if (bExecPinConnected)
|
|
{
|
|
// Make sure our interface is valid
|
|
if (FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode()) == NULL)
|
|
{
|
|
CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalid_Error", "Message node @@ has an invalid interface.").ToString()), this);
|
|
return;
|
|
}
|
|
|
|
UFunction* MessageNodeFunction = GetTargetFunction();
|
|
if (MessageNodeFunction == NULL)
|
|
{
|
|
//@TODO: Why do this here in teh compiler, it's already done on AllocateDefaultPins() during on-load node reconstruction
|
|
MessageNodeFunction = Cast<UFunction>(FMemberReference::FindRemappedField(FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode()), FunctionReference.GetMemberName()));
|
|
}
|
|
|
|
if (MessageNodeFunction == NULL)
|
|
{
|
|
CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalidFunction_Error", "Unable to find function with name %s for Message node @@.").ToString(), *(FunctionReference.GetMemberName().ToString())), this);
|
|
return;
|
|
}
|
|
|
|
// Check to make sure we have a target
|
|
UEdGraphPin* MessageSelfPin = Schema->FindSelfPin(*this, EGPD_Input);
|
|
if( !MessageSelfPin || MessageSelfPin->LinkedTo.Num() == 0 )
|
|
{
|
|
CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeSelfPin_Error", "Message node @@ must have a valid target or reference to self.").ToString()), this);
|
|
return;
|
|
}
|
|
|
|
// First, create an internal cast-to-interface node
|
|
UK2Node_DynamicCast* CastToInterfaceNode = CompilerContext.SpawnIntermediateNode<UK2Node_DynamicCast>(this, SourceGraph);
|
|
CastToInterfaceNode->TargetType = MessageNodeFunction->GetOuterUClass();
|
|
CastToInterfaceNode->SetPurity(false);
|
|
CastToInterfaceNode->AllocateDefaultPins();
|
|
|
|
UEdGraphPin* CastToInterfaceResultPin = CastToInterfaceNode->GetCastResultPin();
|
|
if( !CastToInterfaceResultPin )
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("InvalidInterfaceClass_Error", "Node @@ has an invalid target interface class").ToString(), this);
|
|
return;
|
|
}
|
|
|
|
CastToInterfaceResultPin->PinType.PinSubCategoryObject = *CastToInterfaceNode->TargetType;
|
|
|
|
if (ExecPin != nullptr)
|
|
{
|
|
UEdGraphPin* CastExecInput = CastToInterfaceNode->GetExecPin();
|
|
check(CastExecInput != nullptr);
|
|
|
|
// Wire up the connections
|
|
CompilerContext.MovePinLinksToIntermediate(*ExecPin, *CastExecInput);
|
|
}
|
|
|
|
UEdGraphPin* CastToInterfaceSourceObjectPin = CastToInterfaceNode->GetCastSourcePin();
|
|
CompilerContext.MovePinLinksToIntermediate(*MessageSelfPin, *CastToInterfaceSourceObjectPin);
|
|
CastToInterfaceNode->PinConnectionListChanged(CastToInterfaceSourceObjectPin);
|
|
|
|
// Next, create the function call node
|
|
UK2Node_CallFunction* FunctionCallNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
|
|
FunctionCallNode->bIsInterfaceCall = true;
|
|
FunctionCallNode->FunctionReference = FunctionReference;
|
|
FunctionCallNode->AllocateDefaultPins();
|
|
|
|
UEdGraphPin* CastToInterfaceValidPin = CastToInterfaceNode->GetValidCastPin();
|
|
check(CastToInterfaceValidPin != nullptr);
|
|
|
|
UEdGraphPin* LastOutCastFaildPin = CastToInterfaceNode->GetInvalidCastPin();
|
|
check(LastOutCastFaildPin != nullptr);
|
|
UEdGraphPin* LastOutCastSuccessPin = CastToInterfaceValidPin;
|
|
// Wire up the connections
|
|
if (UEdGraphPin* CallFunctionExecPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Input))
|
|
{
|
|
CastToInterfaceValidPin->MakeLinkTo(CallFunctionExecPin);
|
|
LastOutCastSuccessPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Output);
|
|
}
|
|
|
|
// Self pin
|
|
UEdGraphPin* FunctionCallSelfPin = Schema->FindSelfPin(*FunctionCallNode, EGPD_Input);
|
|
CastToInterfaceResultPin->MakeLinkTo(FunctionCallSelfPin);
|
|
|
|
UFunction* ArrayClearFunction = UKismetArrayLibrary::StaticClass()->FindFunctionByName(FName(TEXT("Array_Clear")));
|
|
check(ArrayClearFunction);
|
|
|
|
bool const bIsPureFunc = Super::IsNodePure();
|
|
// Variable pins - Try to associate variable inputs to the message node with the variable inputs and outputs to the call function node
|
|
for( int32 i = 0; i < Pins.Num(); i++ )
|
|
{
|
|
UEdGraphPin* CurrentPin = Pins[i];
|
|
if( CurrentPin && (CurrentPin->PinType.PinCategory != Schema->PC_Exec) && (CurrentPin->PinName != Schema->PN_Self) )
|
|
{
|
|
// Try to find a match for the pin on the function call node
|
|
UEdGraphPin* FunctionCallPin = FunctionCallNode->FindPin(CurrentPin->PinName);
|
|
if( FunctionCallPin )
|
|
{
|
|
// Move pin links if the pin is connected...
|
|
CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *FunctionCallPin);
|
|
|
|
// when cast fails all return values must be cleared.
|
|
if (EEdGraphPinDirection::EGPD_Output == CurrentPin->Direction)
|
|
{
|
|
UEdGraphPin* VarOutPin = FunctionCallPin;
|
|
if (bIsPureFunc)
|
|
{
|
|
// since we cannot directly use the output from the
|
|
// function call node (since it is pure, and invoking
|
|
// it with a null target would cause an error), we
|
|
// have to use a temporary variable in it's place...
|
|
UK2Node_TemporaryVariable* TempVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
|
|
TempVar->VariableType = CurrentPin->PinType;
|
|
TempVar->AllocateDefaultPins();
|
|
|
|
VarOutPin = TempVar->GetVariablePin();
|
|
// nodes using the function's outputs directly, now
|
|
// use this TempVar node instead
|
|
CompilerContext.MovePinLinksToIntermediate(*FunctionCallPin, *VarOutPin);
|
|
|
|
// on a successful cast, the temp var is filled with
|
|
// the function's value, on a failed cast, the var
|
|
// is filled with a default value (DefaultValueNode,
|
|
// below)... this is the node for the success case:
|
|
UK2Node_AssignmentStatement* AssignTempVar = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
|
|
AssignTempVar->AllocateDefaultPins();
|
|
|
|
// assign the output from the pure function node to
|
|
// the TempVar (either way, this message node is
|
|
// returning the TempVar's value, so on a successful
|
|
// cast we want it to have the function's result)
|
|
UEdGraphPin* ValueInPin = AssignTempVar->GetValuePin();
|
|
Schema->TryCreateConnection(FunctionCallPin, ValueInPin);
|
|
AssignTempVar->PinConnectionListChanged(ValueInPin);
|
|
|
|
UEdGraphPin* VarInPin = AssignTempVar->GetVariablePin();
|
|
Schema->TryCreateConnection(VarOutPin, VarInPin);
|
|
AssignTempVar->PinConnectionListChanged(VarInPin);
|
|
// fold this AssignTempVar node into the cast's
|
|
// success execution chain
|
|
Schema->TryCreateConnection(AssignTempVar->GetExecPin(), LastOutCastSuccessPin);
|
|
LastOutCastSuccessPin = AssignTempVar->GetThenPin();
|
|
}
|
|
|
|
UK2Node* DefaultValueNode = NULL;
|
|
UEdGraphPin* DefaultValueThenPin = NULL;
|
|
if (CurrentPin->PinType.bIsArray)
|
|
{
|
|
UK2Node_CallArrayFunction* ClearArray = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph);
|
|
DefaultValueNode = ClearArray;
|
|
ClearArray->SetFromFunction(ArrayClearFunction);
|
|
ClearArray->AllocateDefaultPins();
|
|
|
|
UEdGraphPin* ArrayPin = ClearArray->GetTargetArrayPin();
|
|
check(ArrayPin);
|
|
Schema->TryCreateConnection(ArrayPin, VarOutPin);
|
|
ClearArray->PinConnectionListChanged(ArrayPin);
|
|
|
|
DefaultValueThenPin = ClearArray->GetThenPin();
|
|
}
|
|
else
|
|
{
|
|
UK2Node_AssignmentStatement* AssignDefaultValue = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
|
|
DefaultValueNode = AssignDefaultValue;
|
|
AssignDefaultValue->AllocateDefaultPins();
|
|
|
|
Schema->TryCreateConnection(AssignDefaultValue->GetVariablePin(), VarOutPin);
|
|
AssignDefaultValue->PinConnectionListChanged(AssignDefaultValue->GetVariablePin());
|
|
Schema->SetPinDefaultValueBasedOnType(AssignDefaultValue->GetValuePin());
|
|
|
|
DefaultValueThenPin = AssignDefaultValue->GetThenPin();
|
|
}
|
|
|
|
UEdGraphPin* DefaultValueExecPin = DefaultValueNode->GetExecPin();
|
|
check(DefaultValueExecPin);
|
|
Schema->TryCreateConnection(DefaultValueExecPin, LastOutCastFaildPin);
|
|
LastOutCastFaildPin = DefaultValueThenPin;
|
|
check(LastOutCastFaildPin);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogK2Compiler, Log, TEXT("%s"), *LOCTEXT("NoPinConnectionFound_Error", "Unable to find connection for pin! Check AllocateDefaultPins() for consistency!").ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bThenPinConnected )
|
|
{
|
|
check(LastOutCastFaildPin != nullptr);
|
|
// Failure case for the cast runs straight through to the exit
|
|
CompilerContext.CopyPinLinksToIntermediate(*ThenPin, *LastOutCastFaildPin);
|
|
|
|
check(LastOutCastSuccessPin != nullptr);
|
|
// Copy all links from the invalid cast case above to the call function node
|
|
CompilerContext.MovePinLinksToIntermediate(*ThenPin, *LastOutCastSuccessPin);
|
|
}
|
|
}
|
|
|
|
// Break all connections to the original node, so it will be pruned
|
|
BreakAllNodeLinks();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |