Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_AssignmentStatement.cpp
dave jones2 62362d4a85 UE-141320 - Float to double conversion warning in existing blueprint function
Macro assignment nodes don't work like other BP nodes: only their nets refer to meaningful data. As a result, its RegisterNets function needs to insert a custom entry into the implicit cast table.

However, when the compiler calls RegisterImplicitCasts to detect implicit cast points in the graph, it can mistakenly identify that a cast is needed for its value pin. This is incorrect since a cast would only be needed between the *nets* of the value and variable pins. The fix here is to have the node explicitly remove the erroneous entry from the map, which we do in other nodes that have special semantics.

Additionally, changed the math expression handler to use RemoveRegisteredImplicitCast, which is just a simple wrapper around ImplicitCastMap.Remove that returns the number of entries removed. This can be useful for debugging when trying to determine how many entries were removed from the cast table.

#rb dave.jones
#jira UE-141320
#preflight 61fd965ff370b0d3111f7155
#lockdown julien.marchand

#ROBOMERGE-AUTHOR: dave.jones2
#ROBOMERGE-SOURCE: CL 18875868 in //UE5/Release-5.0/... via CL 18876118 via CL 18876340
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v910-18824042)

[CL 18876386 by dave jones2 in ue5-main branch]
2022-02-04 18:14:49 -05:00

260 lines
8.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_AssignmentStatement.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphUtilities.h"
#include "KismetCastingUtils.h"
#include "KismetCompiler.h"
#include "VariableSetHandler.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionDatabaseRegistrar.h"
#define LOCTEXT_NAMESPACE "K2Node_AssignmentStatement"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_AssignmentStatement
class FKCHandler_AssignmentStatement : public FKCHandler_VariableSet
{
public:
FKCHandler_AssignmentStatement(FKismetCompilerContext& InCompilerContext)
: FKCHandler_VariableSet(InCompilerContext)
{
}
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
using namespace UE::KismetCompiler;
UEdGraphPin* VariablePin = Node->FindPin(TEXT("Variable"));
UEdGraphPin* ValuePin = Node->FindPin(TEXT("Value"));
if ((VariablePin == NULL) || (ValuePin == NULL))
{
CompilerContext.MessageLog.Error(*LOCTEXT("MissingPins_Error", "Missing pin(s) on @@; expected a pin named Variable and a pin named Value").ToString(), Node);
return;
}
if (VariablePin->LinkedTo.Num() == 0)
{
CompilerContext.MessageLog.Error(*LOCTEXT("NoVariableConnected_Error", "A variable needs to be connected to @@").ToString(), VariablePin);
return;
}
ValidateAndRegisterNetIfLiteral(Context, ValuePin);
UEdGraphPin* VariablePinNet = FEdGraphUtilities::GetNetFromPin(VariablePin);
UEdGraphPin* ValuePinNet = FEdGraphUtilities::GetNetFromPin(ValuePin);
if (VariablePinNet && ValuePinNet)
{
TOptional<CastingUtils::StatementNamePair> ConversionType =
CastingUtils::GetFloatingPointConversionType(*ValuePinNet, *VariablePinNet);
if (ConversionType)
{
FString DescriptiveName = Node->GetName();
FString TerminalName = FString::Printf(TEXT("%s_%s_%s"),
*DescriptiveName,
*VariablePinNet->PinName.ToString(),
ConversionType->Get<1>());
FBPTerminal* NewTerm = Context.CreateLocalTerminal();
NewTerm->Name = TerminalName;
NewTerm->Type = VariablePinNet->PinType;
NewTerm->Source = Node;
EKismetCompiledStatementType CastType = ConversionType->Get<0>();
Context.ImplicitCastMap.Add(VariablePin, FImplicitCastParams{CastType, NewTerm, Node});
}
}
else
{
CompilerContext.MessageLog.Error(*LOCTEXT("NoVariableOrValueNets_Error", "Expected Variable and Value pins to have valid connections in @@").ToString(), Node);
}
}
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
using namespace UE::KismetCompiler;
UEdGraphPin* VariablePin = Node->FindPin(TEXT("Variable"));
UEdGraphPin* ValuePin = Node->FindPin(TEXT("Value"));
check(VariablePin);
check(VariablePin->LinkedTo.Num() == 1);
check(ValuePin);
InnerAssignment(Context, Node, VariablePin, ValuePin);
// Generate the output impulse from this node
GenerateSimpleThenGoto(Context, *Node);
// The assignment node is very much a non-standard node: its pins don't directly reference any data, but their nets do.
// The only cast that could happen is from the value pin's net to the variable's pin net, which we handle in RegisterNets.
// Due to how RegisterImplicitCasts works, it can actually register an erroneous entry to the value pin,
// which we need to remove ourselves.
CastingUtils::RemoveRegisteredImplicitCast(Context, ValuePin);
}
protected:
virtual bool UsesVariablePinAsKey() const override { return true; }
};
FName UK2Node_AssignmentStatement::VariablePinName(TEXT("Variable"));
FName UK2Node_AssignmentStatement::ValuePinName(TEXT("Value"));
UK2Node_AssignmentStatement::UK2Node_AssignmentStatement(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UK2Node_AssignmentStatement::AllocateDefaultPins()
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
UEdGraphPin* VariablePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, VariablePinName);
UEdGraphPin* ValuePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, ValuePinName);
Super::AllocateDefaultPins();
}
FText UK2Node_AssignmentStatement::GetTooltipText() const
{
return LOCTEXT("AssignmentStatementTooltip", "Assigns Value to Variable");
}
FText UK2Node_AssignmentStatement::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("Assign", "Assign");
}
bool UK2Node_AssignmentStatement::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
bool bIsCompatible = Super::IsCompatibleWithGraph(TargetGraph);
if (bIsCompatible)
{
const EGraphType GraphType = TargetGraph->GetSchema()->GetGraphType(TargetGraph);
bIsCompatible = (GraphType == GT_Macro);
}
return bIsCompatible;
}
bool UK2Node_AssignmentStatement::CanPasteHere(const UEdGraph* TargetGraph) const
{
// These nodes can be pasted anywhere that UK2Node's are compatible with the graph
// Avoiding the call to IsCompatibleWithGraph because these nodes should normally only
// be placed in Macros, but it's nice to be able to paste Macro functionality anywhere.
return Super::IsCompatibleWithGraph(TargetGraph);
}
void UK2Node_AssignmentStatement::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
Super::NotifyPinConnectionListChanged(Pin);
UEdGraphPin* VariablePin = FindPin(TEXT("Variable"));
UEdGraphPin* ValuePin = FindPin(TEXT("Value"));
if ((VariablePin->LinkedTo.Num() == 0) && (ValuePin->LinkedTo.Num() == 0))
{
// Restore the wildcard status
VariablePin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
VariablePin->PinType.PinSubCategory = TEXT("");
VariablePin->PinType.PinSubCategoryObject = NULL;
ValuePin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
ValuePin->PinType.PinSubCategory = TEXT("");
ValuePin->PinType.PinSubCategoryObject = NULL;
}
else if (Pin->LinkedTo.Num() > 0 &&
( Pin->LinkedTo[0]->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard ||
Pin->LinkedTo[0]->PinType.PinCategory == Pin->PinType.PinCategory) )
{
Pin->PinType = Pin->LinkedTo[0]->PinType;
// Enforce the type on the other pin
if (VariablePin == Pin)
{
ValuePin->PinType = VariablePin->PinType;
UEdGraphSchema_K2::ValidateExistingConnections(ValuePin);
}
else
{
VariablePin->PinType = ValuePin->PinType;
UEdGraphSchema_K2::ValidateExistingConnections(VariablePin);
}
}
}
void UK2Node_AssignmentStatement::PostReconstructNode()
{
UEdGraphPin* VariablePin = FindPin(TEXT("Variable"));
UEdGraphPin* ValuePin = FindPin(TEXT("Value"));
PinConnectionListChanged(VariablePin);
PinConnectionListChanged(ValuePin);
Super::PostReconstructNode();
}
UEdGraphPin* UK2Node_AssignmentStatement::GetThenPin() const
{
UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Then);
check(Pin != NULL);
return Pin;
}
UEdGraphPin* UK2Node_AssignmentStatement::GetVariablePin() const
{
UEdGraphPin* Pin = FindPin(VariablePinName);
check(Pin != NULL);
return Pin;
}
UEdGraphPin* UK2Node_AssignmentStatement::GetValuePin() const
{
UEdGraphPin* Pin = FindPin(ValuePinName);
check(Pin != NULL);
return Pin;
}
FNodeHandlingFunctor* UK2Node_AssignmentStatement::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_AssignmentStatement(CompilerContext);
}
void UK2Node_AssignmentStatement::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);
}
}
FText UK2Node_AssignmentStatement::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Macro);
}
#undef LOCTEXT_NAMESPACE