Files
UnrealEngine/Engine/Source/Editor/BlueprintGraph/Private/DelegateNodeHandlers.cpp
2025-05-18 13:04:45 +08:00

485 lines
17 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DelegateNodeHandlers.h"
#include "BPTerminal.h"
#include "Containers/Array.h"
#include "Containers/IndirectArray.h"
#include "Containers/UnrealString.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphUtilities.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "Engine/MemberReference.h"
#include "HAL/PlatformCrt.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/Text.h"
#include "K2Node_BaseMCDelegate.h"
#include "K2Node_CallDelegate.h"
#include "K2Node_ClearDelegate.h"
#include "K2Node_CreateDelegate.h"
#include "Kismet2/CompilerResultsLog.h"
#include "KismetCompiledFunctionContext.h"
#include "KismetCompiler.h"
#include "Templates/Casts.h"
#include "Templates/Tuple.h"
#include "UObject/Class.h"
#include "UObject/NameTypes.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/UnrealNames.h"
#include "UObject/UnrealType.h"
class UK2Node;
#define LOCTEXT_NAMESPACE "DelegateNodeHandlers"
struct FKCHandlerDelegateHelper
{
static void CheckOutputsParametersInDelegateSignature(const UFunction* SignatureFunc, const UK2Node * DelegateNode, FCompilerResultsLog& MessageLog)
{
FKismetCompilerUtilities::DetectValuesReturnedByRef(SignatureFunc, DelegateNode, MessageLog);
}
static FMulticastDelegateProperty* FindAndCheckDelegateProperty(FKismetFunctionContext& Context, UK2Node_BaseMCDelegate * DelegateNode, FCompilerResultsLog& MessageLog, const UEdGraphSchema_K2* Schema)
{
check(NULL != DelegateNode);
UEdGraphPin* Pin = Schema->FindSelfPin(*DelegateNode, EEdGraphPinDirection::EGPD_Input);
UStruct* DelegateScope = Pin ? Context.GetScopeFromPinType(Pin->PinType, Context.NewClass) : NULL;
// Early out if we have no pin. That means the delegate is no longer valid, and we need to terminate gracefully
if (!Pin || !DelegateScope)
{
MessageLog.Error(*LOCTEXT("NoDelegateProperty", "Event Dispatcher has no property @@").ToString(), DelegateNode);
return NULL;
}
// Don't use DelegateNode->GetProperty(), because we don't want any property from skeletal class
UClass* PropertyOwnerClass = CastChecked<UClass>(DelegateScope);
FMulticastDelegateProperty* BoundProperty = NULL;
for (TFieldIterator<FMulticastDelegateProperty> It(PropertyOwnerClass); It; ++It)
{
FMulticastDelegateProperty* Prop = *It;
if (DelegateNode->GetPropertyName() == Prop->GetFName())
{
BoundProperty = Prop;
break;
}
}
if (!BoundProperty)
{
if (!FKismetCompilerUtilities::IsMissingMemberPotentiallyLoading(Context.Blueprint, DelegateNode->DelegateReference.GetMemberParentClass()))
{
FString const OwnerName = PropertyOwnerClass->GetName();
FString const PropName = DelegateNode->GetPropertyName().ToString();
MessageLog.Error(*FText::Format(LOCTEXT("DelegateNotFoundFmt", "Could not find an event-dispatcher named \"{0}\" in '{1}'.\nMake sure '{2}' has been compiled for @@"), FText::FromString(PropName), FText::FromString(OwnerName), FText::FromString(OwnerName)).ToString(), DelegateNode);
}
return NULL;
}
{
// MulticastDelegateProperty from NewClass may have empty signature, but property from skeletal class should have it.
const UFunction* OrgSignature = DelegateNode->GetDelegateSignature();
if(const UEdGraphPin* DelegatePin = DelegateNode->GetDelegatePin())
{
const UFunction* PinSignature = FMemberReference::ResolveSimpleMemberReference<UFunction>(DelegatePin->PinType.PinSubCategoryMemberReference);
if (!OrgSignature || !PinSignature || !OrgSignature->IsSignatureCompatibleWith(PinSignature))
{
MessageLog.Error(*LOCTEXT("WrongDelegate", "Wrong Event Dispatcher. Refresh node @@").ToString(), DelegateNode);
return NULL;
}
}
CheckOutputsParametersInDelegateSignature(OrgSignature, DelegateNode, MessageLog);
}
return BoundProperty;
}
static FBPTerminal* CreateInnerTerm(FKismetFunctionContext& Context, UEdGraphPin* SelfPin, UEdGraphPin* NetPin, FMulticastDelegateProperty* BoundProperty, UK2Node_BaseMCDelegate * DelegateNode, FCompilerResultsLog& MessageLog)
{
check(SelfPin && NetPin && BoundProperty && DelegateNode);
FBPTerminal* Term = new FBPTerminal();
Context.VariableReferences.Add(Term);
Term->CopyFromPin(SelfPin, BoundProperty->GetName());
Term->AssociatedVarProperty = BoundProperty;
FBPTerminal** pContextTerm = Context.NetMap.Find(NetPin);
if ((NULL == pContextTerm) && (SelfPin == NetPin))
{
Context.NetMap.Add(SelfPin, Term);
pContextTerm = Context.NetMap.Find(NetPin);
}
if (pContextTerm)
{
if (Term != *pContextTerm)
{
Term->Context = *pContextTerm;
}
}
else
{
MessageLog.Error(*FString(*LOCTEXT("FindDynamicallyBoundDelegate_Error", "Couldn't find target for dynamically bound delegate node @@").ToString()), DelegateNode);
}
return Term;
}
static void RegisterMultipleSelfAndMCDelegateProperty(FKismetFunctionContext& Context, UK2Node_BaseMCDelegate * DelegateNode, FCompilerResultsLog& MessageLog,
const UEdGraphSchema_K2* Schema, FDelegateOwnerId::FInnerTermMap& InnerTermMap)
{
if (DelegateNode && Schema)
{
FMulticastDelegateProperty* BoundProperty = FindAndCheckDelegateProperty(Context, DelegateNode, MessageLog, Schema);
if (BoundProperty)
{
UEdGraphPin* SelfPin = Schema->FindSelfPin(*DelegateNode, EEdGraphPinDirection::EGPD_Input);
check(SelfPin);
if (0 == SelfPin->LinkedTo.Num())
{
FBPTerminal* Term = CreateInnerTerm(Context, SelfPin, FEdGraphUtilities::GetNetFromPin(SelfPin), BoundProperty, DelegateNode, MessageLog);
Context.NetMap.Add(SelfPin, Term);
InnerTermMap.Add(FDelegateOwnerId(SelfPin, DelegateNode), Term);
return;
}
for (int32 LinkIndex = 0; LinkIndex < SelfPin->LinkedTo.Num(); ++LinkIndex)
{
UEdGraphPin* NetPin = SelfPin->LinkedTo[LinkIndex];
FBPTerminal* Term = CreateInnerTerm(Context, SelfPin, NetPin, BoundProperty, DelegateNode, MessageLog);
InnerTermMap.Add(FDelegateOwnerId(NetPin, DelegateNode), Term);
}
}
}
}
};
void FKCHandler_AddRemoveDelegate::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_BaseMCDelegate * DelegateNode = CastChecked<UK2Node_BaseMCDelegate>(Node);
FKCHandlerDelegateHelper::RegisterMultipleSelfAndMCDelegateProperty(Context, DelegateNode, CompilerContext.MessageLog, CompilerContext.GetSchema(), InnerTermMap);
UEdGraphPin* Pin = DelegateNode->GetDelegatePin();
check(NULL != Pin);
if(0 == Pin->LinkedTo.Num())
{
CompilerContext.MessageLog.Error(*FString(*LOCTEXT("AddRemoveDelegate_NoDelegateInput", "Event Dispatcher pin is not connected @@").ToString()), DelegateNode);
}
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(Pin);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
FBPTerminal* Term = FoundTerm ? *FoundTerm : NULL;
if(NULL == Term)
{
Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));
Context.NetMap.Add(Net, Term);
}
}
void FKCHandler_AddRemoveDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_BaseMCDelegate* DelegateNode = CastChecked<UK2Node_BaseMCDelegate>(Node);
UEdGraphPin* DelegatePin = DelegateNode->GetDelegatePin();
check(NULL != DelegatePin);
FBPTerminal** DelegateInputTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(DelegatePin));
check(DelegateInputTerm && *DelegateInputTerm);
UEdGraphPin* SelfPin = CompilerContext.GetSchema()->FindSelfPin(*DelegateNode, EEdGraphPinDirection::EGPD_Input);
check(SelfPin);
TArray<UEdGraphPin*> Links = SelfPin->LinkedTo;
if(!Links.Num())
{
Links.Add(SelfPin);
}
for (auto NetIt = Links.CreateIterator(); NetIt; ++NetIt)
{
UEdGraphPin* NetPin = *NetIt;
check(NetPin);
FBlueprintCompiledStatement& AddStatement = Context.AppendStatementForNode(DelegateNode);
AddStatement.Type = Command;
FBPTerminal** VarDelegate = InnerTermMap.Find(FDelegateOwnerId(NetPin, DelegateNode));
check(VarDelegate && *VarDelegate);
AddStatement.LHS = *VarDelegate;
AddStatement.RHS.Add(*DelegateInputTerm);
}
GenerateSimpleThenGoto(Context, *DelegateNode, DelegateNode->FindPin(UEdGraphSchema_K2::PN_Then));
FNodeHandlingFunctor::Compile(Context, DelegateNode);
}
void FKCHandler_CreateDelegate::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_CreateDelegate * DelegateNode = CastChecked<UK2Node_CreateDelegate>(Node);
check(NULL != DelegateNode);
const FName DelegateFunctionName = DelegateNode->GetFunctionName();
if(DelegateFunctionName == NAME_None)
{
CompilerContext.MessageLog.Error(*LOCTEXT("NoDelegateFunctionName", "@@ : missing a function/event name.").ToString(), DelegateNode);
return;
}
if(!DelegateNode->GetDelegateSignature())
{
const FString ErrorStr = LOCTEXT("NoDelegateFunction", "@@ : unable to determine expected signature - is the delegate pin connected?").ToString();
CompilerContext.MessageLog.Error(*ErrorStr, DelegateNode);
return;
}
{
UEdGraphPin* InputPin = DelegateNode->GetObjectInPin();
check(NULL != InputPin);
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
FBPTerminal* InputObjTerm = FoundTerm ? *FoundTerm : NULL;
if(NULL == InputObjTerm)
{
if (InputPin->LinkedTo.Num() == 0)
{
InputObjTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
InputObjTerm->Name = Context.NetNameMap->MakeValidName(Net);
InputObjTerm->Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;
}
else
{
InputObjTerm = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));
}
Context.NetMap.Add(Net, InputObjTerm);
}
}
{
UEdGraphPin* OutPin = DelegateNode->GetDelegateOutPin();
check(NULL != OutPin);
if(!OutPin->LinkedTo.Num())
{
CompilerContext.MessageLog.Error(*LOCTEXT("NoDelegateSignature", "No delegate signature @@").ToString(), DelegateNode);
return;
}
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(OutPin);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
FBPTerminal* OutDelegateTerm = FoundTerm ? *FoundTerm : NULL;
if(NULL == OutDelegateTerm)
{
OutDelegateTerm = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));
if (NULL == FMemberReference::ResolveSimpleMemberReference<UFunction>(OutDelegateTerm->Type.PinSubCategoryMemberReference))
{
FMemberReference::FillSimpleMemberReference<UFunction>(DelegateNode->GetDelegateSignature(), OutDelegateTerm->Type.PinSubCategoryMemberReference);
}
if (NULL == FMemberReference::ResolveSimpleMemberReference<UFunction>(OutDelegateTerm->Type.PinSubCategoryMemberReference))
{
CompilerContext.MessageLog.Error(*LOCTEXT("UnconnectedDelegateSig", "Event Dispatcher has no signature @@").ToString(), OutPin);
return;
}
Context.NetMap.Add(Net, OutDelegateTerm);
}
}
}
void FKCHandler_CreateDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_CreateDelegate * DelegateNode = CastChecked<UK2Node_CreateDelegate>(Node);
check(NULL != DelegateNode);
FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
Statement.Type = KCST_BindDelegate;
{
UEdGraphPin* OutPin = DelegateNode->GetDelegateOutPin();
check(NULL != OutPin);
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(OutPin);
check(NULL != Net);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
check(FoundTerm && *FoundTerm);
Statement.LHS = *FoundTerm;
}
{
FBPTerminal* DelegateNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
DelegateNameTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Name;
DelegateNameTerm->Name = DelegateNode->GetFunctionName().ToString();
DelegateNameTerm->bIsLiteral = true;
Statement.RHS.Add(DelegateNameTerm);
}
{
UEdGraphPin* InputPin = DelegateNode->GetObjectInPin();
check(NULL != InputPin);
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
check(FoundTerm && *FoundTerm);
Statement.RHS.Add(*FoundTerm);
}
FNodeHandlingFunctor::Compile(Context, Node);
}
void FKCHandler_ClearDelegate::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_ClearDelegate * DelegateNode = CastChecked<UK2Node_ClearDelegate>(Node);
FKCHandlerDelegateHelper::RegisterMultipleSelfAndMCDelegateProperty(Context, DelegateNode, CompilerContext.MessageLog, CompilerContext.GetSchema(), InnerTermMap);
}
void FKCHandler_ClearDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_BaseMCDelegate* DelegateNode = CastChecked<UK2Node_BaseMCDelegate>(Node);
UEdGraphPin* SelfPin = CompilerContext.GetSchema()->FindSelfPin(*DelegateNode, EEdGraphPinDirection::EGPD_Input);
check(SelfPin);
TArray<UEdGraphPin*> Links = SelfPin->LinkedTo;
if(!Links.Num())
{
Links.Add(SelfPin);
}
for (auto NetIt = Links.CreateIterator(); NetIt; ++NetIt)
{
UEdGraphPin* NetPin = *NetIt;
check(NetPin);
FBlueprintCompiledStatement& AddStatement = Context.AppendStatementForNode(DelegateNode);
AddStatement.Type = KCST_ClearMulticastDelegate;
FBPTerminal** VarDelegate = InnerTermMap.Find(FDelegateOwnerId(NetPin, DelegateNode));
check(VarDelegate && *VarDelegate);
AddStatement.LHS = *VarDelegate;
}
GenerateSimpleThenGoto(Context, *DelegateNode, DelegateNode->FindPin(UEdGraphSchema_K2::PN_Then));
FNodeHandlingFunctor::Compile(Context, DelegateNode);
}
//////////////////////////////////////////////////////////////////////////
// FKCHandler_CallDelegate
void FKCHandler_CallDelegate::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
UK2Node_CallDelegate * DelegateNode = CastChecked<UK2Node_CallDelegate>(Node);
FKCHandlerDelegateHelper::RegisterMultipleSelfAndMCDelegateProperty(Context, DelegateNode, CompilerContext.MessageLog, CompilerContext.GetSchema(), InnerTermMap);
FKCHandler_CallFunction::RegisterNets(Context, Node);
}
void FKCHandler_CallDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
check(Node);
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
const UFunction* SignatureFunction = FindFunction(Context, Node);
if(!SignatureFunction)
{
UK2Node_CallDelegate* DelegateNode = CastChecked<UK2Node_CallDelegate>(Node);
if (!FKismetCompilerUtilities::IsMissingMemberPotentiallyLoading(Context.Blueprint, DelegateNode->DelegateReference.GetMemberParentClass()))
{
CompilerContext.MessageLog.Error(*LOCTEXT("CallDelegateNoSignature_Error", "Cannot find signature function for @@").ToString(), Node);
}
return;
}
if(SignatureFunction->HasMetaData(FBlueprintMetadata::MD_DefaultToSelf))
{
CompilerContext.MessageLog.Error(
*FText::Format(
LOCTEXT("CallDelegateWrongMeta_ErrorFmt", "Signature function should not have {0} metadata. @@"),
FText::FromString(FBlueprintMetadata::MD_DefaultToSelf.ToString())
).ToString(),
Node
);
return;
}
if(SignatureFunction->HasMetaData(FBlueprintMetadata::MD_WorldContext))
{
CompilerContext.MessageLog.Error(
*FText::Format(
LOCTEXT("CallDelegateWrongMeta_ErrorFmt", "Signature function should not have {0} metadata. @@"),
FText::FromString(FBlueprintMetadata::MD_WorldContext.ToString())
).ToString(),
Node
);
return;
}
if(SignatureFunction->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm))
{
CompilerContext.MessageLog.Error(
*FText::Format(
LOCTEXT("CallDelegateWrongMeta_ErrorFmt", "Signature function should not have {0} metadata. @@"),
FText::FromString(FBlueprintMetadata::MD_AutoCreateRefTerm.ToString())
).ToString(),
Node
);
return;
}
FKCHandler_CallFunction::Compile(Context, Node);
}
UFunction* FKCHandler_CallDelegate::FindFunction(FKismetFunctionContext& Context, UEdGraphNode* Node)
{
const UK2Node_CallDelegate* DelegateNode = CastChecked<UK2Node_CallDelegate>(Node);
const UClass* TestClass = Context.NewClass;
check(TestClass);
const bool bIsSkeletonClass = TestClass->HasAnyFlags(RF_Transient) && TestClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint);
return DelegateNode->GetDelegateSignature(!bIsSkeletonClass);
}
void FKCHandler_CallDelegate::AdditionalCompiledStatementHandling(FKismetFunctionContext& Context, UEdGraphNode* Node, FBlueprintCompiledStatement& Statement)
{
if(NULL == Statement.FunctionContext)
{
CompilerContext.MessageLog.Error(*LOCTEXT("CallDelegateNoContext_Error", "Call delegate has no context. @@").ToString(), Node);
return;
}
check(NULL != Statement.FunctionToCall);
if (UClass* FunctionOwner = Statement.FunctionToCall->GetOwnerClass())
{
if (FunctionOwner != FunctionOwner->GetAuthoritativeClass())
{
CompilerContext.MessageLog.Warning(*LOCTEXT("CallDelegateWrongOwner", "Signature on delegate doesn't belong to authoritative class. @@").ToString(), Node);
}
}
const UK2Node_BaseMCDelegate* DelegateNode = CastChecked<UK2Node_BaseMCDelegate>(Node);
// Statement.FunctionContext is the term of DelegateOwner. It could be related with many keys (Pins) in Context.NetMap.
// We want to find the exact pin, that is connected with the DelegateNode
FBPTerminal** VarDelegate = NULL;
for (auto Pair : Context.NetMap)
{
if (Pair.Value == Statement.FunctionContext)
{
check(Pair.Key);
VarDelegate = InnerTermMap.Find(FDelegateOwnerId(Pair.Key, DelegateNode));
if (VarDelegate && *VarDelegate)
{
break;
}
}
}
check(VarDelegate && *VarDelegate);
Statement.FunctionContext = *VarDelegate;
Statement.Type = KCST_CallDelegate;
}
#undef LOCTEXT_NAMESPACE