当前位置: 首页 > news >正文

清远做网站的/sem营销推广

清远做网站的,sem营销推广,wordpress ajax 刷新,网站上怎么做返回主页链接系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 UE蓝图 返回结果(FunctionResult)节点和源码 UE蓝图 函数调用(CallFunction)节点和源码 文章目录 系列文章目录一、Call…

系列文章目录

UE蓝图 Get节点和源码
UE蓝图 Set节点和源码
UE蓝图 Cast节点和源码
UE蓝图 分支(Branch)节点和源码
UE蓝图 入口(FunctionEntry)节点和源码
UE蓝图 返回结果(FunctionResult)节点和源码
UE蓝图 函数调用(CallFunction)节点和源码


文章目录

  • 系列文章目录
  • 一、CallFunction节点功能
  • 二、CallFunction节点用法
  • 三、CallFunction使用场景
      • 1. **事件处理**
      • 2. **条件逻辑**
      • 3. **延迟操作**
      • 4. **数据处理**
      • 5. **AI和角色行为**
      • 6. **用户输入处理**
      • 7. **系统交互**
      • 8. **自定义逻辑**
  • 四、实现原理
  • 五、相关源码


一、CallFunction节点功能

在这里插入图片描述

CallFunction节点用于调用指定的函数或方法。这些函数可以是引擎自带的,也可以是用户自定义的。通过CallFunction节点,你可以在蓝图中实现复杂的逻辑和交互。

二、CallFunction节点用法

在Unreal Engine(UE)的蓝图中,CallFunction节点用于在运行时调用一个特定的函数。这个节点通常用于执行那些已经定义好但需要在特定条件下触发的函数。以下是CallFunction节点的基本用法:

  1. 添加CallFunction节点:首先,你需要在你的蓝图中从“右键菜单” -> 在弹出框中输入函数的名字进行检索->选择你要使用的函数,添加一个CallFunction节点。

  2. 传递参数:如果所调用的函数需要参数,你需要将这些参数通过连线的方式传递给CallFunction节点的相应输入引脚。参数的类型和顺序必须与函数定义中的一致。

  3. 执行函数:当蓝图中的逻辑流程到达CallFunction节点时,它将执行所选择的函数。这意味着任何与该函数相关联的逻辑或行为都将被执行。

  4. 处理返回值:如果调用的函数有返回值,你可以在CallFunction节点的“Return Value”引脚处获取这个值。然后,你可以将这个返回值用于蓝图中的其他逻辑或传递给其他节点。

  5. 连接其他节点:你可以将CallFunction节点与其他节点连接起来,以创建更复杂的逻辑流程。例如,你可以使用Sequence节点来确保一系列操作按顺序执行,或者使用Parallel节点来同时执行多个操作。

  6. 编译和测试:完成蓝图编辑后,确保编译你的项目,并在游戏中测试CallFunction节点的行为,以确保它按照预期工作。

三、CallFunction使用场景

UE蓝图中的CallFunction节点有多个使用场景,以下是一些常见的例子:

1. 事件处理

在UE中,事件是蓝图系统的重要组成部分。当某个事件发生时(例如,用户点击按钮、角色受到伤害等),你可以使用CallFunction节点来调用处理该事件的函数。

2. 条件逻辑

根据游戏状态或条件的不同,你可能需要调用不同的函数。CallFunction节点可以与SequenceParallelBranch等节点结合使用,实现复杂的条件逻辑。

3. 延迟操作

使用CallFunction节点与Delay节点结合,可以实现延迟执行某些函数。例如,你可能希望在角色死亡后等待一段时间再触发某些行为。

4. 数据处理

当你需要对数据进行处理或转换时,可以创建自定义函数来处理这些数据,并通过CallFunction节点来调用这些函数。

5. AI和角色行为

在创建AI或角色行为时,你可能需要根据角色的状态或环境来调用不同的函数。CallFunction节点是实现这一目标的重要工具。

6. 用户输入处理

处理用户输入(如键盘、鼠标或手柄输入)时,你可以使用CallFunction节点来调用处理这些输入的函数。

7. 系统交互

与游戏系统(如音频、物理、渲染等)进行交互时,你可能需要调用特定的函数来执行某些操作。CallFunction节点提供了一种方便的方式来调用这些函数。

8. 自定义逻辑

除了上述常见场景外,CallFunction节点还可以用于任何需要调用自定义函数的场景。你可以创建自己的函数来实现特定的逻辑或行为,并通过CallFunction节点来调用它们。

四、实现原理

  • 创建输入引脚
    解析函数的注解获取函数的元数据(输入输出参数和是否执行节点等)创建引脚
    函数定义示例如下
	/** Modulo (A % B) */UFUNCTION(BlueprintPure, meta=(DisplayName = "% (integer)", CompactNodeTitle = "%", Keywords = "% modulus"), Category="Math|Integer")static int32 Percent_IntInt(int32 A, int32 B = 1);/** Addition (A + B) */UFUNCTION(BlueprintPure, meta=(DisplayName = "int + int", CompactNodeTitle = "+", Keywords = "+ add plus", CommutativeAssociativeBinaryOperator = "true"), Category="Math|Integer")static int32 Add_IntInt(int32 A, int32 B = 1);
  • 调用FKCHandler_CallFunction.RegisterNets注册函数引脚
		for (UEdGraphPin* Pin : Node->Pins){const bool bIsConnected = (Pin->LinkedTo.Num() != 0);// if this pin could use a default (it doesn't have a connection or default of its own)if (!bIsConnected && (Pin->DefaultObject == nullptr)){if (DefaultToSelfParamNames.Contains(Pin->PinName) && FKismetCompilerUtilities::ValidateSelfCompatibility(Pin, Context)){ensure(Pin->PinType.PinSubCategoryObject != nullptr);ensure((Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object) || (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface));FBPTerminal* Term = Context.RegisterLiteral(Pin);Term->Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;Context.NetMap.Add(Pin, Term);}else if (RequiresSetValue.Contains(Pin->PinName)){CompilerContext.MessageLog.Error(*NSLOCTEXT("KismetCompiler", "PinMustHaveConnection_Error", "Pin @@ must have a connection").ToString(), Pin);}}}
  • 调用Compile编译创建Statement
FBlueprintCompiledStatement* LatentStatement = nullptr;
//遍历需要调用该函数的所有上下文,并为每个上下文发出一个调用函数语句
for (FBPTerminal* Target : ContextTerms)
{FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);Statement.FunctionToCall = Function;Statement.FunctionContext = Target;Statement.Type = KCST_CallFunction;Statement.bIsInterfaceContext = IsCalledFunctionFromInterface(Node);Statement.bIsParentContext = IsCalledFunctionFinal(Node);Statement.LHS = LHSTerm;Statement.RHS = RHSTerms;if (!bIsLatent){// Fixup ubergraph callsif (pSrcEventNode){UEdGraphPin* ExecOut = CompilerContext.GetSchema()->FindExecutionPin(**pSrcEventNode, EGPD_Output);check(CompilerContext.UbergraphContext);CompilerContext.UbergraphContext->GotoFixupRequestMap.Add(&Statement, ExecOut);Statement.UbergraphCallIndex = 0;}}else{// Fixup latent functionsif (LatentTargetNode && (Target == ContextTerms.Last())){check(LatentTargetParamIndex != INDEX_NONE);Statement.UbergraphCallIndex = LatentTargetParamIndex;Context.GotoFixupRequestMap.Add(&Statement, ThenExecPin);LatentStatement = &Statement;}}AdditionalCompiledStatementHandling(Context, Node, Statement);if(Statement.Type == KCST_CallFunction && Function->HasAnyFunctionFlags(FUNC_Delegate)){CompilerContext.MessageLog.Error(*LOCTEXT("CallingDelegate_Error", "@@ is trying to call a delegate function - delegates cannot be called directly").ToString(), Node);// Sanitize the statement, this would have ideally been detected earlier but we need// to run AdditionalCompiledStatementHandling to satisify the DelegateNodeHandler// implementation:Statement.Type = KCST_CallDelegate;}
}

五、相关源码

源码文件:
CallFunctionHandler.h
CallFunctionHandler.cpp
K2Node_CallFunction.h
K2Node_CallFunction.cpp
相关类:
FKCHandler_CallFunction
K2Node_CallFunction


/********************************************************************************  UK2Node_CallFunction******************************************************************************/UK2Node_CallFunction::UK2Node_CallFunction(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer), bPinTooltipsValid(false)
{OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll;
}bool UK2Node_CallFunction::HasDeprecatedReference() const
{UFunction* Function = GetTargetFunction();return (Function && Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction));
}FEdGraphNodeDeprecationResponse UK2Node_CallFunction::GetDeprecationResponse(EEdGraphNodeDeprecationType DeprecationType) const
{FEdGraphNodeDeprecationResponse Response = Super::GetDeprecationResponse(DeprecationType);if (DeprecationType == EEdGraphNodeDeprecationType::NodeHasDeprecatedReference){// TEMP: Do not warn in the case of SpawnActor, as we have a special upgrade path for those nodesif (FunctionReference.GetMemberName() == FName(TEXT("BeginSpawningActorFromBlueprint"))){Response.MessageType = EEdGraphNodeDeprecationMessageType::None;}else{UFunction* Function = GetTargetFunction();if (ensureMsgf(Function != nullptr, TEXT("This node should not be able to report having a deprecated reference if the target function cannot be resolved."))){FString DetailedMessage = Function->GetMetaData(FBlueprintMetadata::MD_DeprecationMessage);Response.MessageText = FBlueprintEditorUtils::GetDeprecatedMemberUsageNodeWarning(GetUserFacingFunctionName(Function), FText::FromString(DetailedMessage));}}}return Response;
}FText UK2Node_CallFunction::GetFunctionContextString() const
{FText ContextString;// Don't show 'target is' if no target pin!UEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input);if(SelfPin != NULL && !SelfPin->bHidden){const UFunction* Function = GetTargetFunction();UClass* CurrentSelfClass = (Function != NULL) ? Function->GetOwnerClass() : NULL;UClass const* TrueSelfClass = CurrentSelfClass;if (CurrentSelfClass && CurrentSelfClass->ClassGeneratedBy){TrueSelfClass = CurrentSelfClass->GetAuthoritativeClass();}const FText TargetText = FBlueprintEditorUtils::GetFriendlyClassDisplayName(TrueSelfClass);FFormatNamedArguments Args;Args.Add(TEXT("TargetName"), TargetText);ContextString = FText::Format(LOCTEXT("CallFunctionOnDifferentContext", "Target is {TargetName}"), Args);}return ContextString;
}FText UK2Node_CallFunction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{FText FunctionName;FText ContextString;FText RPCString;if (UFunction* Function = GetTargetFunction()){RPCString = UK2Node_Event::GetLocalizedNetString(Function->FunctionFlags, true);FunctionName = GetUserFacingFunctionName(Function);ContextString = GetFunctionContextString();}else{FunctionName = FText::FromName(FunctionReference.GetMemberName());if ((GEditor != NULL) && (GetDefault<UEditorStyleSettings>()->bShowFriendlyNames)){FunctionName = FText::FromString(FName::NameToDisplayString(FunctionName.ToString(), false));}}if(TitleType == ENodeTitleType::FullTitle){FFormatNamedArguments Args;Args.Add(TEXT("FunctionName"), FunctionName);Args.Add(TEXT("ContextString"), ContextString);Args.Add(TEXT("RPCString"), RPCString);if (ContextString.IsEmpty() && RPCString.IsEmpty()){return FText::Format(LOCTEXT("CallFunction_FullTitle", "{FunctionName}"), Args);}else if (ContextString.IsEmpty()){return FText::Format(LOCTEXT("CallFunction_FullTitle_WithRPCString", "{FunctionName}\n{RPCString}"), Args);}else if (RPCString.IsEmpty()){return FText::Format(LOCTEXT("CallFunction_FullTitle_WithContextString", "{FunctionName}\n{ContextString}"), Args);}else{return FText::Format(LOCTEXT("CallFunction_FullTitle_WithContextRPCString", "{FunctionName}\n{ContextString}\n{RPCString}"), Args);}}else{return FunctionName;}
}void UK2Node_CallFunction::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const
{if (!bPinTooltipsValid){for (UEdGraphPin* P : Pins){if (!P->PinToolTip.IsEmpty() && ExpandAsEnumPins.Contains(P)){continue;}P->PinToolTip.Reset();GeneratePinTooltip(*P);}bPinTooltipsValid = true;}return UK2Node::GetPinHoverText(Pin, HoverTextOut);
}void UK2Node_CallFunction::AllocateDefaultPins()
{InvalidatePinTooltips();UBlueprint* MyBlueprint = GetBlueprint();UFunction* Function = GetTargetFunction();// favor the skeleton function if possible (in case the signature has // changed, and not yet compiled).if (!FunctionReference.IsSelfContext()){UClass* FunctionClass = FunctionReference.GetMemberParentClass(MyBlueprint->GeneratedClass);if (UBlueprintGeneratedClass* BpClassOwner = Cast<UBlueprintGeneratedClass>(FunctionClass)){// this function could currently only be a part of some skeleton // class (the blueprint has not be compiled with it yet), so let's // check the skeleton class as well, see if we can pull pin data // from there...UBlueprint* FunctionBlueprint = CastChecked<UBlueprint>(BpClassOwner->ClassGeneratedBy, ECastCheckedType::NullAllowed);if (FunctionBlueprint){if (UFunction* SkelFunction = FindUField<UFunction>(FunctionBlueprint->SkeletonGeneratedClass, FunctionReference.GetMemberName())){Function = SkelFunction;}}}}// First try remap tableif (Function == NULL){UClass* ParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());if (ParentClass != NULL){if (UFunction* NewFunction = FMemberReference::FindRemappedField<UFunction>(ParentClass, FunctionReference.GetMemberName())){// Found a remapped property, update the nodeFunction = NewFunction;SetFromFunction(NewFunction);}}}if (Function == NULL){// The function no longer exists in the stored scope// Try searching inside all function libraries, in case the function got refactored into one of them.for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt){UClass* TestClass = *ClassIt;if (TestClass->IsChildOf(UBlueprintFunctionLibrary::StaticClass())){Function = FindUField<UFunction>(TestClass, FunctionReference.GetMemberName());if (Function != NULL){UClass* OldClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());Message_Note(FText::Format(LOCTEXT("FixedUpFunctionInLibraryFmt", "UK2Node_CallFunction: Fixed up function '{0}', originally in '{1}', now in library '{2}'."),FText::FromString(FunctionReference.GetMemberName().ToString()),(OldClass != NULL) ? FText::FromString(*OldClass->GetName()) : LOCTEXT("FixedUpFunctionInLibraryNull", "(null)"),FText::FromString(TestClass->GetName())).ToString());SetFromFunction(Function);break;}}}}// Now create the pins if we ended up with a valid function to callif (Function != NULL){CreatePinsForFunctionCall(Function);}FCustomStructureParamHelper::UpdateCustomStructurePins(Function, this);Super::AllocateDefaultPins();
}/** Util to find self pin in an array */
UEdGraphPin* FindSelfPin(TArray<UEdGraphPin*>& Pins)
{for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++){if(Pins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Self){return Pins[PinIdx];}}return NULL;
}void UK2Node_CallFunction::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{// BEGIN TEMP// We had a bug where the class was being messed up by copy/paste, but the self pin class was still ok. This code fixes up those cases.UFunction* Function = GetTargetFunction();if (Function == NULL){if (UEdGraphPin* SelfPin = FindSelfPin(OldPins)){if (UClass* SelfPinClass = Cast<UClass>(SelfPin->PinType.PinSubCategoryObject.Get())){if (UFunction* NewFunction = FindUField<UFunction>(SelfPinClass, FunctionReference.GetMemberName())){SetFromFunction(NewFunction);}}}}// END TEMPSuper::ReallocatePinsDuringReconstruction(OldPins);// Connect Execute and Then pins for functions, which became pure.ReconnectPureExecPins(OldPins);
}UEdGraphPin* UK2Node_CallFunction::CreateSelfPin(const UFunction* Function)
{return FBlueprintNodeStatics::CreateSelfPin(this, Function);
}void UK2Node_CallFunction::CreateExecPinsForFunctionCall(const UFunction* Function)
{bool bCreateSingleExecInputPin = true;bool bCreateThenPin = true;ExpandAsEnumPins.Reset();// If not pure, create exec pinsif (!bIsPureFunc){// If we want enum->exec expansion, and it is not disabled, do it nowif(bWantsEnumToExecExpansion){TArray<FName> EnumNames;GetExpandEnumPinNames(Function, EnumNames);FProperty* PreviousInput = nullptr;for (const FName& EnumParamName : EnumNames){FProperty* Prop = nullptr;UEnum* Enum = nullptr;if (FByteProperty* ByteProp = FindFProperty<FByteProperty>(Function, EnumParamName)){Prop = ByteProp;Enum = ByteProp->Enum;}else if (FEnumProperty* EnumProp = FindFProperty<FEnumProperty>(Function, EnumParamName)){Prop = EnumProp;Enum = EnumProp->GetEnum();}else if (FBoolProperty* BoolProp = FindFProperty<FBoolProperty>(Function, EnumParamName)){Prop = BoolProp;}if (Prop != nullptr){const bool bIsFunctionInput = !Prop->HasAnyPropertyFlags(CPF_ReturnParm) &&(!Prop->HasAnyPropertyFlags(CPF_OutParm) ||Prop->HasAnyPropertyFlags(CPF_ReferenceParm));const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;if (bIsFunctionInput){if (PreviousInput){bHasCompilerMessage = true;ErrorType = EMessageSeverity::Error;ErrorMsg = FString::Printf(TEXT("Parameter '%s' is listed as an ExpandEnumAsExecs input, but %s already was one. Only one is permitted."), *EnumParamName.ToString(), *PreviousInput->GetName());break;}PreviousInput = Prop;}if (Enum){// yay, found it! Now create exec pin for eachint32 NumExecs = (Enum->NumEnums() - 1);for (int32 ExecIdx = 0; ExecIdx < NumExecs; ExecIdx++){bool const bShouldBeHidden = Enum->HasMetaData(TEXT("Hidden"), ExecIdx) || Enum->HasMetaData(TEXT("Spacer"), ExecIdx);if (!bShouldBeHidden){// Can't use Enum->GetNameByIndex here because it doesn't do namespace manglingconst FString NameStr = Enum->GetNameStringByIndex(ExecIdx);UEdGraphPin* CreatedPin = nullptr;// todo: really only makes sense if there are multiple outputsif (bIsFunctionInput || EnumNames.Num() == 1){CreatedPin = CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, *NameStr);}else{CreatedPin = CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, *NameStr);CreatedPin->PinFriendlyName = FText::FromString(FString::Printf(TEXT("(%s) %s"), *Prop->GetDisplayNameText().ToString(), *NameStr));}ExpandAsEnumPins.Add(CreatedPin);if (Enum->HasMetaData(TEXT("Tooltip"), ExecIdx)){FString EnumTooltip = Enum->GetMetaData(TEXT("Tooltip"), ExecIdx);if (const UEdGraphSchema_K2* const K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema())){K2Schema->ConstructBasicPinTooltip(*CreatedPin, FText::FromString(EnumTooltip), CreatedPin->PinToolTip);}else{CreatedPin->PinToolTip = EnumTooltip;}}}}}else{check(Prop->IsA<FBoolProperty>());// Create a pin for true and false, note that the order here does not match the// numeric order of bool, but it is more natural to put true first (e.g. to match branch node):ExpandAsEnumPins.Add(CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, TEXT("True")));ExpandAsEnumPins.Add(CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, TEXT("False")));}if (bIsFunctionInput){// If using ExpandEnumAsExec for input, don't want to add a input exec pinbCreateSingleExecInputPin = false;}else{// If using ExpandEnumAsExec for output, don't want to add a "then" pinbCreateThenPin = false;}}}}if (bCreateSingleExecInputPin){// Single input exec pinCreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);}if (bCreateThenPin){UEdGraphPin* OutputExecPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);// Use 'completed' name for output pins on latent functionsif (Function->HasMetaData(FBlueprintMetadata::MD_Latent)){OutputExecPin->PinFriendlyName = FText::FromName(UEdGraphSchema_K2::PN_Completed);}}}
}FName UK2Node_CallFunction::GetFunctionName() const
{return FunctionReference.GetMemberName();
}void UK2Node_CallFunction::DetermineWantsEnumToExecExpansion(const UFunction* Function)
{bWantsEnumToExecExpansion = false;if (WantsExecPinsForParams(Function)){TArray<FName> EnumNamesToCheck;GetExpandEnumPinNames(Function, EnumNamesToCheck);for (int32 i = EnumNamesToCheck.Num() - 1; i >= 0; --i){const FName& EnumParamName = EnumNamesToCheck[i];FByteProperty* EnumProp = FindFProperty<FByteProperty>(Function, EnumParamName);if ((EnumProp != NULL && EnumProp->Enum != NULL) || FindFProperty<FEnumProperty>(Function, EnumParamName)){bWantsEnumToExecExpansion = true;EnumNamesToCheck.RemoveAt(i);}else{FBoolProperty* BoolProp = FindFProperty<FBoolProperty>(Function, EnumParamName);if (BoolProp){bWantsEnumToExecExpansion = true;EnumNamesToCheck.RemoveAt(i);}}}if (bWantsEnumToExecExpansion && EnumNamesToCheck.Num() > 0 && !bHasCompilerMessage){bHasCompilerMessage = true;ErrorType = EMessageSeverity::Warning;if (EnumNamesToCheck.Num() == 1){ErrorMsg = FText::Format(LOCTEXT("EnumToExecExpansionFailedFmt", "Unable to find enum parameter with name '{0}' to expand for @@"), FText::FromName(EnumNamesToCheck[0])).ToString();}else{FString ParamNames;for (const FName& Name : EnumNamesToCheck){if (!ParamNames.IsEmpty()){ParamNames += TEXT(", ");}ParamNames += Name.ToString();}ErrorMsg = FText::Format(LOCTEXT("EnumToExecExpansionFailedMultipleFmt", "Unable to find enum parameters for names:\n '{{0}}' \nto expand for @@"), FText::FromString(ParamNames)).ToString();}}}
}void UK2Node_CallFunction::GetExpandEnumPinNames(const UFunction* Function, TArray<FName>& EnumNamesToCheck)
{ EnumNamesToCheck.Reset();// todo: use metadatacache if/when that's accepted.const FString EnumParamString = GetAllExecParams(Function);TArray<FString> RawGroupings;EnumParamString.ParseIntoArray(RawGroupings, TEXT(","), false);for (FString& RawGroup : RawGroupings){RawGroup.TrimStartAndEndInline();TArray<FString> IndividualEntries;RawGroup.ParseIntoArray(IndividualEntries, TEXT("|"));for (const FString& Entry : IndividualEntries){if (Entry.IsEmpty()){continue;}EnumNamesToCheck.Add(*Entry);}}
}void UK2Node_CallFunction::GeneratePinTooltip(UEdGraphPin& Pin) const
{ensure(Pin.GetOwningNode() == this);UEdGraphSchema const* Schema = GetSchema();check(Schema != NULL);UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(Schema);if (K2Schema == NULL){Schema->ConstructBasicPinTooltip(Pin, FText::GetEmpty(), Pin.PinToolTip);return;}// get the class function object associated with this nodeUFunction* Function = GetTargetFunction();if (Function == NULL){Schema->ConstructBasicPinTooltip(Pin, FText::GetEmpty(), Pin.PinToolTip);return;}GeneratePinTooltipFromFunction(Pin, Function);
}bool UK2Node_CallFunction::CreatePinsForFunctionCall(const UFunction* Function)
{const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();UClass* FunctionOwnerClass = Function->GetOuterUClass();bIsInterfaceCall = FunctionOwnerClass->HasAnyClassFlags(CLASS_Interface);bIsPureFunc = (Function->HasAnyFunctionFlags(FUNC_BlueprintPure) != false);bIsConstFunc = (Function->HasAnyFunctionFlags(FUNC_Const) != false);DetermineWantsEnumToExecExpansion(Function);// Create input pinsCreateExecPinsForFunctionCall(Function);UEdGraphPin* SelfPin = CreateSelfPin(Function);// Renamed self pin to targetSelfPin->PinFriendlyName = LOCTEXT("Target", "Target");const bool bIsProtectedFunc = Function->GetBoolMetaData(FBlueprintMetadata::MD_Protected);const bool bIsStaticFunc = Function->HasAllFunctionFlags(FUNC_Static);UEdGraph const* const Graph = GetGraph();UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);ensure(BP);if (BP != nullptr){const bool bIsFunctionCompatibleWithSelf = BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass);if (bIsStaticFunc){// For static methods, wire up the self to the CDO of the class if it's not usif (!bIsFunctionCompatibleWithSelf){UClass* AuthoritativeClass = FunctionOwnerClass->GetAuthoritativeClass();SelfPin->DefaultObject = AuthoritativeClass->GetDefaultObject();}// Purity doesn't matter with a static function, we can always hide the self pin since we know how to call the methodSelfPin->bHidden = true;}else{if (Function->GetBoolMetaData(FBlueprintMetadata::MD_HideSelfPin)){SelfPin->bHidden = true;SelfPin->bNotConnectable = true;}else{// Hide the self pin if the function is compatible with the blueprint class and pure (the !bIsConstFunc portion should be going away soon too hopefully)SelfPin->bHidden = (bIsFunctionCompatibleWithSelf && bIsPureFunc && !bIsConstFunc);}}}// Build a list of the pins that should be hidden for this function (ones that are automagically filled in by the K2 compiler)TSet<FName> PinsToHide;TSet<FName> InternalPins;FBlueprintEditorUtils::GetHiddenPinsForFunction(Graph, Function, PinsToHide, &InternalPins);const bool bShowWorldContextPin = ((PinsToHide.Num() > 0) && BP && BP->ParentClass && BP->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin));// Create the inputs and outputsbool bAllPinsGood = true;for (TFieldIterator<FProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt){FProperty* Param = *PropIt;const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_ReturnParm) && (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm));const bool bIsRefParam = Param->HasAnyPropertyFlags(CPF_ReferenceParm) && bIsFunctionInput;const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;UEdGraphNode::FCreatePinParams PinParams;PinParams.bIsReference = bIsRefParam;UEdGraphPin* Pin = CreatePin(Direction, NAME_None, Param->GetFName(), PinParams);const bool bPinGood = (Pin && K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType));if (bPinGood){// Check for a display name overrideconst FString& PinDisplayName = Param->GetMetaData(FBlueprintMetadata::MD_DisplayName);if (!PinDisplayName.IsEmpty()){Pin->PinFriendlyName = FText::FromString(PinDisplayName);}else if (Function->GetReturnProperty() == Param && Function->HasMetaData(FBlueprintMetadata::MD_ReturnDisplayName)){Pin->PinFriendlyName = Function->GetMetaDataText(FBlueprintMetadata::MD_ReturnDisplayName);}//Flag pin as read only for const reference propertyPin->bDefaultValueIsIgnored = Param->HasAllPropertyFlags(CPF_ConstParm | CPF_ReferenceParm) && (!Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm) || Pin->PinType.IsContainer());const bool bAdvancedPin = Param->HasAllPropertyFlags(CPF_AdvancedDisplay);Pin->bAdvancedView = bAdvancedPin;if(bAdvancedPin && (ENodeAdvancedPins::NoPins == AdvancedPinDisplay)){AdvancedPinDisplay = ENodeAdvancedPins::Hidden;}FString ParamValue;if (K2Schema->FindFunctionParameterDefaultValue(Function, Param, ParamValue)){K2Schema->SetPinAutogeneratedDefaultValue(Pin, ParamValue);}else{K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin);}if (PinsToHide.Contains(Pin->PinName)){const FString PinNameStr = Pin->PinName.ToString();const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);const FString& WorldContextMetaValue  = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);bool bIsSelfPin = ((PinNameStr == DefaultToSelfMetaValue) || (PinNameStr == WorldContextMetaValue));if (!bShowWorldContextPin || !bIsSelfPin){Pin->bHidden = true;Pin->bNotConnectable = InternalPins.Contains(Pin->PinName);}}PostParameterPinCreated(Pin);}bAllPinsGood = bAllPinsGood && bPinGood;}// If we have 'enum to exec' parameters, set their default value to something valid so we don't get warningsif(bWantsEnumToExecExpansion){TArray<FName> EnumNamesToCheck;GetExpandEnumPinNames(Function, EnumNamesToCheck);for (const FName& Name : EnumNamesToCheck){UEdGraphPin* EnumParamPin = FindPin(Name);if (UEnum* PinEnum = (EnumParamPin ? Cast<UEnum>(EnumParamPin->PinType.PinSubCategoryObject.Get()) : NULL)){EnumParamPin->DefaultValue = PinEnum->GetNameStringByIndex(0);}}}return bAllPinsGood;
}void UK2Node_CallFunction::PostReconstructNode()
{Super::PostReconstructNode();InvalidatePinTooltips();// conform pins that are marked as SetParam:ConformContainerPins();FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this);// Fixup self node, may have been overridden from old self nodeUFunction* Function = GetTargetFunction();const bool bIsStaticFunc = Function ? Function->HasAllFunctionFlags(FUNC_Static) : false;UEdGraphPin* SelfPin = FindPin(UEdGraphSchema_K2::PN_Self);if (bIsStaticFunc && SelfPin){// Wire up the self to the CDO of the class if it's not usif (UBlueprint* BP = GetBlueprint()){UClass* FunctionOwnerClass = Function->GetOuterUClass();if (!BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass)){SelfPin->DefaultObject = FunctionOwnerClass->GetAuthoritativeClass()->GetDefaultObject();}else{// In case a non-NULL reference was previously serialized on load, ensure that it's set to NULL here to match what a new node's self pin would be initialized as (see CreatePinsForFunctionCall).SelfPin->DefaultObject = nullptr;}}}if (UEdGraphPin* TypePickerPin = FDynamicOutputHelper::GetTypePickerPin(this)){FDynamicOutputHelper(TypePickerPin).ConformOutputType();}if (IsNodePure()){// Remove any pre-existing breakpoint on this node since pure nodes cannot have breakpointsif (UBreakpoint* ExistingBreakpoint = FKismetDebugUtilities::FindBreakpointForNode(GetBlueprint(), this)){// Remove the breakpointFKismetDebugUtilities::StartDeletingBreakpoint(ExistingBreakpoint, GetBlueprint());}}
}void UK2Node_CallFunction::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{Super::NotifyPinConnectionListChanged(Pin);// conform pins that are marked as SetParam:ConformContainerPins();if (!ensure(Pin)){return;}FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this, Pin);// Refresh the node to hide internal-only pins once the [invalid] connection has been brokenif (Pin->bHidden && Pin->bNotConnectable && Pin->LinkedTo.Num() == 0){GetGraph()->NotifyGraphChanged();}if (bIsBeadFunction){if (Pin->LinkedTo.Num() == 0){// Commit suicide; bead functions must always have an input and output connectionDestroyNode();}}InvalidatePinTooltips();if(!Pin->IsPendingKill()){FDynamicOutputHelper(Pin).ConformOutputType();}
}void UK2Node_CallFunction::PinDefaultValueChanged(UEdGraphPin* Pin)
{Super::PinDefaultValueChanged(Pin);InvalidatePinTooltips();FDynamicOutputHelper(Pin).ConformOutputType();
}UFunction* UK2Node_CallFunction::GetTargetFunction() const
{if(!FBlueprintCompilationManager::IsGeneratedClassLayoutReady()){// first look in the skeleton class:if(UFunction* SkeletonFn = GetTargetFunctionFromSkeletonClass()){return SkeletonFn;}}UFunction* Function = FunctionReference.ResolveMember<UFunction>(GetBlueprintClassFromNode());return Function;
}UFunction* UK2Node_CallFunction::GetTargetFunctionFromSkeletonClass() const
{UFunction* TargetFunction = nullptr;UClass* ParentClass = FunctionReference.GetMemberParentClass( GetBlueprintClassFromNode() );UBlueprint* OwningBP = ParentClass ? Cast<UBlueprint>( ParentClass->ClassGeneratedBy ) : nullptr;if( UClass* SkeletonClass = OwningBP ? OwningBP->SkeletonGeneratedClass : nullptr ){TargetFunction = SkeletonClass->FindFunctionByName( FunctionReference.GetMemberName() );}return TargetFunction;
}UEdGraphPin* UK2Node_CallFunction::GetThenPin() const
{UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Then);check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be outputreturn Pin;
}UEdGraphPin* UK2Node_CallFunction::GetReturnValuePin() const
{UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_ReturnValue);check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be outputreturn Pin;
}bool UK2Node_CallFunction::IsLatentFunction() const
{if (UFunction* Function = GetTargetFunction()){if (Function->HasMetaData(FBlueprintMetadata::MD_Latent)){return true;}}return false;
}bool UK2Node_CallFunction::AllowMultipleSelfs(bool bInputAsArray) const
{if (UFunction* Function = GetTargetFunction()){return CanFunctionSupportMultipleTargets(Function);}return Super::AllowMultipleSelfs(bInputAsArray);
}bool UK2Node_CallFunction::CanFunctionSupportMultipleTargets(UFunction const* Function)
{bool const bIsImpure = !Function->HasAnyFunctionFlags(FUNC_BlueprintPure);bool const bIsLatent = Function->HasMetaData(FBlueprintMetadata::MD_Latent);bool const bHasReturnParam = (Function->GetReturnProperty() != nullptr);return !bHasReturnParam && bIsImpure && !bIsLatent;
}bool UK2Node_CallFunction::CanPasteHere(const UEdGraph* TargetGraph) const
{// Basic check for graph compatibility, etc.bool bCanPaste = Super::CanPasteHere(TargetGraph);// We check function context for placability only in the base class case; derived classes are typically bound to// specific functions that should always be placeable, but may not always be explicitly callable (e.g. InternalUseOnly).if(bCanPaste && GetClass() == StaticClass()){const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();uint32 AllowedFunctionTypes = UEdGraphSchema_K2::EFunctionType::FT_Pure | UEdGraphSchema_K2::EFunctionType::FT_Const | UEdGraphSchema_K2::EFunctionType::FT_Protected;if(K2Schema->DoesGraphSupportImpureFunctions(TargetGraph)){AllowedFunctionTypes |= UEdGraphSchema_K2::EFunctionType::FT_Imperative;}UFunction* TargetFunction = GetTargetFunction();if( !TargetFunction ){TargetFunction = GetTargetFunctionFromSkeletonClass();}if (!TargetFunction){// If the function doesn't exist and it is from self context, then it could be created from a CustomEvent node, that was also pasted (but wasn't compiled yet).bCanPaste = FunctionReference.IsSelfContext();}else{bCanPaste = K2Schema->CanFunctionBeUsedInGraph(FBlueprintEditorUtils::FindBlueprintForGraphChecked(TargetGraph)->GeneratedClass, TargetFunction, TargetGraph, AllowedFunctionTypes, false);}}return bCanPaste;
}bool UK2Node_CallFunction::IsActionFilteredOut(FBlueprintActionFilter const& Filter)
{bool bIsFilteredOut = false;for(UEdGraph* TargetGraph : Filter.Context.Graphs){bIsFilteredOut |= !CanPasteHere(TargetGraph);}if(const UFunction* TargetFunction = GetTargetFunction()){const bool bIsProtected = (TargetFunction->FunctionFlags & FUNC_Protected) != 0;const bool bIsPrivate = (TargetFunction->FunctionFlags & FUNC_Private) != 0;const UClass* OwningClass = TargetFunction->GetOwnerClass();if( (bIsProtected || bIsPrivate) && !FBlueprintEditorUtils::IsNativeSignature(TargetFunction) && OwningClass){OwningClass = OwningClass->GetAuthoritativeClass();// we can filter private and protected blueprints that are unrelated:bool bAccessibleInAll = true;for (const UBlueprint* Blueprint : Filter.Context.Blueprints){UClass* AuthoritativeClass = Blueprint->GeneratedClass;if(!AuthoritativeClass){continue;}if(bIsPrivate){bAccessibleInAll = bAccessibleInAll && AuthoritativeClass == OwningClass;}else if(bIsProtected){bAccessibleInAll = bAccessibleInAll && AuthoritativeClass->IsChildOf(OwningClass);}}if(!bAccessibleInAll){bIsFilteredOut = true;}}}return bIsFilteredOut;
}static FLinearColor GetPalletteIconColor(UFunction const* Function)
{bool const bIsPure = (Function != nullptr) && Function->HasAnyFunctionFlags(FUNC_BlueprintPure);if (bIsPure){return GetDefault<UGraphEditorSettings>()->PureFunctionCallNodeTitleColor;}return GetDefault<UGraphEditorSettings>()->FunctionCallNodeTitleColor;
}FSlateIcon UK2Node_CallFunction::GetPaletteIconForFunction(UFunction const* Function, FLinearColor& OutColor)
{static const FName NativeMakeFunc(TEXT("NativeMakeFunc"));static const FName NativeBrakeFunc(TEXT("NativeBreakFunc"));if (Function && Function->HasMetaData(NativeMakeFunc)){static FSlateIcon Icon("EditorStyle", "GraphEditor.MakeStruct_16x");return Icon;}else if (Function && Function->HasMetaData(NativeBrakeFunc)){static FSlateIcon Icon("EditorStyle", "GraphEditor.BreakStruct_16x");return Icon;}// Check to see if the function is calling an function that could be an event, display the event icon instead.else if (Function && UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(Function)){static FSlateIcon Icon("EditorStyle", "GraphEditor.Event_16x");return Icon;}else{OutColor = GetPalletteIconColor(Function);static FSlateIcon Icon("EditorStyle", "Kismet.AllClasses.FunctionIcon");return Icon;}
}FLinearColor UK2Node_CallFunction::GetNodeTitleColor() const
{return GetPalletteIconColor(GetTargetFunction());
}FText UK2Node_CallFunction::GetTooltipText() const
{FText Tooltip;UFunction* Function = GetTargetFunction();if (Function == nullptr){return FText::Format(LOCTEXT("CallUnknownFunction", "Call unknown function {0}"), FText::FromName(FunctionReference.GetMemberName()));}else if (CachedTooltip.IsOutOfDate(this)){FText BaseTooltip = FText::FromString(GetDefaultTooltipForFunction(Function));FFormatNamedArguments Args;Args.Add(TEXT("DefaultTooltip"), BaseTooltip);if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly)){Args.Add(TEXT("ClientString"),NSLOCTEXT("K2Node", "ServerFunction", "Authority Only. This function will only execute on the server."));// FText::Format() is slow, so we cache this to save on performanceCachedTooltip.SetCachedText(FText::Format(LOCTEXT("CallFunction_SubtitledTooltip", "{DefaultTooltip}\n\n{ClientString}"), Args), this);}else if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic)){Args.Add(TEXT("ClientString"),NSLOCTEXT("K2Node", "ClientFunction", "Cosmetic. This event is only for cosmetic, non-gameplay actions."));// FText::Format() is slow, so we cache this to save on performanceCachedTooltip.SetCachedText(FText::Format(LOCTEXT("CallFunction_SubtitledTooltip", "{DefaultTooltip}\n\n{ClientString}"), Args), this);} else{CachedTooltip.SetCachedText(BaseTooltip, this);}}return CachedTooltip;
}void UK2Node_CallFunction::GeneratePinTooltipFromFunction(UEdGraphPin& Pin, const UFunction* Function)
{if (Pin.bWasTrashed){return;}// figure what tag we should be parsing for (is this a return-val pin, or a parameter?)FString ParamName;FString TagStr = TEXT("@param");const bool bReturnPin = Pin.PinName == UEdGraphSchema_K2::PN_ReturnValue;if (bReturnPin){TagStr = TEXT("@return");}else{ParamName = Pin.PinName.ToString();}// grab the the function's comment block for us to parseFString FunctionToolTipText = Function->GetToolTipText().ToString();int32 CurStrPos = INDEX_NONE;int32 FullToolTipLen = FunctionToolTipText.Len();// parse the full function tooltip text, looking for tag linesdo {CurStrPos = FunctionToolTipText.Find(TagStr, ESearchCase::IgnoreCase, ESearchDir::FromStart, CurStrPos);if (CurStrPos == INDEX_NONE) // if the tag wasn't found{break;}// advance past the tagCurStrPos += TagStr.Len();// handle people having done @returns instead of @returnif (bReturnPin && CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] == TEXT('s')){++CurStrPos;}// advance past whitespacewhile(CurStrPos < FullToolTipLen && FChar::IsWhitespace(FunctionToolTipText[CurStrPos])){++CurStrPos;}// if this is a parameter pinif (!ParamName.IsEmpty()){FString TagParamName;// copy the parameter namewhile (CurStrPos < FullToolTipLen && !FChar::IsWhitespace(FunctionToolTipText[CurStrPos])){TagParamName.AppendChar(FunctionToolTipText[CurStrPos++]);}// if this @param tag doesn't match the param we're looking forif (TagParamName != ParamName){continue;}}// advance past whitespace (get to the meat of the comment)// since many doxygen style @param use the format "@param <param name> - <comment>" we also strip - if it is before we get to any other non-whitespacewhile(CurStrPos < FullToolTipLen && (FChar::IsWhitespace(FunctionToolTipText[CurStrPos]) || FunctionToolTipText[CurStrPos] == '-')){++CurStrPos;}FString ParamDesc;// collect the param/return-val descriptionwhile (CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] != TEXT('@')){// advance past newlinewhile(CurStrPos < FullToolTipLen && FChar::IsLinebreak(FunctionToolTipText[CurStrPos])){++CurStrPos;// advance past whitespace at the start of a new linewhile(CurStrPos < FullToolTipLen && FChar::IsWhitespace(FunctionToolTipText[CurStrPos])){++CurStrPos;}// replace the newline with a single spaceif(CurStrPos < FullToolTipLen && !FChar::IsLinebreak(FunctionToolTipText[CurStrPos])){ParamDesc.AppendChar(TEXT(' '));}}if (CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] != TEXT('@')){ParamDesc.AppendChar(FunctionToolTipText[CurStrPos++]);}}// trim any trailing whitespace from the descriptive textParamDesc.TrimEndInline();// if we came up with a valid description for the param/return-valif (!ParamDesc.IsEmpty()){Pin.PinToolTip += ParamDesc;break; // we found a match, so there's no need to continue}} while (CurStrPos < FullToolTipLen);// If we have no parameter or return value descriptions the full description will be relevant in describing the return value:if( bReturnPin && Pin.PinToolTip.IsEmpty() && FunctionToolTipText.Find(TEXT("@param")) == INDEX_NONE && FunctionToolTipText.Find(TEXT("@return")) == INDEX_NONE){// for the return pin, default to using the function description if no @return tag was provided:Pin.PinToolTip = Function->GetToolTipText().ToString();}GetDefault<UEdGraphSchema_K2>()->ConstructBasicPinTooltip(Pin, FText::FromString(Pin.PinToolTip), Pin.PinToolTip);
}FText UK2Node_CallFunction::GetUserFacingFunctionName(const UFunction* Function)
{FText ReturnDisplayName;if (Function != NULL){if (GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames){ReturnDisplayName = Function->GetDisplayNameText();}else{static const FString Namespace = TEXT("UObjectDisplayNames");const FString Key = Function->GetFullGroupName(false);ReturnDisplayName = Function->GetMetaDataText(TEXT("DisplayName"), Namespace, Key);}}return ReturnDisplayName;
}FString UK2Node_CallFunction::GetDefaultTooltipForFunction(const UFunction* Function)
{FString Tooltip;if (Function != NULL){Tooltip = Function->GetToolTipText().ToString();}if (!Tooltip.IsEmpty()){// Strip off the doxygen nastinessstatic const FString DoxygenParam(TEXT("@param"));static const FString DoxygenReturn(TEXT("@return"));static const FString DoxygenSee(TEXT("@see"));static const FString TooltipSee(TEXT("See:"));static const FString DoxygenNote(TEXT("@note"));static const FString TooltipNote(TEXT("Note:"));Tooltip.Split(DoxygenParam, &Tooltip, nullptr, ESearchCase::IgnoreCase, ESearchDir::FromStart);Tooltip.Split(DoxygenReturn, &Tooltip, nullptr, ESearchCase::IgnoreCase, ESearchDir::FromStart);Tooltip.ReplaceInline(*DoxygenSee, *TooltipSee);Tooltip.ReplaceInline(*DoxygenNote, *TooltipNote);Tooltip.TrimStartAndEndInline();UClass* CurrentSelfClass = (Function != NULL) ? Function->GetOwnerClass() : NULL;UClass const* TrueSelfClass = CurrentSelfClass;if (CurrentSelfClass && CurrentSelfClass->ClassGeneratedBy){TrueSelfClass = CurrentSelfClass->GetAuthoritativeClass();}FText TargetDisplayText = (TrueSelfClass != NULL) ? TrueSelfClass->GetDisplayNameText() : LOCTEXT("None", "None");FFormatNamedArguments Args;Args.Add(TEXT("TargetName"), TargetDisplayText);Args.Add(TEXT("Tooltip"), FText::FromString(Tooltip));return FText::Format(LOCTEXT("CallFunction_Tooltip", "{Tooltip}\n\nTarget is {TargetName}"), Args).ToString();}else{return GetUserFacingFunctionName(Function).ToString();}
}FText UK2Node_CallFunction::GetDefaultCategoryForFunction(const UFunction* Function, const FText& BaseCategory)
{FText NodeCategory = BaseCategory;if( Function->HasMetaData(FBlueprintMetadata::MD_FunctionCategory) ){FText FuncCategory;// If we are not showing friendly names, return the metadata stored, without localizationif( GEditor && !GetDefault<UEditorStyleSettings>()->bShowFriendlyNames ){FuncCategory = FText::FromString(Function->GetMetaData(FBlueprintMetadata::MD_FunctionCategory));}else{// Look for localized metadataFuncCategory = FObjectEditorUtils::GetCategoryText(Function);// If the result is culture invariant, force it into a display stringif (FuncCategory.IsCultureInvariant()){FuncCategory = FText::FromString(FName::NameToDisplayString(FuncCategory.ToString(), false));}}// Combine with the BaseCategory to form the full category, delimited by "|"if (!FuncCategory.IsEmpty() && !NodeCategory.IsEmpty()){NodeCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), NodeCategory, FuncCategory);}else if (NodeCategory.IsEmpty()){NodeCategory = FuncCategory;}}return NodeCategory;
}FText UK2Node_CallFunction::GetKeywordsForFunction(const UFunction* Function)
{// If the friendly name and real function name do not match add the real function name friendly name as a keyword.FString Keywords;if( Function->GetName() != GetUserFacingFunctionName(Function).ToString() ){Keywords = Function->GetName();}if (ShouldDrawCompact(Function)){Keywords.AppendChar(TEXT(' '));Keywords += GetCompactNodeTitle(Function);}FText MetadataKeywords = Function->GetMetaDataText(FBlueprintMetadata::MD_FunctionKeywords, TEXT("UObjectKeywords"), Function->GetFullGroupName(false));FText ResultKeywords;if (!MetadataKeywords.IsEmpty()){FFormatNamedArguments Args;Args.Add(TEXT("Name"), FText::FromString(Keywords));Args.Add(TEXT("MetadataKeywords"), MetadataKeywords);ResultKeywords = FText::Format(FText::FromString("{Name} {MetadataKeywords}"), Args);}else{ResultKeywords = FText::FromString(Keywords);}return ResultKeywords;
}void UK2Node_CallFunction::SetFromFunction(const UFunction* Function)
{if (Function != NULL){bIsPureFunc = Function->HasAnyFunctionFlags(FUNC_BlueprintPure);bIsConstFunc = Function->HasAnyFunctionFlags(FUNC_Const);DetermineWantsEnumToExecExpansion(Function);FunctionReference.SetFromField<UFunction>(Function, GetBlueprintClassFromNode());}
}FString UK2Node_CallFunction::GetDocumentationLink() const
{UClass* ParentClass = NULL;if (FunctionReference.IsSelfContext()){if (HasValidBlueprint()){UFunction* Function = FindUField<UFunction>(GetBlueprint()->GeneratedClass, FunctionReference.GetMemberName());if (Function != NULL){ParentClass = Function->GetOwnerClass();}}		}else {ParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());}if (ParentClass != NULL){return FString::Printf(TEXT("Shared/GraphNodes/Blueprint/%s%s"), ParentClass->GetPrefixCPP(), *ParentClass->GetName());}return FString("Shared/GraphNodes/Blueprint/UK2Node_CallFunction");
}FString UK2Node_CallFunction::GetDocumentationExcerptName() const
{return FunctionReference.GetMemberName().ToString();
}FString UK2Node_CallFunction::GetDescriptiveCompiledName() const
{return FString(TEXT("CallFunc_")) + FunctionReference.GetMemberName().ToString();
}bool UK2Node_CallFunction::ShouldDrawCompact(const UFunction* Function)
{return (Function != NULL) && Function->HasMetaData(FBlueprintMetadata::MD_CompactNodeTitle);
}bool UK2Node_CallFunction::ShouldDrawCompact() const
{UFunction* Function = GetTargetFunction();return ShouldDrawCompact(Function);
}bool UK2Node_CallFunction::ShouldDrawAsBead() const
{return bIsBeadFunction;
}bool UK2Node_CallFunction::ShouldShowNodeProperties() const
{// Show node properties if this corresponds to a function graphif (FunctionReference.GetMemberName() != NAME_None && HasValidBlueprint()){return FindObject<UEdGraph>(GetBlueprint(), *(FunctionReference.GetMemberName().ToString())) != NULL;}return false;
}FString UK2Node_CallFunction::GetCompactNodeTitle(const UFunction* Function)
{static const FString ProgrammerMultiplicationSymbol = TEXT("*");static const FString CommonMultiplicationSymbol = TEXT("\xD7");static const FString ProgrammerDivisionSymbol = TEXT("/");static const FString CommonDivisionSymbol = TEXT("\xF7");static const FString ProgrammerConversionSymbol = TEXT("->");static const FString CommonConversionSymbol = TEXT("\x2022");const FString& OperatorTitle = Function->GetMetaData(FBlueprintMetadata::MD_CompactNodeTitle);if (!OperatorTitle.IsEmpty()){if (OperatorTitle == ProgrammerMultiplicationSymbol){return CommonMultiplicationSymbol;}else if (OperatorTitle == ProgrammerDivisionSymbol){return CommonDivisionSymbol;}else if (OperatorTitle == ProgrammerConversionSymbol){return CommonConversionSymbol;}else{return OperatorTitle;}}return Function->GetName();
}FText UK2Node_CallFunction::GetCompactNodeTitle() const
{UFunction* Function = GetTargetFunction();if (Function != NULL){return FText::FromString(GetCompactNodeTitle(Function));}else{return Super::GetCompactNodeTitle();}
}void UK2Node_CallFunction::GetRedirectPinNames(const UEdGraphPin& Pin, TArray<FString>& RedirectPinNames) const
{Super::GetRedirectPinNames(Pin, RedirectPinNames);if (RedirectPinNames.Num() > 0){const FString OldPinName = RedirectPinNames[0];// first add functionname.paramRedirectPinNames.Add(FString::Printf(TEXT("%s.%s"), *FunctionReference.GetMemberName().ToString(), *OldPinName));// if there is class, also add an option for class.functionname.paramUClass* FunctionClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());while (FunctionClass){RedirectPinNames.Add(FString::Printf(TEXT("%s.%s.%s"), *FunctionClass->GetName(), *FunctionReference.GetMemberName().ToString(), *OldPinName));FunctionClass = FunctionClass->GetSuperClass();}}
}void UK2Node_CallFunction::FixupSelfMemberContext()
{UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(this);auto IsBlueprintOfType = [Blueprint](UClass* ClassType)->bool{bool bIsChildOf  = Blueprint && (Blueprint->GeneratedClass != nullptr) && Blueprint->GeneratedClass->IsChildOf(ClassType);if (!bIsChildOf && Blueprint && (Blueprint->SkeletonGeneratedClass)){bIsChildOf = Blueprint->SkeletonGeneratedClass->IsChildOf(ClassType);}return bIsChildOf;};UClass* MemberClass = FunctionReference.GetMemberParentClass();if (FunctionReference.IsSelfContext()){// if there is a function that matches the reference in the new context// and there are no connections to the self pin, we just want to call// that functionUEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input);if (!FunctionReference.ResolveMember<UFunction>(Blueprint) || (SelfPin && SelfPin->HasAnyConnections())){if (MemberClass == nullptr){// the self pin may have type information stored on itif (SelfPin){MemberClass = Cast<UClass>(SelfPin->PinType.PinSubCategoryObject.Get());}}// if we happened to retain the ParentClass for a self reference // (unlikely), then we know where this node came from... let's keep it// referencing that functionif (MemberClass != nullptr){if (!IsBlueprintOfType(MemberClass)){FunctionReference.SetExternalMember(FunctionReference.GetMemberName(), MemberClass);}}// else, there is nothing we can do... the node will produce an error later during compilation}}else if (MemberClass != nullptr){if (IsBlueprintOfType(MemberClass)){FunctionReference.SetSelfMember(FunctionReference.GetMemberName());}}
}void UK2Node_CallFunction::PostPasteNode()
{Super::PostPasteNode();FixupSelfMemberContext();if (UFunction* Function = GetTargetFunction()){if (Pins.Num() > 0){// After pasting we need to go through and ensure the hidden the self pins is correct in case the source blueprint had different metadataTSet<FName> PinsToHide;FBlueprintEditorUtils::GetHiddenPinsForFunction(GetGraph(), Function, PinsToHide);const bool bShowWorldContextPin = ((PinsToHide.Num() > 0) && GetBlueprint()->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin));const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);const FString& WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex){UEdGraphPin* Pin = Pins[PinIndex];const FString PinNameStr = Pin->PinName.ToString();const bool bIsSelfPin = ((PinNameStr == DefaultToSelfMetaValue) || (PinNameStr == WorldContextMetaValue));const bool bPinShouldBeHidden = ((Pin->SubPins.Num() > 0) || (PinsToHide.Contains(Pin->PinName) && (!bShowWorldContextPin || !bIsSelfPin)));if (bPinShouldBeHidden && !Pin->bHidden){Pin->BreakAllPinLinks();K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin);}Pin->bHidden = bPinShouldBeHidden;}}}
}void UK2Node_CallFunction::PostDuplicate(bool bDuplicateForPIE)
{Super::PostDuplicate(bDuplicateForPIE);if (!bDuplicateForPIE && (!this->HasAnyFlags(RF_Transient))){FixupSelfMemberContext();}
}void UK2Node_CallFunction::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{Super::ValidateNodeDuringCompilation(MessageLog);const UBlueprint* Blueprint = GetBlueprint();UFunction *Function = GetTargetFunction();if (Function == NULL){FString OwnerName;if (Blueprint != nullptr){OwnerName = Blueprint->GetName();if (UClass* FuncOwnerClass = FunctionReference.GetMemberParentClass(Blueprint->GeneratedClass)){OwnerName = FuncOwnerClass->GetName();}}FString const FunctName = FunctionReference.GetMemberName().ToString();FText const WarningFormat = LOCTEXT("FunctionNotFoundFmt", "Could not find a function named \"{0}\" in '{1}'.\nMake sure '{2}' has been compiled for @@");MessageLog.Error(*FText::Format(WarningFormat, FText::FromString(FunctName), FText::FromString(OwnerName), FText::FromString(OwnerName)).ToString(), this);}else if (WantsExecPinsForParams(Function) && bWantsEnumToExecExpansion == false){// will technically not have a properly formatted output for multiple params... but /shrug. const FString EnumParamName = GetAllExecParams(Function);MessageLog.Warning(*FText::Format(LOCTEXT("EnumToExecExpansionFailedFmt", "Unable to find enum parameter with name '{0}' to expand for @@"), FText::FromString(EnumParamName)).ToString(), this);}const UClass* BlueprintClass = Blueprint ? Blueprint->ParentClass : nullptr;const bool bIsEditorOnlyBlueprintBaseClass = !BlueprintClass || IsEditorOnlyObject(BlueprintClass);// This error is disabled while we figure out how we can identify uncooked only// blueprints that want to make use of uncooked only APIs:#if 0const bool bIsUncookedOnlyFunction = Function && Function->GetOutermost()->HasAllPackagesFlags(PKG_UncookedOnly);if (	bIsUncookedOnlyFunction &&// Only allow calls to uncooked only functions from editor only/uncooked only// contexts:!(	GetOutermost()->HasAnyPackageFlags(PKG_UncookedOnly|PKG_EditorOnly) ||bIsEditorOnlyBlueprintBaseClass )){MessageLog.Error(*LOCTEXT("UncookedOnlyError", "Attempting to call uncooked only function @@ in runtime blueprint").ToString(), this);}#endif //0// Ensure that editor module BP exposed UFunctions can only be called in blueprints for which the base class is also part of an editor module// Also check for functions wrapped in WITH_EDITOR if (Function && Blueprint &&(IsEditorOnlyObject(Function) || Function->HasAnyFunctionFlags(FUNC_EditorOnly))){	if (!bIsEditorOnlyBlueprintBaseClass){FString const FunctName = Function->GetName();FText const WarningFormat = LOCTEXT("EditorFunctionFmt", "Cannot use the editor function \"{0}\" in this runtime Blueprint. Only for use in Editor Utility Blueprints and Blutilities.");MessageLog.Error(*FText::Format(WarningFormat, FText::FromString(FunctName)).ToString(), this);}}if (Function){// enforce UnsafeDuringActorConstruction keywordif (Function->HasMetaData(FBlueprintMetadata::MD_UnsafeForConstructionScripts)){// emit warning if we are in a construction scriptUEdGraph const* const Graph = GetGraph();bool bNodeIsInConstructionScript = UEdGraphSchema_K2::IsConstructionScript(Graph);if (bNodeIsInConstructionScript == false){// IsConstructionScript() can return false if graph was cloned from the construction script// in that case, check the function entryTArray<const UK2Node_FunctionEntry*> EntryPoints;Graph->GetNodesOfClass(EntryPoints);if (EntryPoints.Num() == 1){UK2Node_FunctionEntry const* const Node = EntryPoints[0];if (Node){UFunction* const SignatureFunction = Node->FunctionReference.ResolveMember<UFunction>(Node->GetBlueprintClassFromNode());bNodeIsInConstructionScript = SignatureFunction && (SignatureFunction->GetFName() == UEdGraphSchema_K2::FN_UserConstructionScript);}}}if ( bNodeIsInConstructionScript ){MessageLog.Warning(*LOCTEXT("FunctionUnsafeDuringConstruction", "Function '@@' is unsafe to call in a construction script.").ToString(), this);}}// enforce WorldContext restrictionsconst bool bInsideBpFuncLibrary = Blueprint && (BPTYPE_FunctionLibrary == Blueprint->BlueprintType);if (!bInsideBpFuncLibrary && Function->HasMetaData(FBlueprintMetadata::MD_WorldContext) && !Function->HasMetaData(FBlueprintMetadata::MD_CallableWithoutWorldContext)){check(Blueprint);UClass* ParentClass = Blueprint->ParentClass;check(ParentClass);if (ParentClass && !FBlueprintEditorUtils::ImplementsGetWorld(Blueprint) && !ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin)){MessageLog.Warning(*LOCTEXT("FunctionUnsafeInContext", "Function '@@' is unsafe to call from blueprints of class '@@'.").ToString(), this, ParentClass);}}if(Blueprint && !FBlueprintEditorUtils::IsNativeSignature(Function)){// enforce protected function restrictionconst bool bCanTreatAsError = Blueprint->GetLinkerCustomVersion(FFrameworkObjectVersion::GUID) >= FFrameworkObjectVersion::EnforceBlueprintFunctionVisibility;const bool bIsProtected = (Function->FunctionFlags & FUNC_Protected) != 0;const bool bFuncBelongsToSubClass = Blueprint->SkeletonGeneratedClass->IsChildOf(Function->GetOuterUClass());if (bIsProtected && !bFuncBelongsToSubClass){if(bCanTreatAsError){MessageLog.Error(*LOCTEXT("FunctionProtectedAccessed", "Function '@@' is protected and can't be accessed outside of its hierarchy.").ToString(), this);}else{MessageLog.Note(*LOCTEXT("FunctionProtectedAccessedNote", "Function '@@' is protected and can't be accessed outside of its hierarchy - this will be an error if the asset is resaved.").ToString(), this);}}// enforce private function restrictionconst bool bIsPrivate = (Function->FunctionFlags & FUNC_Private) != 0;const bool bFuncBelongsToClass = bFuncBelongsToSubClass && (Blueprint->SkeletonGeneratedClass == Function->GetOuterUClass());if (bIsPrivate && !bFuncBelongsToClass){if(bCanTreatAsError){MessageLog.Error(*LOCTEXT("FunctionPrivateAccessed", "Function '@@' is private and can't be accessed outside of its defined class '@@'.").ToString(), this, Function->GetOuterUClass());}else{MessageLog.Note(*LOCTEXT("FunctionPrivateAccessedNote", "Function '@@' is private and can't be accessed outside of its defined class '@@' - this will be an error if the asset is resaved.").ToString(), this, Function->GetOuterUClass());}}}}FDynamicOutputHelper::VerifyNode(this, MessageLog);for (UEdGraphPin* Pin : Pins){if (Pin && Pin->PinType.bIsWeakPointer && !Pin->PinType.IsContainer()){const FString ErrorString = FText::Format(LOCTEXT("WeakPtrNotSupportedErrorFmt", "Weak pointers are not supported as function parameters. Pin '{0}' @@"),FText::FromString(Pin->GetName())).ToString();MessageLog.Error(*ErrorString, this);}}
}void UK2Node_CallFunction::Serialize(FArchive& Ar)
{Super::Serialize(Ar);Ar.UsingCustomVersion(FReleaseObjectVersion::GUID);if (Ar.IsLoading()){if (Ar.UE4Ver() < VER_UE4_SWITCH_CALL_NODE_TO_USE_MEMBER_REFERENCE){UFunction* Function = FindUField<UFunction>(CallFunctionClass_DEPRECATED, CallFunctionName_DEPRECATED);const bool bProbablySelfCall = (CallFunctionClass_DEPRECATED == NULL) || ((Function != NULL) && (Function->GetOuterUClass()->ClassGeneratedBy == GetBlueprint()));FunctionReference.SetDirect(CallFunctionName_DEPRECATED, FGuid(), CallFunctionClass_DEPRECATED, bProbablySelfCall);}if(Ar.UE4Ver() < VER_UE4_K2NODE_REFERENCEGUIDS){FGuid FunctionGuid;if (UBlueprint::GetGuidFromClassByFieldName<UFunction>(GetBlueprint()->GeneratedClass, FunctionReference.GetMemberName(), FunctionGuid)){const bool bSelf = FunctionReference.IsSelfContext();FunctionReference.SetDirect(FunctionReference.GetMemberName(), FunctionGuid, (bSelf ? NULL : FunctionReference.GetMemberParentClass((UClass*)NULL)), bSelf);}}// Consider the 'CPF_UObjectWrapper' flag on native function call parameters and return values.if (Ar.CustomVer(FReleaseObjectVersion::GUID) < FReleaseObjectVersion::PinTypeIncludesUObjectWrapperFlag){if (UFunction* TargetFunction = GetTargetFunction()){if (TargetFunction->IsNative()){for (TFieldIterator<FProperty> PropIt(TargetFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt){if (UEdGraphPin* Pin = FindPin(PropIt->GetFName())){if (const FMapProperty* MapProperty = CastField<FMapProperty>(*PropIt)){if (MapProperty->KeyProp && MapProperty->KeyProp->HasAllPropertyFlags(CPF_UObjectWrapper)){Pin->PinType.bIsUObjectWrapper = 1;}if (MapProperty->ValueProp && MapProperty->ValueProp->HasAllPropertyFlags(CPF_UObjectWrapper)){Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;}}else if (const FSetProperty* SetProperty = CastField<FSetProperty>(*PropIt)){if (SetProperty->ElementProp && SetProperty->ElementProp->HasAllPropertyFlags(CPF_UObjectWrapper)){Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;}}else if(const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(*PropIt)){if(ArrayProperty->Inner && ArrayProperty->Inner->HasAllPropertyFlags(CPF_UObjectWrapper)){Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;}}else if (PropIt->HasAllPropertyFlags(CPF_UObjectWrapper)){Pin->PinType.bIsUObjectWrapper = 1;}}}}}}if (!Ar.IsObjectReferenceCollector()){// Don't validate the enabled state if the user has explicitly set it. Also skip validation if we're just duplicating this node.const bool bIsDuplicating = (Ar.GetPortFlags() & PPF_Duplicate) != 0;if (!bIsDuplicating && !HasUserSetTheEnabledState()){if (const UFunction* Function = GetTargetFunction()){// Enable as development-only if specified in metadata. This way existing functions that have the metadata added to them will get their enabled state fixed up on load.if (GetDesiredEnabledState() == ENodeEnabledState::Enabled && Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly)){SetEnabledState(ENodeEnabledState::DevelopmentOnly, /*bUserAction=*/ false);}// Ensure that if the metadata is removed, we also fix up the enabled state to avoid leaving it set as development-only in that case.else if (GetDesiredEnabledState() == ENodeEnabledState::DevelopmentOnly && !Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly)){SetEnabledState(ENodeEnabledState::Enabled, /*bUserAction=*/ false);}}}}}
}void UK2Node_CallFunction::PostPlacedNewNode()
{Super::PostPlacedNewNode();// Try re-setting the function given our new parent scope, in case it turns an external to an internal, or vis versaFunctionReference.RefreshGivenNewSelfScope<UFunction>(GetBlueprintClassFromNode());// Set the node to development only if the function specifies thatcheck(!HasUserSetTheEnabledState());if (const UFunction* Function = GetTargetFunction()){if (Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly)){SetEnabledState(ENodeEnabledState::DevelopmentOnly, /*bUserAction=*/ false);}}
}FNodeHandlingFunctor* UK2Node_CallFunction::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{return new FKCHandler_CallFunction(CompilerContext);
}void UK2Node_CallFunction::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{Super::ExpandNode(CompilerContext, SourceGraph);const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();UFunction* Function = GetTargetFunction();// connect DefaultToSelf and WorldContext inside static functions to proper 'self'  if (SourceGraph && Schema->IsStaticFunctionGraph(SourceGraph) && Function){TArray<UK2Node_FunctionEntry*> EntryPoints;SourceGraph->GetNodesOfClass(EntryPoints);if (1 != EntryPoints.Num()){CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("WrongEntryPointsNumFmt", "{0} entry points found while expanding node @@"), EntryPoints.Num()).ToString(), this);}else if (UEdGraphPin* BetterSelfPin = EntryPoints[0]->GetAutoWorldContextPin()){const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);const FString& WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);struct FStructConnectHelper{static void Connect(const FString& PinName, UK2Node* Node, UEdGraphPin* BetterSelf, const UEdGraphSchema_K2* InSchema, FCompilerResultsLog& MessageLog){UEdGraphPin* Pin = Node->FindPin(PinName);if (!PinName.IsEmpty() && Pin && !Pin->LinkedTo.Num()){const bool bConnected = InSchema->TryCreateConnection(Pin, BetterSelf);if (!bConnected){MessageLog.Warning(*LOCTEXT("DefaultToSelfNotConnected", "DefaultToSelf pin @@ from node @@ cannot be connected to @@").ToString(), Pin, Node, BetterSelf);}}}};FStructConnectHelper::Connect(DefaultToSelfMetaValue, this, BetterSelfPin, Schema, CompilerContext.MessageLog);if (!Function->HasMetaData(FBlueprintMetadata::MD_CallableWithoutWorldContext)){FStructConnectHelper::Connect(WorldContextMetaValue, this, BetterSelfPin, Schema, CompilerContext.MessageLog);}}}// If we have an enum param that is expanded, we handle that firstif(bWantsEnumToExecExpansion){if(Function){TArray<FName> EnumNamesToCheck;GetExpandEnumPinNames(Function, EnumNamesToCheck);bool bAlreadyHandleInput = false;UEdGraphPin* OutMainExecutePin = nullptr;UK2Node_ExecutionSequence* SpawnedSequenceNode = nullptr;int32 OutSequenceIndex = 0;const auto LinkIntoOutputChain = [&OutMainExecutePin, &SpawnedSequenceNode, &OutSequenceIndex, &CompilerContext, this, SourceGraph, Schema](UK2Node* Node){if (!OutMainExecutePin){// Create normal exec output -- only once though.OutMainExecutePin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);}else{// set up a sequence so we can call one after another.if (!SpawnedSequenceNode){SpawnedSequenceNode = CompilerContext.SpawnIntermediateNode<UK2Node_ExecutionSequence>(this, SourceGraph);SpawnedSequenceNode->AllocateDefaultPins();CompilerContext.MovePinLinksToIntermediate(*OutMainExecutePin, *SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex++));Schema->TryCreateConnection(OutMainExecutePin, SpawnedSequenceNode->Pins[0]);}}// Hook up execution to the branch nodeif (!SpawnedSequenceNode){Schema->TryCreateConnection(OutMainExecutePin, Node->GetExecPin());}else{UEdGraphPin* SequenceOutput = SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex);if (!SequenceOutput){SpawnedSequenceNode->AddInputPin();SequenceOutput = SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex);}Schema->TryCreateConnection(SequenceOutput, Node->GetExecPin());OutSequenceIndex++;}};for (const FName& EnumParamName : EnumNamesToCheck){UEnum* Enum = nullptr;if (FByteProperty* ByteProp = FindFProperty<FByteProperty>(Function, EnumParamName)){Enum = ByteProp->Enum;}else if (FEnumProperty* EnumProp = FindFProperty<FEnumProperty>(Function, EnumParamName)){Enum = EnumProp->GetEnum();}UEdGraphPin* EnumParamPin = FindPin(EnumParamName);if (Enum && EnumParamPin){// Expanded as input execs pinsif (EnumParamPin->Direction == EGPD_Input){if (bAlreadyHandleInput){CompilerContext.MessageLog.Error(TEXT("@@ Already provided an input enum parameter for ExpandEnumAsExecs. Only one is permitted."), this);return;}bAlreadyHandleInput = true;// Create normal exec inputUEdGraphPin* ExecutePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);// Create temp enum variableUK2Node_TemporaryVariable* TempEnumVarNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);TempEnumVarNode->VariableType.PinCategory = UEdGraphSchema_K2::PC_Byte;TempEnumVarNode->VariableType.PinSubCategoryObject = Enum;TempEnumVarNode->AllocateDefaultPins();// Get the output pinUEdGraphPin* TempEnumVarOutput = TempEnumVarNode->GetVariablePin();// Connect temp enum variable to (hidden) enum pinSchema->TryCreateConnection(TempEnumVarOutput, EnumParamPin);// Now we want to iterate over other exec inputs...for (int32 PinIdx = Pins.Num() - 1; PinIdx >= 0; PinIdx--){UEdGraphPin* Pin = Pins[PinIdx];if (Pin != NULL &&Pin != ExecutePin &&Pin->Direction == EGPD_Input &&Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec){// Create node to set the temp enum varUK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);AssignNode->AllocateDefaultPins();// Move connections from fake 'enum exec' pint to this assignment nodeCompilerContext.MovePinLinksToIntermediate(*Pin, *AssignNode->GetExecPin());// Connect this to out temp enum varSchema->TryCreateConnection(AssignNode->GetVariablePin(), TempEnumVarOutput);// Connect exec output to 'real' exec pinSchema->TryCreateConnection(AssignNode->GetThenPin(), ExecutePin);// set the literal enum value to set toAssignNode->GetValuePin()->DefaultValue = Pin->PinName.ToString();// Finally remove this 'cosmetic' exec pinPins[PinIdx]->MarkPendingKill();Pins.RemoveAt(PinIdx);}}}// Expanded as output execs pinselse if (EnumParamPin->Direction == EGPD_Output){// Create a SwitchEnum node to switch on the output enumUK2Node_SwitchEnum* SwitchEnumNode = CompilerContext.SpawnIntermediateNode<UK2Node_SwitchEnum>(this, SourceGraph);UEnum* EnumObject = Cast<UEnum>(EnumParamPin->PinType.PinSubCategoryObject.Get());SwitchEnumNode->SetEnum(EnumObject);SwitchEnumNode->AllocateDefaultPins();LinkIntoOutputChain(SwitchEnumNode);// Connect (hidden) enum pin to switch node's selection pinSchema->TryCreateConnection(EnumParamPin, SwitchEnumNode->GetSelectionPin());// Now we want to iterate over other exec outputs corresponding to the enum.// the first pins created are the ExpandEnumAsExecs pins, and they're all made at the same time.for (int32 PinIdx = Enum->NumEnums() - 2; PinIdx >= 0; PinIdx--){UEdGraphPin* Pin = Pins[PinIdx];if (Pin &&Pin != OutMainExecutePin &&Pin->Direction == EGPD_Output &&Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec){if (UEdGraphPin* FoundPin = SwitchEnumNode->FindPin(Pin->PinName)){if (!FoundPin->LinkedTo.Contains(Pin)){// Move connections from fake 'enum exec' pin to this switch nodeCompilerContext.MovePinLinksToIntermediate(*Pin, *FoundPin);// Finally remove this 'cosmetic' exec pinPins[PinIdx]->MarkPendingKill();Pins.RemoveAt(PinIdx);}}// Have passed the relevant entries... no more work to do here.else{break;}}}}}else if(EnumParamPin && !EnumParamPin->PinType.IsContainer() && EnumParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Boolean){if (EnumParamPin->Direction == EGPD_Input){// Create normal exec inputUEdGraphPin* ExecutePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);// Create temp bool variableUK2Node_TemporaryVariable* TempBoolVarNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);TempBoolVarNode->VariableType.PinCategory = UEdGraphSchema_K2::PC_Boolean;TempBoolVarNode->AllocateDefaultPins();// Get the output pinUEdGraphPin* TempBoolVarOutput = TempBoolVarNode->GetVariablePin();// Connect temp enum variable to (hidden) bool pinSchema->TryCreateConnection(TempBoolVarOutput, EnumParamPin);// create a true entry and a false:const auto CreateAssignNode = [Schema, &CompilerContext, this, SourceGraph, TempBoolVarOutput, ExecutePin](UEdGraphPin* FakePin, const TCHAR* DefaultValue){UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);AssignNode->AllocateDefaultPins();// Move connections from fake 'enum exec' pint to this assignment nodeCompilerContext.MovePinLinksToIntermediate(*FakePin, *AssignNode->GetExecPin());// Connect this to out temp enum varSchema->TryCreateConnection(AssignNode->GetVariablePin(), TempBoolVarOutput);// Connect exec output to 'real' exec pinSchema->TryCreateConnection(AssignNode->GetThenPin(), ExecutePin);// set the literal enum value to set toAssignNode->GetValuePin()->DefaultValue = DefaultValue;};UEdGraphPin* TruePin = FindPinChecked(TEXT("True"), EEdGraphPinDirection::EGPD_Input);UEdGraphPin* FalsePin = FindPinChecked(TEXT("False"), EEdGraphPinDirection::EGPD_Input);CreateAssignNode(TruePin, TEXT("True"));CreateAssignNode(FalsePin, TEXT("False"));// remove fake false/true nodes:RemovePin(TruePin);RemovePin(FalsePin);}else if (EnumParamPin->Direction == EGPD_Output){// Create a Branch node to switch on the output bool:UK2Node_IfThenElse* IfElseNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);IfElseNode->AllocateDefaultPins();LinkIntoOutputChain(IfElseNode);// Connect (hidden) bool pin to branch nodeSchema->TryCreateConnection(EnumParamPin, IfElseNode->GetConditionPin());UEdGraphPin* TruePin = FindPinChecked(TEXT("True"), EEdGraphPinDirection::EGPD_Output);UEdGraphPin* FalsePin = FindPinChecked(TEXT("False"), EEdGraphPinDirection::EGPD_Output);// move true connection to branch node:CompilerContext.MovePinLinksToIntermediate(*TruePin, *IfElseNode->GetThenPin());// move false connection to branch node:CompilerContext.MovePinLinksToIntermediate(*FalsePin, *IfElseNode->GetElsePin());// remove fake false/true nodes:RemovePin(TruePin);RemovePin(FalsePin);}}}}}// AUTO CREATED REFS{if ( Function ){TArray<FString> AutoCreateRefTermPinNames;const bool bHasAutoCreateRefTerms = Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm);if ( bHasAutoCreateRefTerms ){CompilerContext.GetSchema()->GetAutoEmitTermParameters(Function, AutoCreateRefTermPinNames);}for (UEdGraphPin* Pin : Pins){const bool bIsRefInputParam = Pin && Pin->PinType.bIsReference && (Pin->Direction == EGPD_Input) && !CompilerContext.GetSchema()->IsMetaPin(*Pin);if (!bIsRefInputParam){continue;}const bool bHasConnections = Pin->LinkedTo.Num() > 0;const bool bCreateDefaultValRefTerm = bHasAutoCreateRefTerms && !bHasConnections && AutoCreateRefTermPinNames.Contains(Pin->PinName.ToString());if (bCreateDefaultValRefTerm){const bool bHasDefaultValue = !Pin->DefaultValue.IsEmpty() || Pin->DefaultObject || !Pin->DefaultTextValue.IsEmpty();// copy defaults as default values can be reset when the pin is connectedconst FString DefaultValue = Pin->DefaultValue;UObject* DefaultObject = Pin->DefaultObject;const FText DefaultTextValue = Pin->DefaultTextValue;bool bMatchesDefaults = Pin->DoesDefaultValueMatchAutogenerated();UEdGraphPin* ValuePin = InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, bHasDefaultValue);if ( ValuePin ){if (bMatchesDefaults){// Use the latest code to set default valueSchema->SetPinAutogeneratedDefaultValueBasedOnType(ValuePin);}else{ValuePin->DefaultValue = DefaultValue;ValuePin->DefaultObject = DefaultObject;ValuePin->DefaultTextValue = DefaultTextValue;}}}// since EX_Self does not produce an addressable (referenceable) FProperty, we need to shim// in a "auto-ref" term in its place (this emulates how UHT generates a local value for // native functions; hence the IsNative() check)else if (bHasConnections && Pin->LinkedTo[0]->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self && Pin->PinType.bIsConst && !Function->IsNative()){InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, /*bForceAssignment =*/true);}}}}// Then we go through and expand out array iteration if necessaryconst bool bAllowMultipleSelfs = AllowMultipleSelfs(true);UEdGraphPin* MultiSelf = Schema->FindSelfPin(*this, EEdGraphPinDirection::EGPD_Input);if(bAllowMultipleSelfs && MultiSelf && !MultiSelf->PinType.IsArray()){const bool bProperInputToExpandForEach = (1 == MultiSelf->LinkedTo.Num()) && (nullptr != MultiSelf->LinkedTo[0]) && (MultiSelf->LinkedTo[0]->PinType.IsArray());if(bProperInputToExpandForEach){CallForEachElementInArrayExpansion(this, MultiSelf, CompilerContext, SourceGraph);}}
}UEdGraphPin* UK2Node_CallFunction::InnerHandleAutoCreateRef(UK2Node* Node, UEdGraphPin* Pin, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph, bool bForceAssignment)
{const bool bAddAssigment = !Pin->PinType.IsContainer() && bForceAssignment;// ADD LOCAL VARIABLEUK2Node_TemporaryVariable* LocalVariable = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);LocalVariable->VariableType = Pin->PinType;LocalVariable->VariableType.bIsReference = false;LocalVariable->AllocateDefaultPins();if (!bAddAssigment){if (!CompilerContext.GetSchema()->TryCreateConnection(LocalVariable->GetVariablePin(), Pin)){CompilerContext.MessageLog.Error(*LOCTEXT("AutoCreateRefTermPin_NotConnected", "AutoCreateRefTerm Expansion: Pin @@ cannot be connected to @@").ToString(), LocalVariable->GetVariablePin(), Pin);return nullptr;}}// ADD ASSIGMENTelse{// TODO connect to dest..UK2Node_PureAssignmentStatement* AssignDefaultValue = CompilerContext.SpawnIntermediateNode<UK2Node_PureAssignmentStatement>(Node, SourceGraph);AssignDefaultValue->AllocateDefaultPins();const bool bVariableConnected = CompilerContext.GetSchema()->TryCreateConnection(AssignDefaultValue->GetVariablePin(), LocalVariable->GetVariablePin());UEdGraphPin* AssignInputPit = AssignDefaultValue->GetValuePin();const bool bPreviousInputSaved = AssignInputPit && CompilerContext.MovePinLinksToIntermediate(*Pin, *AssignInputPit).CanSafeConnect();const bool bOutputConnected = CompilerContext.GetSchema()->TryCreateConnection(AssignDefaultValue->GetOutputPin(), Pin);if (!bVariableConnected || !bOutputConnected || !bPreviousInputSaved){CompilerContext.MessageLog.Error(*LOCTEXT("AutoCreateRefTermPin_AssignmentError", "AutoCreateRefTerm Expansion: Assignment Error @@").ToString(), AssignDefaultValue);return nullptr;}CompilerContext.GetSchema()->SetPinAutogeneratedDefaultValueBasedOnType(AssignDefaultValue->GetValuePin());return AssignInputPit;}return nullptr;
}void UK2Node_CallFunction::CallForEachElementInArrayExpansion(UK2Node* Node, UEdGraphPin* MultiSelf, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();check(Node && MultiSelf && SourceGraph && Schema);const bool bProperInputToExpandForEach = (1 == MultiSelf->LinkedTo.Num()) && (NULL != MultiSelf->LinkedTo[0]) && (MultiSelf->LinkedTo[0]->PinType.IsArray());ensure(bProperInputToExpandForEach);UEdGraphPin* ThenPin = Node->FindPinChecked(UEdGraphSchema_K2::PN_Then);// Create int IteratorUK2Node_TemporaryVariable* IteratorVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);IteratorVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Int;IteratorVar->AllocateDefaultPins();// Initialize iteratorUK2Node_AssignmentStatement* InteratorInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);InteratorInitialize->AllocateDefaultPins();InteratorInitialize->GetValuePin()->DefaultValue = TEXT("0");Schema->TryCreateConnection(IteratorVar->GetVariablePin(), InteratorInitialize->GetVariablePin());CompilerContext.MovePinLinksToIntermediate(*Node->GetExecPin(), *InteratorInitialize->GetExecPin());// Do loop branchUK2Node_IfThenElse* Branch = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(Node, SourceGraph);Branch->AllocateDefaultPins();Schema->TryCreateConnection(InteratorInitialize->GetThenPin(), Branch->GetExecPin());CompilerContext.MovePinLinksToIntermediate(*ThenPin, *Branch->GetElsePin());// Do loop conditionUK2Node_CallFunction* Condition = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph); Condition->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Less_IntInt)));Condition->AllocateDefaultPins();Schema->TryCreateConnection(Condition->GetReturnValuePin(), Branch->GetConditionPin());Schema->TryCreateConnection(Condition->FindPinChecked(TEXT("A")), IteratorVar->GetVariablePin());// Array sizeUK2Node_CallArrayFunction* ArrayLength = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(Node, SourceGraph); ArrayLength->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetArrayLibrary, Array_Length)));ArrayLength->AllocateDefaultPins();CompilerContext.CopyPinLinksToIntermediate(*MultiSelf, *ArrayLength->GetTargetArrayPin());ArrayLength->PinConnectionListChanged(ArrayLength->GetTargetArrayPin());Schema->TryCreateConnection(Condition->FindPinChecked(TEXT("B")), ArrayLength->GetReturnValuePin());// Get ElementUK2Node_CallArrayFunction* GetElement = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(Node, SourceGraph); GetElement->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetArrayLibrary, Array_Get)));GetElement->AllocateDefaultPins();CompilerContext.CopyPinLinksToIntermediate(*MultiSelf, *GetElement->GetTargetArrayPin());GetElement->PinConnectionListChanged(GetElement->GetTargetArrayPin());Schema->TryCreateConnection(GetElement->FindPinChecked(TEXT("Index")), IteratorVar->GetVariablePin());// Iterator incrementUK2Node_CallFunction* Increment = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph); Increment->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Add_IntInt)));Increment->AllocateDefaultPins();Schema->TryCreateConnection(Increment->FindPinChecked(TEXT("A")), IteratorVar->GetVariablePin());Increment->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("1");// Iterator assignedUK2Node_AssignmentStatement* IteratorAssign = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);IteratorAssign->AllocateDefaultPins();Schema->TryCreateConnection(IteratorAssign->GetVariablePin(), IteratorVar->GetVariablePin());Schema->TryCreateConnection(IteratorAssign->GetValuePin(), Increment->GetReturnValuePin());Schema->TryCreateConnection(IteratorAssign->GetThenPin(), Branch->GetExecPin());// Connect pins from intermediate nodes back in to the original nodeSchema->TryCreateConnection(Branch->GetThenPin(), Node->GetExecPin());Schema->TryCreateConnection(ThenPin, IteratorAssign->GetExecPin());Schema->TryCreateConnection(GetElement->FindPinChecked(TEXT("Item")), MultiSelf);
}FName UK2Node_CallFunction::GetCornerIcon() const
{if (const UFunction* Function = GetTargetFunction()){if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly)){return TEXT("Graph.Replication.AuthorityOnly");		}else if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic)){return TEXT("Graph.Replication.ClientEvent");}else if(Function->HasMetaData(FBlueprintMetadata::MD_Latent)){return TEXT("Graph.Latent.LatentIcon");}}return Super::GetCornerIcon();
}FSlateIcon UK2Node_CallFunction::GetIconAndTint(FLinearColor& OutColor) const
{return GetPaletteIconForFunction(GetTargetFunction(), OutColor);
}bool UK2Node_CallFunction::ReconnectPureExecPins(TArray<UEdGraphPin*>& OldPins)
{if (IsNodePure()){// look for an old exec pinUEdGraphPin* PinExec = nullptr;for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++){if (OldPins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Execute){PinExec = OldPins[PinIdx];break;}}if (PinExec){PinExec->SetSavePinIfOrphaned(false); // look for old then pinUEdGraphPin* PinThen = nullptr;for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++){if (OldPins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Then){PinThen = OldPins[PinIdx];break;}}if (PinThen){PinThen->SetSavePinIfOrphaned(false);// reconnect all incoming links to old exec pin to the far end of the old then pin.if (PinThen->LinkedTo.Num() > 0){UEdGraphPin* PinThenLinked = PinThen->LinkedTo[0];while (PinExec->LinkedTo.Num() > 0){UEdGraphPin* PinExecLinked = PinExec->LinkedTo[0];PinExecLinked->BreakLinkTo(PinExec);PinExecLinked->MakeLinkTo(PinThenLinked);}return true;}}}}return false;
}void UK2Node_CallFunction::InvalidatePinTooltips()
{bPinTooltipsValid = false;
}void UK2Node_CallFunction::ConformContainerPins()
{// helper functions for type propagation:const auto TryReadTypeToPropagate = [](UEdGraphPin* Pin, bool& bOutPropagated, FEdGraphTerminalType& TypeToPropagete){if (Pin && !bOutPropagated){if (Pin->HasAnyConnections() || !Pin->DoesDefaultValueMatchAutogenerated() ){bOutPropagated = true;if (Pin->LinkedTo.Num() != 0){TypeToPropagete = Pin->LinkedTo[0]->GetPrimaryTerminalType();}else{TypeToPropagete = Pin->GetPrimaryTerminalType();}}}};const auto TryReadValueTypeToPropagate = [](UEdGraphPin* Pin, bool& bOutPropagated, FEdGraphTerminalType& TypeToPropagete){if (Pin && !bOutPropagated){if (Pin->LinkedTo.Num() != 0 || !Pin->DoesDefaultValueMatchAutogenerated()){bOutPropagated = true;if (Pin->LinkedTo.Num() != 0){TypeToPropagete = Pin->LinkedTo[0]->PinType.PinValueType;}else{TypeToPropagete = Pin->PinType.PinValueType;}}}};const UEdGraphSchema_K2* Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());const auto TryPropagateType = [Schema](UEdGraphPin* Pin, const FEdGraphTerminalType& TerminalType, bool bTypeIsAvailable){if(Pin){if(bTypeIsAvailable){const FEdGraphTerminalType PrimaryType = Pin->GetPrimaryTerminalType();if( PrimaryType.TerminalCategory != TerminalType.TerminalCategory ||PrimaryType.TerminalSubCategory != TerminalType.TerminalSubCategory ||PrimaryType.TerminalSubCategoryObject != TerminalType.TerminalSubCategoryObject){// terminal type changed:if (Pin->SubPins.Num() > 0 && Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard){Schema->RecombinePin(Pin->SubPins[0]);}Pin->PinType.PinCategory = TerminalType.TerminalCategory;Pin->PinType.PinSubCategory = TerminalType.TerminalSubCategory;Pin->PinType.PinSubCategoryObject = TerminalType.TerminalSubCategoryObject;// Also propagate the CPF_UObjectWrapper flag, which will be set for "wrapped" object ptr types (e.g. TSubclassOf).Pin->PinType.bIsUObjectWrapper = TerminalType.bTerminalIsUObjectWrapper;// Reset default valuesif (!Schema->IsPinDefaultValid(Pin, Pin->DefaultValue, Pin->DefaultObject, Pin->DefaultTextValue).IsEmpty()){Schema->ResetPinToAutogeneratedDefaultValue(Pin, false);}}}else{// reset to wildcard:if (Pin->SubPins.Num() > 0){Schema->RecombinePin(Pin->SubPins[0]);}Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;Pin->PinType.PinSubCategory = NAME_None;Pin->PinType.PinSubCategoryObject = nullptr;Pin->PinType.bIsUObjectWrapper = false;Schema->ResetPinToAutogeneratedDefaultValue(Pin, false);}}};const auto TryPropagateValueType = [](UEdGraphPin* Pin, const FEdGraphTerminalType& TerminalType, bool bTypeIsAvailable){if (Pin){if (bTypeIsAvailable){Pin->PinType.PinValueType.TerminalCategory = TerminalType.TerminalCategory;Pin->PinType.PinValueType.TerminalSubCategory = TerminalType.TerminalSubCategory;Pin->PinType.PinValueType.TerminalSubCategoryObject = TerminalType.TerminalSubCategoryObject;}else{Pin->PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Wildcard;Pin->PinType.PinValueType.TerminalSubCategory = NAME_None;Pin->PinType.PinValueType.TerminalSubCategoryObject = nullptr;}}};const UFunction* TargetFunction = GetTargetFunction();if (TargetFunction == nullptr){return;}// find any pins marked as SetParamconst FString& SetPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_SetParam);// useless copies/allocates in this code, could be an optimization target...TArray<FString> SetParamPinGroups;{SetPinMetaData.ParseIntoArray(SetParamPinGroups, TEXT(","), true);}for (FString& Entry : SetParamPinGroups){// split the group:TArray<FString> GroupEntries;Entry.ParseIntoArray(GroupEntries, TEXT("|"), true);// resolve pinsTArray<UEdGraphPin*> ResolvedPins;for(UEdGraphPin* Pin : Pins){if (GroupEntries.Contains(Pin->GetName())){ResolvedPins.Add(Pin);}}// if nothing is connected (or non-default), reset to wildcard// else, find the first type and propagate to everyone else::bool bReadyToPropagatSetType = false;FEdGraphTerminalType TypeToPropagate;for (UEdGraphPin* Pin : ResolvedPins){TryReadTypeToPropagate(Pin, bReadyToPropagatSetType, TypeToPropagate);if(bReadyToPropagatSetType){break;}}for (UEdGraphPin* Pin : ResolvedPins){TryPropagateType( Pin, TypeToPropagate, bReadyToPropagatSetType );}}const FString& MapPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapParam);const FString& MapKeyPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapKeyParam);const FString& MapValuePinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapValueParam);if(!MapPinMetaData.IsEmpty() || !MapKeyPinMetaData.IsEmpty() || !MapValuePinMetaData.IsEmpty() ){// if the map pin has a connection infer from that, otherwise use the information on the key param and value param:bool bReadyToPropagateKeyType = false;FEdGraphTerminalType KeyTypeToPropagate;bool bReadyToPropagateValueType = false;FEdGraphTerminalType ValueTypeToPropagate;UEdGraphPin* MapPin = MapPinMetaData.IsEmpty() ? nullptr : FindPin(MapPinMetaData);UEdGraphPin* MapKeyPin = MapKeyPinMetaData.IsEmpty() ? nullptr : FindPin(MapKeyPinMetaData);UEdGraphPin* MapValuePin = MapValuePinMetaData.IsEmpty() ? nullptr : FindPin(MapValuePinMetaData);TryReadTypeToPropagate(MapPin, bReadyToPropagateKeyType, KeyTypeToPropagate);TryReadValueTypeToPropagate(MapPin, bReadyToPropagateValueType, ValueTypeToPropagate);TryReadTypeToPropagate(MapKeyPin, bReadyToPropagateKeyType, KeyTypeToPropagate);TryReadTypeToPropagate(MapValuePin, bReadyToPropagateValueType, ValueTypeToPropagate);TryPropagateType(MapPin, KeyTypeToPropagate, bReadyToPropagateKeyType);TryPropagateType(MapKeyPin, KeyTypeToPropagate, bReadyToPropagateKeyType);TryPropagateValueType(MapPin, ValueTypeToPropagate, bReadyToPropagateValueType);TryPropagateType(MapValuePin, ValueTypeToPropagate, bReadyToPropagateValueType);}
}FText UK2Node_CallFunction::GetToolTipHeading() const
{FText Heading = Super::GetToolTipHeading();struct FHeadingBuilder{FHeadingBuilder(FText InitialHeading) : ConstructedHeading(InitialHeading) {}void Append(FText HeadingAddOn){if (ConstructedHeading.IsEmpty()){ConstructedHeading = HeadingAddOn;}else {ConstructedHeading = FText::Format(FText::FromString("{0}\n{1}"), HeadingAddOn, ConstructedHeading);}}FText ConstructedHeading;};FHeadingBuilder HeadingBuilder(Super::GetToolTipHeading());if (const UFunction* Function = GetTargetFunction()){if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly)){HeadingBuilder.Append(LOCTEXT("ServerOnlyFunc", "Server Only"));	}if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic)){HeadingBuilder.Append(LOCTEXT("ClientOnlyFunc", "Client Only"));}if(Function->HasMetaData(FBlueprintMetadata::MD_Latent)){HeadingBuilder.Append(LOCTEXT("LatentFunc", "Latent"));}}return HeadingBuilder.ConstructedHeading;
}void UK2Node_CallFunction::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{UFunction* TargetFunction = GetTargetFunction();const FString TargetFunctionName = TargetFunction ? TargetFunction->GetName() : TEXT( "InvalidFunction" );OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "Function" ) ));OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), TargetFunctionName ));
}FText UK2Node_CallFunction::GetMenuCategory() const
{UFunction* TargetFunction = GetTargetFunction();if (TargetFunction != nullptr){return GetDefaultCategoryForFunction(TargetFunction, FText::GetEmpty());}return FText::GetEmpty();
}bool UK2Node_CallFunction::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{UFunction* Function = GetTargetFunction();const UClass* SourceClass = Function ? Function->GetOwnerClass() : nullptr;const UBlueprint* SourceBlueprint = GetBlueprint();bool bResult = (SourceClass != nullptr) && (SourceClass->ClassGeneratedBy != SourceBlueprint);if (bResult && OptionalOutput){OptionalOutput->AddUnique(Function);}// All structures, that are required for the BP compilation, should be gatheredfor (UEdGraphPin* Pin : Pins){UStruct* DepStruct = Pin ? Cast<UStruct>(Pin->PinType.PinSubCategoryObject.Get()) : nullptr;UClass* DepClass = Cast<UClass>(DepStruct);if (DepClass && (DepClass->ClassGeneratedBy == SourceBlueprint)){//Don't include selfcontinue;}if (DepStruct && !DepStruct->IsNative()){if (OptionalOutput){OptionalOutput->AddUnique(DepStruct);}bResult = true;}}const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);return bSuperResult || bResult;
}UEdGraph* UK2Node_CallFunction::GetFunctionGraph(const UEdGraphNode*& OutGraphNode) const
{OutGraphNode = nullptr;// Search for the Blueprint owner of the function graph, climbing up through the Blueprint hierarchyUClass* MemberParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());if(MemberParentClass != nullptr){UBlueprintGeneratedClass* ParentClass = Cast<UBlueprintGeneratedClass>(MemberParentClass);if(ParentClass != nullptr && ParentClass->ClassGeneratedBy != nullptr){UBlueprint* Blueprint = Cast<UBlueprint>(ParentClass->ClassGeneratedBy);while(Blueprint != nullptr){UEdGraph* TargetGraph = nullptr;const FName FunctionName = FunctionReference.GetMemberName();for (UEdGraph* const Graph : Blueprint->FunctionGraphs) {if (Graph->GetFName() == FunctionName){TargetGraph = Graph;break;}}if (!TargetGraph){for (const FBPInterfaceDescription& Interface : Blueprint->ImplementedInterfaces){for (UEdGraph* const Graph : Interface.Graphs){if (Graph->GetFName() == FunctionName){TargetGraph = Graph;break;}}if (TargetGraph){break;}}}if((TargetGraph != nullptr) && !TargetGraph->HasAnyFlags(RF_Transient)){// Found the function graph in a Blueprint, return that graphreturn TargetGraph;}else{// Did not find the function call as a graph, it may be a custom eventUK2Node_CustomEvent* CustomEventNode = nullptr;TArray<UK2Node_CustomEvent*> CustomEventNodes;FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, CustomEventNodes);for (UK2Node_CustomEvent* const CustomEvent : CustomEventNodes){if(CustomEvent->CustomFunctionName == FunctionReference.GetMemberName()){OutGraphNode = CustomEvent;return CustomEvent->GetGraph();}}}ParentClass = Cast<UBlueprintGeneratedClass>(Blueprint->ParentClass);Blueprint = ParentClass != nullptr ? Cast<UBlueprint>(ParentClass->ClassGeneratedBy) : nullptr;}}}return nullptr;
}bool UK2Node_CallFunction::IsStructureWildcardProperty(const UFunction* Function, const FName PropertyName)
{if (Function && !PropertyName.IsNone()){TArray<FString> Names;FCustomStructureParamHelper::FillCustomStructureParameterNames(Function, Names);if (Names.Contains(PropertyName.ToString())){return true;}}return false;
}bool UK2Node_CallFunction::IsWildcardProperty(const UFunction* InFunction, const FProperty* InProperty)
{if (InProperty){return FEdGraphUtilities::IsSetParam(InFunction, InProperty->GetFName()) || FEdGraphUtilities::IsMapParam(InFunction, InProperty->GetFName());}return false;
}void UK2Node_CallFunction::AddSearchMetaDataInfo(TArray<struct FSearchTagDataPair>& OutTaggedMetaData) const
{Super::AddSearchMetaDataInfo(OutTaggedMetaData);if (UFunction* TargetFunction = GetTargetFunction()){OutTaggedMetaData.Add(FSearchTagDataPair(FFindInBlueprintSearchTags::FiB_NativeName, FText::FromString(TargetFunction->GetName())));}
}TSharedPtr<SWidget> UK2Node_CallFunction::CreateNodeImage() const
{// For set, map and array functions we have a cool icon. This helps users quickly// identify container types:if (UFunction* TargetFunction = GetTargetFunction()){UEdGraphPin* NodeImagePin = FEdGraphUtilities::FindArrayParamPin(TargetFunction, this);NodeImagePin = NodeImagePin ? NodeImagePin : FEdGraphUtilities::FindSetParamPin(TargetFunction, this);NodeImagePin = NodeImagePin ? NodeImagePin : FEdGraphUtilities::FindMapParamPin(TargetFunction, this);if(NodeImagePin){// Find the first array param pin and bind that to our array image:return SPinTypeSelector::ConstructPinTypeImage(NodeImagePin);}}return TSharedPtr<SWidget>();
}UObject* UK2Node_CallFunction::GetJumpTargetForDoubleClick() const
{// If there is an event node, jump to it, otherwise jump to the function graphconst UEdGraphNode* ResultEventNode = nullptr;UEdGraph* FunctionGraph = GetFunctionGraph(/*out*/ ResultEventNode);if (ResultEventNode != nullptr){return const_cast<UEdGraphNode*>(ResultEventNode);}else{return FunctionGraph;}
}bool UK2Node_CallFunction::CanJumpToDefinition() const
{const UFunction* TargetFunction = GetTargetFunction();const bool bNativeFunction = (TargetFunction != nullptr) && (TargetFunction->IsNative());return bNativeFunction || (GetJumpTargetForDoubleClick() != nullptr);
}void UK2Node_CallFunction::JumpToDefinition() const
{// For native functions, try going to the function definition in C++ if availableif (UFunction* TargetFunction = GetTargetFunction()){if (TargetFunction->IsNative()){// First try the nice way that will get to the right line numberbool bSucceeded = false;const bool bNavigateToNativeFunctions = GetDefault<UBlueprintEditorSettings>()->bNavigateToNativeFunctionsFromCallNodes;if(bNavigateToNativeFunctions) {if(FSourceCodeNavigation::CanNavigateToFunction(TargetFunction)){bSucceeded = FSourceCodeNavigation::NavigateToFunction(TargetFunction);}// Failing that, fall back to the older method which will still get the file open assuming it existsif (!bSucceeded){FString NativeParentClassHeaderPath;const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(TargetFunction, NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE);if (bFileFound){const FString AbsNativeParentClassHeaderPath = FPaths::ConvertRelativePathToFull(NativeParentClassHeaderPath);bSucceeded = FSourceCodeNavigation::OpenSourceFile(AbsNativeParentClassHeaderPath);}}}else{	// Inform user that the function is native, give them opportunity to enable navigation to native// functions:FNotificationInfo Info(LOCTEXT("NavigateToNativeDisabled", "Navigation to Native (c++) Functions Disabled"));Info.ExpireDuration = 10.0f;Info.CheckBoxState = bNavigateToNativeFunctions ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;Info.CheckBoxStateChanged = FOnCheckStateChanged::CreateStatic([](ECheckBoxState NewState){const FScopedTransaction Transaction(LOCTEXT("ChangeNavigateToNativeFunctionsFromCallNodes", "Change Navigate to Native Functions from Call Nodes Setting"));UBlueprintEditorSettings* MutableEditorSetings = GetMutableDefault<UBlueprintEditorSettings>();MutableEditorSetings->Modify();MutableEditorSetings->bNavigateToNativeFunctionsFromCallNodes = (NewState == ECheckBoxState::Checked) ? true : false;MutableEditorSetings->SaveConfig();});Info.CheckBoxText = LOCTEXT("EnableNavigationToNative", "Navigate to Native Functions from Blueprint Call Nodes?");FSlateNotificationManager::Get().AddNotification(Info);}return;}}// Otherwise, fall back to the inherited behavior which should go to the function entry nodeSuper::JumpToDefinition();
}FString UK2Node_CallFunction::GetPinMetaData(FName InPinName, FName InKey)
{FString MetaData = Super::GetPinMetaData(InPinName, InKey);// If there's no metadata directly on the pin then check for metadata on the functionif (MetaData.IsEmpty()){if (UFunction* Function = GetTargetFunction()){// Find the corresponding property for the pinif (FProperty* Property = Function->FindPropertyByName(InPinName)){MetaData = Property->GetMetaData(InKey);}}}return MetaData;
}bool UK2Node_CallFunction::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{bool bIsDisallowed = Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);if (!bIsDisallowed && MyPin != nullptr){if (MyPin->bNotConnectable){bIsDisallowed = true;OutReason = LOCTEXT("PinConnectionDisallowed", "This parameter is for internal use only.").ToString();}else if (UFunction* TargetFunction = GetTargetFunction()){const bool bIsObjectType = (MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject) &&(OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject);if (// Strictly speaking this first check is not needed, but by not disabling the connection here we get a better reason later:(	OtherPin->PinType.IsContainer() // make sure we don't allow connections of mismatched container types (e.g. maps to arrays)&& (OtherPin->PinType.ContainerType != MyPin->PinType.ContainerType)&& ((FEdGraphUtilities::IsSetParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsSet()) ||(FEdGraphUtilities::IsMapParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsMap()) ||(FEdGraphUtilities::IsArrayDependentParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsArray())))){bIsDisallowed = true;OutReason = LOCTEXT("PinSetConnectionDisallowed", "Containers of containers are not supported - consider wrapping a container in a Structure object").ToString();}// Do not allow exec pins to be connected to a wildcard if this is a container functionelse if(MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec){bIsDisallowed = true;OutReason = LOCTEXT("PinExecConnectionDisallowed", "Cannot create a container of Exec pins.").ToString();}else if (bIsObjectType && MyPin->Direction == EGPD_Input && MyPin->PinType.IsContainer() && OtherPin->PinType.IsContainer()){// Check that we can actually connect the dependent pins to this new arrayconst UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema());// Gather all pins that would be dependent on on the container typeTArray<UEdGraphPin*> DependentPins;{for (UEdGraphPin* Pin : Pins){if (Pin->Direction == EGPD_Input && Pin != MyPin && FEdGraphUtilities::IsDynamicContainerParam(TargetFunction, Pin->PinName)){DependentPins.Add(Pin);}}}for (UEdGraphPin* Pin : DependentPins){// If the pins are both containers, then ArePinTypesCompatible will fail incorrectly.if (OtherPin->PinType.ContainerType != Pin->PinType.ContainerType){continue;}UClass* Context = nullptr;UBlueprint* Blueprint = GetBlueprint();if (Blueprint){Context = Blueprint->GeneratedClass;}const bool ConnectResponse = K2Schema->ArePinTypesCompatible(Pin->PinType, OtherPin->PinType, Context, /* bIgnoreArray = */ true);if (!ConnectResponse){// For sets, we have to check if the other pin is a valid child that can actually // be connected in cases like the "Union" functionUStruct const* OutputObject = (OtherPin->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? Context : Cast<UStruct>(OtherPin->PinType.PinSubCategoryObject.Get());UStruct const* InputObject = (Pin->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? Context : Cast<UStruct>(Pin->PinType.PinSubCategoryObject.Get());if (OtherPin->PinType.IsSet() && OutputObject && InputObject && OutputObject->IsChildOf(InputObject)){bIsDisallowed = false;}else{// Display the necessary tooltip on the pin hover, and log it if we are compilingFFormatNamedArguments MessageArgs;MessageArgs.Add(TEXT("PinAType"), UEdGraphSchema_K2::TypeToText(Pin->PinType));MessageArgs.Add(TEXT("PinBType"), UEdGraphSchema_K2::TypeToText(OtherPin->PinType));UBlueprint* BP = GetBlueprint();UEdGraph* OwningGraph = GetGraph();OutReason = FText::Format(LOCTEXT("DefaultPinIncompatibilityMessage", "{PinAType} is not compatible with {PinBType}."), MessageArgs).ToString();return true;}}}}}}return bIsDisallowed;
}

相关文章:

UE蓝图 函数调用(CallFunction)节点和源码

系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 UE蓝图 返回结果(FunctionResult)节点和源码 UE蓝图 函数调用(CallFunction)节点和源码 文章目录 系列文章目录一、Call…...

Vue单文件学习项目综合案例Demo,黑马vue教程

文章目录 前言一、小黑记事本二、购物车三、小黑记账清单 前言 bilibili视频地址 一、小黑记事本 效果图 主代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"/><meta http-equiv"X-UA-Compatible&…...

机器视觉【3】非线性求解相机几何参数

线性求解相机几何参数的缺点 上一章节介绍学习了&#xff08;DLT&#xff09;线性求解相机几何参数&#xff0c;了解到线性求解法当中比较明显的缺点&#xff1a; 没有考虑到镜头畸变的影响不能引入更多的约束条件融入到DLT算法当中优化最关键的是&#xff0c;代数距离并不是…...

Qt编译报错:The slot requires more arguments than the signal provides.

编译时代码没有提示错误的地方&#xff0c;报错的地方在qt的文件&#xff0c;还以为什么莫名其妙的错误呢&#xff0c;原来就是连接的信号和槽函数参数不匹配&#xff0c;有个信号是没有参数的&#xff0c;但我的槽函数有个参数&#xff0c;然后就报错了。 改下槽函数的参数就…...

【Unity】提示No valid Unity Editor liscense found.Please active your liscense.

有两个软件&#xff0c;如果只有一个&#xff0c;点黑的不会有效果、、、、&#xff08;楼主是这个原因&#xff0c;可以对号入座一下&#xff09; 简而言之&#xff0c;就是去下载Unity Hub&#xff0c;再里面激活管理通行证 问题情境&#xff1a; 点击unity出现以下弹窗&a…...

如何在 Tomcat 中为 Web 应用程序启用和配置缓存?

在Tomcat中为Web应用程序启用和配置缓存通常涉及到对Tomcat的连接器&#xff08;Connector&#xff09;进行配置&#xff0c;以及可能的话&#xff0c;配置Web应用程序本身以支持缓存。 1. 配置Tomcat连接器以启用缓存 Tomcat的连接器可以通过其配置来启用各种…...

QEMU开发入门

1. 简介 QEMU&#xff08;Quick EMUlator&#xff09;是一个开源的虚拟化软件&#xff0c;它能够模拟多种硬件平台&#xff0c;并在这些平台上运行各种操作系统。QEMU可以在不同的主机架构之间进行虚拟化&#xff0c;例如x86、ARM、PowerPC、Risc-V等。QEMU是一个功能强大且灵…...

10-pytorch-完整模型训练

b站小土堆pytorch教程学习笔记 一、从零开始构建自己的神经网络 1.模型构建 #准备数据集 import torch import torchvision from torch.utils.tensorboard import SummaryWriterfrom model import * from torch.utils.data import DataLoadertrain_datatorchvision.datasets.…...

高级RAG:重新排名,从原理到实现的两种主流方法

原文地址&#xff1a;https://pub.towardsai.net/advanced-rag-04-re-ranking-85f6ae8170b1 2024 年 2 月 14 日 重新排序在检索增强生成&#xff08;RAG&#xff09;过程中起着至关重要的作用。在简单的 RAG 方法中&#xff0c;可以检索大量上下文&#xff0c;但并非所有上下…...

使用logicflow流程图实例

一.背景 需要使用流程引擎开发项目&#xff0c;没有使用flowable、activiti这类的国外流程引擎&#xff0c;想使用国内的引擎二次开发&#xff0c;缺少单例模式的流程画图程序&#xff0c;都是vue、react、angluer的不适合&#xff0c;从网上找了antx6、logicflow、bpmn.js。感…...

Stable Diffusion 绘画入门教程(webui)-ControlNet(IP2P)

上篇文章介绍了深度Depth&#xff0c;这篇文章介绍下IP2P&#xff08;InstructP2P&#xff09;, 通俗理解就是图生图&#xff0c;给原有图加一些效果,比如下图&#xff0c;左边为原图&#xff0c;右边为增加了效果的图&#xff1a; 文章目录 一、选大模型二、写提示词三、基础参…...

五力分析(Porter‘s Five Forces)

五力分析是一种用于评估竞争力的框架&#xff0c;由哈佛商学院教授迈克尔波特&#xff08;Michael Porter&#xff09;提出。它通过分析一个行业的五个关键力量&#xff08;竞争对手、供应商、顾客、替代品和新进入者&#xff09;来评估一个企业或行业的竞争环境。这个框架可以…...

十一、Qt数据库操作

一、Sql介绍 Qt Sql模块包含多个类&#xff0c;实现数据库的连接&#xff0c;Sql语句的执行&#xff0c;数据获取与界面显示&#xff0c;数据与界面直接使用Model/View结构。1、使用Sql模块 &#xff08;1&#xff09;工程加入 QT sql&#xff08;2&#xff09;添加头文件 …...

【Spring】IoC容器 控制反转 与 DI依赖注入 XML实现版本 第二期

文章目录 基于 XML 配置方式组件管理前置 准备项目一、 组件&#xff08;Bean&#xff09;信息声明配置&#xff08;IoC&#xff09;&#xff1a;1.1 基于无参构造1.2 基于静态 工厂方法实例化1.3 基于非静态 工厂方法实例化 二、 组件&#xff08;Bean&#xff09;依赖注入配置…...

神经网络系列---感知机(Neuron)

文章目录 感知机(Neuron)感知机(Neuron)的决策函数可以表示为&#xff1a;感知机(Neuron)的学习算法主要包括以下步骤&#xff1a;感知机可以实现逻辑运算中的AND、OR、NOT和异或(XOR)运算。 感知机(Neuron) 感知机(Neuron)是一种简单而有效的二分类算法&#xff0c;用于将输入…...

k8s(2)

目录 一.二进制部署k8s 常见的K8S安装部署方式&#xff1a; k8s部署 二进制与高可用的区别 二.部署k8s 初始化操作&#xff1a; 每台node安装docker&#xff1a; 在 master01 节点上操作; 准备cfssl证书生成工具:&#xff1a; 执行脚本文件&#xff1a; 拉入etcd压缩包…...

利用nginx内部访问特性实现静态资源授权访问

在nginx中&#xff0c;将静态资源设为internal&#xff1b;然后将前端的静态资源地址改为指向后端&#xff0c;在后端的响应头部中写上静态资源地址。 近期客户对我们项目做安全性测评&#xff0c;暴露出一些安全性问题&#xff0c;其中一个是有些静态页面&#xff08;*.html&…...

fly-barrage 前端弹幕库(1):项目介绍

fly-barrage 是我写的一个前端弹幕库&#xff0c;由于经常在 Bilibili 上看视频&#xff0c;所以对网页的弹幕功能一直蛮感兴趣的&#xff0c;所以做了这个库&#xff0c;可以帮助前端快速的实现弹幕功能。 项目官网地址&#xff1a;https://fly-barrage.netlify.app/&#xff…...

jetcache如果一个主体涉及多个缓存时编辑或者删除时如何同时失效多个缓存

在实际使用过程中&#xff0c;可能会遇到这种情形&#xff1a;一个主体会有多个缓存&#xff0c;比如用户基础信息缓存、用户详情缓存&#xff0c;那么当删除用户信息后就需要同时失效多个缓存中该主体数据&#xff0c;那么jetcache支持这种应用场景么&#xff0c;答案是支持&a…...

uni-app 实现拍照后给照片加水印功能

遇到个需求需要实现&#xff0c;研究了一下后写了个demo 本质上就是把拍完照后的照片放到canvas里&#xff0c;然后加上水印样式然后再重新生成一张图片 代码如下&#xff0c;看注释即可~使用的话记得还是得优化下代码 <template><view class"content"&g…...

【ArcGIS】利用DEM进行水文分析:流向/流量等

利用DEM进行水文分析 ArcGIS实例参考 水文分析通过建立地表水文模型&#xff0c;研究与地表水流相关的各种自然现象&#xff0c;在城市和区域规划、农业及森林、交通道路等许多领域具有广泛的应用。 ArcGIS实例 某流域30m分辨率DEM如下&#xff1a; &#xff08;1&#xff09…...

论文阅读笔记——PathAFL:Path-Coverage Assisted Fuzzing

文章目录 前言PathAFL&#xff1a;Path-Coverage Assisted Fuzzing1、解决的问题和目标2、技术路线2.1、如何识别 h − p a t h h-path h−path&#xff1f;2.2、如何减少 h − p a t h h-path h−path的数量&#xff1f;2.3、哪些h-path将被添加到种子队列&#xff1f;2.4、种…...

C语言中各种运算符用法

C语言中有许多不同的运算符&#xff0c;用于执行各种不同的操作。 以下是C语言中常见的运算符及其用法&#xff1a; 算术运算符&#xff1a; 加法运算符&#xff08;&#xff09;&#xff1a;用于将两个值相加。减法运算符&#xff08;-&#xff09;&#xff1a;用于将一个值减…...

pythonJax小记(五):python: 使用Jax深度图像(正交投影和透视投影之间的转换)(持续更新,评论区可以补充)

python: 使用Jax深度图像&#xff08;正交投影和透视投影之间的转换&#xff09; 前言问题描述1. 透视投影2. 正交投影 直接上代码解释1. compute_projection_parameters 函数a. 参数解释b. 函数计算 2. ortho_to_persp 函数a. 计算投影参数&#xff1a;b. 生成像素坐标网格&am…...

web安全学习笔记【16】——信息打点(6)

信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等[1] #知识点&#xff1a; 1、业务资产-应用类型分类 2、Web单域名获取-接口查询 3、Web子域名获取-解析枚举 4、Web架构资产-平台指纹识别 ------------------------------------ 1、开源-C…...

145.二叉树的后序遍历

// 定义一个名为Solution的类&#xff0c;用于解决二叉树的后序遍历问题 class Solution { // 定义一个公共方法&#xff0c;输入是一个二叉树的根节点&#xff0c;返回一个包含后序遍历结果的整数列表 public List<Integer> postorderTraversal(TreeNode root) { /…...

ssh远程连接免密码访问

我们在远程登录的时候&#xff0c;经常需要输入密码&#xff0c;密码往往比较复杂&#xff0c;输入比较耗费时间&#xff0c;这种情况下可以使用ssh免密码登录。 一般的教程是需要生成ssh密钥后&#xff0c;然后把密钥复制到server端完成配置&#xff0c;这里提供一个简单的方…...

Vue-Json-Schema-Form: 如何基于模板定制前端页面

本人从事的是工业物联网, 面对工业设备的通讯难题是各大设备都有各自的通讯协议, 如果想要用一款硬件去和所有设备做通讯的话, 就得面对怎么把自己想要采集的配置下发给自己的采集器的问题, 以前都是采用各种模型去尝试构建配置项, 但是因为配置可能会有深层次嵌套, 而且…...

保存Json对象到数据库

文章目录 背景实现方式1. 直接以 Json 对象保存到数据库2. 以 String 类型保存到数据库 背景 项目过程中可能需要保存 Json 对象到数据库中。 实现方式 有两种实现方式&#xff0c;一种是直接保存 Json 对象到数据库&#xff0c;这种方式在创建实体类以及编写 Mapper XML 脚本…...

《Docker 简易速速上手小册》第3章 Dockerfile 与镜像构建(2024 最新版)

文章目录 3.1 编写 Dockerfile3.1.1 重点基础知识3.1.2 重点案例&#xff1a;创建简单 Python 应用的 Docker 镜像3.1.3 拓展案例 1&#xff1a;Dockerfile 优化3.1.4 拓展案例 2&#xff1a;多阶段构建 3.2 构建流程深入解析3.2.1 重点基础知识3.2.2 重点案例&#xff1a;构建…...