K2 Nodes - Need help getting my head around them

i! I am working on some custom nodes or at least trying to. I am a fairly competent coder that is more used C# and Java making the transition into C++.

I was following a Tutorial by Nicholas Ferrar, as he is seemingly the only person on the internet that’s ever posted a fully functioning k2Node script, Cpp and .h and supporting classes. I am doing this because I need some nodes to have dynamic numbered/functioning output and exec pins for some nodes in my system, and simply exposing functions in blueprints doesn’t give me these dynamic functionalities.

I am sure the problem stems from him having made this in UE4 and the project I am working for right now is using UE5.

[text](https://github.com/nFerrar/K2Node_Intro)

Anyway, at some point I tried using a function he wrote called FindSetterFunction, which looksl ike this:

UFunction * UMyK2Node::FindSetterFunctionByType(FEdGraphPinType& PinType)
    {
        UClass* LibraryClass = UMyK2Node::StaticClass();
        FName FunctionName = NAME_None;
        UFunction* Function = nullptr;

        //this should really be in a const FNAME
        //here goes a bunch of IFs to check the pin type and set FUNCTION NAME to its NAME
        if (PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
        {
            FunctionName = FSetterFunctionNames::FloatSetterName;
        }

        return Function;
    }

This is my console on after compilation:

> Build started...
1>------ Build started: Project: MyProject, Configuration: Development_Editor x64 ------
1>Using bundled DotNet SDK
1>Log file: C:\Users\jarri\AppData\Local\UnrealBuildTool\Log.txt
1>Building MyProjectEditor...
1>Using Visual Studio 2019 14.29.30146 toolchain (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133) and Windows 10.0.20348.0 SDK (C:\Program Files (x86)\Windows Kits\10).
1>[Adaptive Build] Excluded from MyProject unity file: MyK2Node.cpp, MyK2NodeData.cpp, MyProject.cpp, MyProjectGameModeBase.cpp
1>Determining max actions to execute in parallel (8 physical cores, 16 logical cores)
1>  Executing up to 8 processes, one per physical core
1>  Requested 1.5 GB free memory per action, 4.91 GB available: limiting max parallel actions to 3
1>Building 4 actions with 3 processes...
1>[1/4] Compile MyK2Node.cpp
1>[2/4] Link UnrealEditor-MyProject-0012.lib
1>   Creating library C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.lib and object C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.exp
1>[3/4] Link UnrealEditor-MyProject-0012.dll
1>   Creating library C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.suppressed.lib and object C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.suppressed.exp
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static void __cdecl FEdGraphToken::Create(class UObject const *,class FCompilerResultsLog *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &)" (__imp_?Create@FEdGraphToken@@SAXPEBVUObject@@PEAVFCompilerResultsLog@@AEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "protected: void __cdecl FCompilerResultsLog::Tokenize<class UMyK2Node *>(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &,class UMyK2Node *)" (??$Tokenize@PEAVUMyK2Node@@$V@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@PEAVUMyK2Node@@@Z)
1>  Hint on symbols that are defined and could potentially match:
1>    "__declspec(dllimport) public: static class TSharedRef<class FTextToken,1> __cdecl FTextToken::Create(class FText const &)" (__imp_?Create@FTextToken@@SA?AV?$TSharedRef@VFTextToken@@$00@@AEBVFText@@@Z)
1>    "__declspec(dllimport) public: static class TSharedRef<class FTokenizedMessage,1> __cdecl FTokenizedMessage::Create(enum EMessageSeverity::Type,class FText const &)" (__imp_?Create@FTokenizedMessage@@SA?AV?$TSharedRef@VFTokenizedMessage@@$00@@W4Type@EMessageSeverity@@AEBVFText@@@Z)
1>    "__declspec(dllimport) public: static class UBlueprintNodeSpawner * __cdecl UBlueprintNodeSpawner::Create(class TSubclassOf<class UEdGraphNode>,class UObject *,class TDelegate<void __cdecl(class UEdGraphNode *,bool),struct FDefaultDelegateUserPolicy>)" (__imp_?Create@UBlueprintNodeSpawner@@SAPEAV1@V?$TSubclassOf@VUEdGraphNode@@@@PEAVUObject@@V?$TDelegate@$A6AXPEAVUEdGraphNode@@_N@ZUFDefaultDelegateUserPolicy@@@@@Z)
1>MyK2Node.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __cdecl FCompilerResultsLog::NotifyIntermediateObjectCreation(class UObject *,class UObject *)" (__imp_?NotifyIntermediateObjectCreation@FCompilerResultsLog@@QEAAXPEAVUObject@@0@Z)
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FCompilerResultsLog::InternalLogMessage(class FName,class TSharedRef<class FTokenizedMessage,1> const &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > const &)" (__imp_?InternalLogMessage@FCompilerResultsLog@@IEAAXVFName@@AEBV?$TSharedRef@VFTokenizedMessage@@$00@@AEBV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "public: virtual void __cdecl UMyK2Node::ExpandNode(class FKismetCompilerContext &,class UEdGraph *)" (?ExpandNode@UMyK2Node@@UEAAXAEAVFKismetCompilerContext@@PEAVUEdGraph@@@Z)
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FCompilerResultsLog::Tokenize(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &)" (__imp_?Tokenize@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "protected: void __cdecl FCompilerResultsLog::Tokenize<class UMyK2Node *>(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &,class UMyK2Node *)" (??$Tokenize@PEAVUMyK2Node@@$V@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@PEAVUMyK2Node@@@Z)
1>MyK2Node.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FKismetCompilerContext::AutoAssignNodePosition(class UEdGraphNode *)" (__imp_?AutoAssignNodePosition@FKismetCompilerContext@@IEAAXPEAVUEdGraphNode@@@Z)
1>C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Binaries\Win64\UnrealEditor-MyProject-0012.dll : fatal error LNK1120: 5 unresolved externals
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(45,5): error MSB3073: The command ""D:\Program Files\Epic Games\UE_5.0\Engine\Build\BatchFiles\Build.bat" MyProjectEditor Win64 Development -Project="C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\MyProject.uproject" -WaitMutex -FromMsBuild" exited with code 6.
1>Done building project "MyProject.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

PS: If anyone doesn’t know how to solve this specific problem, and has a better resource for learning how UE handles making nodes (Custom dialog system, and no I do not want to use a behaviour tree) please let me know!

I am making this node strictly for practice, and this is as car as I have gotten. As of right now this code draws a small featureless node with the nodes name on it.

Thanks!

I was hoping this would finally draw a full node to the graph and draw the pins onto the graph. `

Full code so far:

.h

#pragma once

#include "CoreMinimal.h"
#include "K2Node.h"
#include "MyK2Node.generated.h"

/**
 * 
 */
UCLASS()
class MYPROJECT_API UMyK2Node : public UK2Node
{
    GENERATED_BODY()

public:

    virtual void AllocateDefaultPins() override;
    virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;

    virtual FText GetNodeTitle(ENodeTitleType::Type TitleType)const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetMenuCategory() const override;
    
    virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;

    UEdGraphPin* GetThenPin() const;
    UEdGraphPin* GetNewValuePin() const;
    UEdGraphPin* GetReturnResultPin() const;
    UEdGraphPin* GetReturnValuePin() const;

    static UFunction* FindSetterFunctionByType(FEdGraphPinType& PinType);
    
};

.cpp

#include "MyK2Node.h"

#include "EdGraphSchema_K2.h"

#include "MyK2NodeData.h"

//probably boiler plate
#include "KismetCompiler.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "K2Node_CallFunction.h"

#define LOCTEXT_NAMESPACE "MYK2NODE"


struct FGetPinName 
{
    static const FName& GetTargetPinName()
    {
        static const FName TargetPinName(TEXT("Target"));
        return TargetPinName;
    }

    static const FName& GetVarNamePinName()
    {
        static const FName VarNamePinName(TEXT("VarName"));
        return VarNamePinName;
    }

    static const FName& GetNewValuePinName()
    {
        static const FName NewValuePinName(TEXT("NewValue"));
        return NewValuePinName;
    }

    static const FName& GetOutputResultPinName()
    {
        static const FName OutputResultPinName(TEXT("bSuccess"));
        return OutputResultPinName;
    }

    static const FName& GetOutputValuePinName()
    {
        static const FName OutputValuePinName(TEXT("NewValueResult"));
        return OutputValuePinName;
    }


};

//FText FName Handling

namespace FSetterFunctionNames
{
    static const FName FloatSetterName(GET_FUNCTION_NAME_CHECKED(UMyK2NodeData, SetFloatByName));
    
};

// // // // // // // // // //   


void UMyK2Node::AllocateDefaultPins()
{
    const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
    /*Create our pins*/
    // Execution pins
    CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
    CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
    //Input
    UEdGraphNode::FCreatePinParams PinParams;
    PinParams.bIsReference = true;
    UEdGraphPin* InTargetPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UObject::StaticClass(), FGetPinName::GetTargetPinName(), PinParams);
    UEdGraphPin* InVarNamePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Name, FGetPinName::GetVarNamePinName());
    K2Schema->SetPinAutogeneratedDefaultValue(InVarNamePin, FName("My Var Name").ToString());
    UEdGraphPin* InNewValuePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, FGetPinName::GetNewValuePinName(), PinParams);
    //Output
    UEdGraphPin* OutValidPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Boolean, FGetPinName::GetOutputResultPinName());
    K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(OutValidPin);
    UEdGraphPin* OutNewValuePin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, FGetPinName::GetOutputValuePinName());
    Super::AllocateDefaultPins();
}



// // // // // // // // //

FText UMyK2Node::GetNodeTitle(ENodeTitleType::Type TitleType) const
    {
        return LOCTEXT("TestNode_Title", "Test Node");
    }
    FText UMyK2Node::GetTooltipText() const
    {
        return LOCTEXT("TestNode_Tooltip", "This is a test.");
    }
    FText UMyK2Node::GetMenuCategory() const
    {
        return LOCTEXT("TestNode_MenuCategory", "Dialog Node");
    }

    // // // // // // // // // // // // // //

    void UMyK2Node::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
    {
        Super::ExpandNode(CompilerContext, SourceGraph);
        UFunction* BlueprintFunction = FindSetterFunctionByType(GetNewValuePin()->PinType);
        if (!BlueprintFunction)
        {
            CompilerContext.MessageLog.Error(*LOCTEXT("InvalidFunctionName", "The function has not been found.").ToString(), this);
            return;
        }
        UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
        CallFunction->SetFromFunction(BlueprintFunction);
        CallFunction->AllocateDefaultPins();
        CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, this);
        //Input
        CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetTargetPinName()), *CallFunction->FindPinChecked(TEXT("Target")));
        CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetVarNamePinName()), *CallFunction->FindPinChecked(TEXT("VarName")));
        CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetNewValuePinName()), *CallFunction->FindPinChecked(TEXT("NewValue")));
        //Output
        CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetOutputValuePinName()), *CallFunction->FindPinChecked(TEXT("OutValue")));
        CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetOutputResultPinName()), *CallFunction->GetReturnValuePin());
        //Exec pins
        UEdGraphPin* NodeExec = GetExecPin();
        UEdGraphPin* NodeThen = FindPin(UEdGraphSchema_K2::PN_Then);
        UEdGraphPin* InternalExec = CallFunction->GetExecPin();
        CompilerContext.MovePinLinksToIntermediate(*NodeExec, *InternalExec);
        UEdGraphPin* InternalThen = CallFunction->GetThenPin();
        CompilerContext.MovePinLinksToIntermediate(*NodeThen, *InternalThen);
        //After we are done we break all links to this node (not the internally created one)
        BreakAllNodeLinks();
    }

    void UMyK2Node::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
    {
        Super::GetMenuActions(ActionRegistrar);
  UClass* Action = GetClass();
  if (ActionRegistrar.IsOpenForRegistration(Action))
  {
    UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(GetClass());
    check(Spawner != nullptr);
    ActionRegistrar.AddBlueprintAction(Action, Spawner);
  }
    }

    UEdGraphPin* UMyK2Node::GetThenPin() const
    {
        const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
        UEdGraphPin* Pin = FindPinChecked(UEdGraphSchema_K2::PN_Then);
        check(Pin->Direction == EGPD_Output);
        return Pin;
    }

    UEdGraphPin* UMyK2Node::GetNewValuePin() const
    {
        UEdGraphPin* Pin = FindPin(FGetPinName::GetNewValuePinName());
        check(Pin == NULL || Pin->Direction == EGPD_Input);
        return Pin;
    }
    
    UEdGraphPin* UMyK2Node::GetReturnResultPin() const
    {
        UEdGraphPin* Pin = FindPin(FGetPinName::GetNewValuePinName());
        check(Pin == NULL || Pin->Direction == EGPD_Input);
        return Pin;
    }

    UEdGraphPin* UMyK2Node::GetReturnValuePin() const
    {
        UEdGraphPin* Pin = FindPin(FGetPinName::GetOutputResultPinName());
        check(Pin == NULL || Pin->Direction == EGPD_Output);
        return Pin;;
    }

    UFunction * UMyK2Node::FindSetterFunctionByType(FEdGraphPinType& PinType)
    {
        UClass* LibraryClass = UMyK2Node::StaticClass();
        FName FunctionName = NAME_None;
        UFunction* Function = nullptr;

        //this should really be in a const FNAME
        //here goes a bunch of IFs to check the pin type and set FUNCTION NAME to its NAME
        if (PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
        {
            FunctionName = FSetterFunctionNames::FloatSetterName;
        }

        return Function;
    }


#undef LOCTEXT_NAMESPACE