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

系列文章目录

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/701372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android14之input高级调试技巧(一百八十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【GPTs分享】GPTs分享之consensus

大家好&#xff0c;元宵节快乐&#xff0c;今天给大家分享的GPTs是consensus。consensu号称无需关键字即可搜索2亿文章&#xff0c;而且给出的链接绝对保真&#xff0c;不再是胡编乱造的&#xff0c;而且能够根据指定主题辅助编写论文或者博客。 简介 consensus使用chat.cons…

SQL-Labs靶场“46-50”关通关教程

君衍. 一、四十六关 ORDER BY数字型注入1、源码分析2、rand()盲注3、if语句盲注4、时间盲注5、报错注入6、Limit注入 二、四十七关 ORDER BY单引号报错注入1、源码分析2、报错注入3、时间盲注 三、四十八关 ODRER BY数字型盲注1、源码分析2、rand()盲注3、if语句盲注4、时间盲注…

东方博宜 1518. 多项式求和

东方博宜 1518. 多项式求和 #include<iostream> using namespace std ; int main() {int n ;cin >> n ;int s 0 ;int m 1 ;for(int i 1 ; i < n ; i){ int n 1 ;for( int j 1 ; j < i ; j)n n*j ;s s n*m ;m m*(-1) ;}cout << s ;return 0 ;…

【Android】View 与 ViewGroup

View 是 Android 所有控件的基类&#xff0c;我们平常所用的 TextView 和 ImageView 都是继承自 View 的&#xff0c;源码如下&#xff1a; public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {... }public class ImageView extends View {.…

前端工程化面试题 | 18.精选前端工程化高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Unity资源加密解决方案

据统计&#xff0c;全球范围内超过50%的游戏均使用Unity创作而成&#xff0c;作为游戏开发市场第一大游戏引擎占有者&#xff0c;Unity已经全面覆盖到各个游戏平台。 全球游戏引擎市场占有率 由于体量庞大&#xff0c;Unity游戏已成为受游戏黑灰产攻击的重灾区&#xff0c;因游…

智慧库室管控系统-部队装备综合管理系统

项目背景 在建设万兆主干、千兆终端的监控专网的基础上&#xff0c;弹药库安全技术防范系统主要由库室管控平台、视频监控、入侵报警、出入控制&#xff0c;钥匙柜管理&#xff0c;环境监测、报警响应等子系统组成&#xff0c;采用不间断电源供电&#xff0c;在各系统独立运行…

【Linux】 faillock 命令使用

faillock 命令 faillock 命令是 PAM (Pluggable Authentication Modules) 的一部分&#xff0c;它被设计用来跟踪失败的登录尝试&#xff0c;并在连续失败尝试超过某个阈值时锁定账户。这个功能可以帮助系统管理员识别和防止暴力破解攻击。当一个用户连续多次输入错误的密码后&…

WIFI EEPROM 简略分析-MT7628 EEPROM ANALYSIS

经常做WIFI的同学都了解,硬件设计完成后经过射频的测试满足设计要求后,在量产以及生产中都需要对WIFI的射频需要校准,保证产品射频输出功率的一致性。 在开发阶段就必须需要了解WIFI的EEPROM都是一些什么参数,一般在硬件开发阶段会拿到芯片厂家都SDK,里面都包含对EEPROM的…

[SpringDataMongodb开发游戏服务器实战]

背景&#xff1a; xdb其实足够完美了&#xff0c;现在回想一下&#xff0c;觉得有点复杂&#xff0c;我们不应该绑定语言&#xff0c;最好有自己的架构思路。 七号堡垒作为成功的商业项目&#xff0c;告诉我&#xff1a;其实数据是多读少写的&#xff0c;有修改的时候直接改库也…

C语言:指针的进阶讲解

目录 1. 二级指针 1.1 二级指针是什么&#xff1f; 1.2 二级指针的作用 2. 一维数组和二维数组的本质 3. 指针数组 4. 数组指针 5. 函数指针 6. typedef的使用 7. 函数指针数组 7.1 转移表 1. 二级指针 如果了解了一级指针&#xff0c;那二级指针也是可以很好的理解…

windows安装onlyoffice8.0

安装erlang 安装Erlang25.3 下载地址 设置环境变量 ERLANG_HOME C:\Program Files\Erlang OTP Path下设置%ERLANG_HOME%\bin 打开cmd输入erl不报错即可 安装rabbitmq rabbitmq和erlang对应的关系 下载地址 执行完exe文件后&#xff0c;找到安装目录下的sbin&am…

网络原理——HTTP

1. 什么是HTTP协议 HTTP是应用层的协议。Java最主要的应用场景是做网站&#xff0c;而网站由 后端&#xff08;HTTP服务器&#xff09; 和 前端&#xff08;浏览器&#xff09;组成&#xff0c;HTTP协议就是负责这里后端和前端的数据交互。 HTTP3.0 之前在传输层是通过 TCP传…

Linux:Jenkins:GitLab+Maven+Jenkins的部署——离线包rpm版

Linux&#xff1a;Jenkins&#xff1a;GitLabMavenJenkins的部署&#xff08;1&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136157018?spm1001.2014.3001.5501这个是原版文章&#xff0c;均是使用的在线情况&#xff0c;但是不排除会有离线部署&am…

性格正直的人适合什么职业?

有信仰&#xff0c;有责任&#xff0c;有骨气&#xff0c;有尊严&#xff0c;这应该是大多数人对正直的人的理解&#xff0c;他们的心中有信仰&#xff0c;肩上有责任&#xff0c;灵魂有骨气&#xff0c;头上有尊严&#xff0c;不管在什么时候都能够坚守道德准则&#xff0c;不…

数字信号处理:傅里叶分析

本文主要参考视频如下&#xff1a; 数字信号处理9-1_线性时不变系统对复指数信号的响应_哔哩哔哩_bilibili 傅里叶分析的主要研究内容如下所示&#xff1a; 注意&#xff0c;计算机中使用的离散傅里叶变换并不是离散时间傅里叶变换&#xff1b; 前四种都是理论上的变换方式&…

nginx-------- 高性能的 Web服务端 验证模块 页面配置(三)

一、http设置 1.1 验证模块 需要输入用户名和密码 htpasswd 此命令来自于 httpd-tools 包&#xff0c;如果没有安装 安装一下即可 也可以安装httpd 直接yum install httpd -y 也一样 第一次生成文件htpasswd -c 文件路径 姓名 交互式生成密码 htpasswd -bc 文…

ubantu设置mysql开机启动

阅读本文之前请参阅----MySQL 数据库安装教程详解&#xff08;linux系统和windows系统&#xff09; 在Ubuntu系统中设置MySQL开机启动&#xff0c;通常有以下几种方法&#xff1a; 1. **使用systemctl命令**&#xff1a; Ubuntu 16.04及更高版本使用systemd作为…

人工智能在网络安全中的重要性

介绍&#xff1a; 人工智能&#xff08;AI&#xff09;是计算机科学的一个分支&#xff0c;基于某些独特的算法和相关数学计算&#xff0c;使机器能够拥有人类的决策能力。另一方面&#xff0c;网络安全包括保护虚拟世界免受网络攻击和威胁的安全措施。人工智能能够通过采取与…