Delegate - Fire event from c++ and have blueprint act on it

I am trying to fire an event from a async task within c++ that my blueprint can receive and then act on… I’m new to c++ and I’m confused how to use delegates properly and then receive the event in my blueprint.

So in my .h file I have

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTcpRxData, FString, rxData);

UCLASS()
class UAsyncTcpClientBPLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_UCLASS_BODY()	

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "Connect To Server", ScriptName = "Connect To Server"), Category = "Utilities|TCP")
	static void ConnectToServer(bool& error);

	UPROPERTY(BlueprintAssignable, Category = "Utilities|TCP")
	FOnTcpRxData OnTcpDataRx;

};

In my blueprint I was expecting to see something like the “Event BeginPlay” component that I can then attach what I want to happen in my blueprint, but I don’t see anything.

I’m sure I’m missing some pieces and have read through some of the tutorials, but I still don’t understand.

If it matters the C++ code is a blueprint function library.

I am trying to write c++ code for a tcp client, that can receive commands and then fire events that are received in the blueprint for a static mesh component and acted upon.

I have the tcp code working in the blueprint library, but if there is a better parent class I can use that.

Hey NexSim,

could you please provide me with your full .cpp and .h file for this Blueprint Function Library class?

Thanks!

.h file →

#pragma once


#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")

#include "Runtime/Core/Public/Async/AsyncWork.h"
#include "Runtime/Engine/Classes/Engine/EngineTypes.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AsyncTcpClientBPLibrary.generated.h"

#define DEFAULT_BUFLEN 128

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTcpRxData, FString, rxData);

UCLASS()
class UAsyncTcpClientBPLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_UCLASS_BODY()	

	UFUNCTION(BlueprintCallable, meta = (DisplayName = "Connect To Server", ScriptName = "Connect To Server"), Category = "Utilities|TCP")
	static void ConnectToServer(bool& error);

	UPROPERTY(BlueprintAssignable, Category = "Utilities|TCP")
	FOnTcpRxData OnTcpDataRx;


};

namespace ThreadingTest
{
	static void OnTcpRxData(SOCKET ConnectSocket)
	{

			int iResult;
			int recvbuflen = DEFAULT_BUFLEN;
			bool error = true;
			char recvbuf[DEFAULT_BUFLEN];
			FString rxMsg = FString("No Message");
		
				iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
				if (iResult > 0)
					error = false;
				else if (iResult == 0)
					error = true;
				else
					error = true;

			if(error == false)
			{ 
				rxMsg = FString(ANSI_TO_TCHAR(recvbuf));
			}
			else
			{
				rxMsg = FString("error");
			}
		
			if (error == false)
			{
			}
	}
}

.h cont

    class TcpRxAsyncTask : public FNonAbandonableTask
    {
    	SOCKET ConnectSocket;
    
    public:
    	TcpRxAsyncTask(SOCKET ConnectSocket)
    	{
    	}
    
    	FORCEINLINE TStatId GetStatId() const
    	{
    		RETURN_QUICK_DECLARE_CYCLE_STAT(TcpRxAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
    	}
    
    	void DoWork()
    	{
    		ThreadingTest::OnTcpRxData(ConnectSocket);
    	}
    };

.cpp

#include "AsyncTcpClientBPLibrary.h"
#include "AsyncTcpClient.h"

#include "Runtime/Core/Public/Async/AsyncWork.h"
#include "Runtime/Engine/Classes/Engine/EngineTypes.h"

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")

#define DEFAULT_PORT "27015"

SOCKET ConnectSocket = INVALID_SOCKET;
uint32 SOCKET_READ_TIMEOUT_MSEC = 1;


UAsyncTcpClientBPLibrary::UAsyncTcpClientBPLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{

}

.cpp cont

    void UAsyncTcpClientBPLibrary::ConnectToServer(bool& error)
    {
    	WSADATA wsaData;
    	 error = true;
    	struct addrinfo *result = NULL,
    		*ptr = NULL,
    		hints;
    	char *sendbuf = "this is a test";
    
    	int iResult;
    	// Initialize Winsock
    	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    	if (iResult != 0) {
    		printf("WSAStartup failed with error: %d\n", iResult);
    
    	}
    
    	ZeroMemory(&hints, sizeof(hints));
    	hints.ai_family = AF_UNSPEC;
    	hints.ai_socktype = SOCK_STREAM;
    	hints.ai_protocol = IPPROTO_TCP;
    
    	// Resolve the server address and port
    	iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
    	if (iResult != 0) {
    		printf("getaddrinfo failed with error: %d\n", iResult);
    		WSACleanup();
    		error = true;
    	}
    
    	// Attempt to connect to an address until one succeeds
    	for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
    
    		// Create a SOCKET for connecting to server
    		ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
    			ptr->ai_protocol);
    		if (ConnectSocket == INVALID_SOCKET) {
    
    			WSACleanup();
    			error = true;
    		}
    		DWORD timeout = SOCKET_READ_TIMEOUT_MSEC;
    		setsockopt(ConnectSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
    		iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    		if (iResult == SOCKET_ERROR) {
    			closesocket(ConnectSocket);
    			ConnectSocket = INVALID_SOCKET;
    			continue;
    		}
    		break;
    	}
    
    	freeaddrinfo(result);

.cpp cont 2

	if (ConnectSocket == INVALID_SOCKET) {
		WSACleanup();
		error = true;
	}
	else
	{
		error = false;
	}

	if (error == false)
	{
		(new FAutoDeleteAsyncTask<TcpRxAsyncTask>(ConnectSocket))->StartBackgroundTask();
	}
}

Do I need to bind my event to a function in the blueprint?

Something like:

Where RxData is a blueprint function?

That looks like it may be what I’m looking for… but it doesn’t compile because in the bind block target can’t be self… but I don’t know what I can connect it to. anything I try it says it isn’t compatible with BP library object.

Is a blueprint library not the base class I want?

Its expecting an object to bind to, but I don’t think blueprint libraries work as objects.

So what would be the best class to use?

I don’t really understand the difference between classes… and what I am trying to do isn’t something that exists in the world, but i need it to be able to act on different static mesh components.

I only want one instance of my tcp client…but then need at least a reference to each component that will be affected.

Hey NexSim,

On your bind event to OnTCPDDataRX, where it says Target Self, drag off of that node and type in “self” you should see an option to “get a reference to self”.

254862-referencetoself.png

Does this compile the blueprint successfully?

There is no reference to self that shows.

In your example it shows actions providing a Actor Object reference, but mine is for my BP library object.

I tried creating a c++ class based on an actor, and I can get a reference to self as described above, but I can’t connect it to to the target in the bind block, it gives the same error that they are different types.

NexSim,

Can you provide some more context to how you want to use these Static Mesh Components within your project? Usually static mesh components are objects that will “exist” your world.

Thanks!

I’m trying to programmatically change the material on different object. So say I have some roads, I want to be able to change it from brick to gravel to stone.

So I am trying to create a tcp client that will receive a string, and then based on the string change the material of the affected components.

I have the tcp client piece worked out (as an async task), and separately the material swapping worked out (based on key pressed) but I can’t combine the two.

As you have mentioned it is possible to set materials on an object via c++, these materials can also be used within Unreal Material Editor, and extended using Material Functions to create your desired blend effect during run time. (It’s also possible to paint layers together using the landscape tool)

Here are some links to documentation on Material Instances, Material Functions, and Landscape Materials:

[Material Instances][1]

[Material Functions][2]

[Landscape Materials][3]

Also here is a code example on setting materials via in c++

MaterialActor.h

 #pragma once
     
     #include "CoreMinimal.h"
     #include "GameFramework/Actor.h"
     #include "Engine/Classes/Materials/Material.h"
     #include "MaterialActor.generated.h"
     
     UCLASS()
     class TESTINGMATERIALS_API AMaterialActor : public AActor
     {
         GENERATED_BODY()
         
     public:    
         // Sets default values for this actor's properties
         AMaterialActor();
     
     protected:
         // Called when the game starts or when spawned
         virtual void BeginPlay() override;
     
     public:    
         // Called every frame
         virtual void Tick(float DeltaTime) override;
         
         UPROPERTY(VisibleAnywhere)
         UStaticMeshComponent* MeshComp;
     
         UPROPERTY(VisibleAnywhere)
         UMaterial* StoredMaterial;
             
 };

MaterialActor.Cpp

#include "MaterialActor.h"
 
 // Sets default values
 AMaterialActor::AMaterialActor()
 {
      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
 
     MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
     RootComponent = MeshComp;
 
     static ConstructorHelpers::FObjectFinder<UStaticMesh>FoundMesh(TEXT("/Engine/EditorMeshes/EditorSphere.EditorSphere"));
 
     if (FoundMesh.Succeeded())
     {
         MeshComp->SetStaticMesh(FoundMesh.Object);
     }
 
     static ConstructorHelpers::FObjectFinder<UMaterial>FoundMaterial(TEXT("/Game/StarterContent/Materials/M_CobbleStone_Pebble.M_CobbleStone_Pebble"));
 
     if (FoundMaterial.Succeeded())
     {
         StoredMaterial = FoundMaterial.Object;
     }
 
  
     MeshComp->SetMaterial(0, StoredMaterial);
     
 }
 
 // Called when the game starts or when spawned
 void AMaterialActor::BeginPlay()
 {
     Super::BeginPlay();
     
 }
 
 // Called every frame
 void AMaterialActor::Tick(float DeltaTime)
 {
     Super::Tick(DeltaTime);
 
 }

This will yield a material change on the texture:

Hope this helps!

I’m still confused what are the proper classes to use. Because a blueprint library doesn’t seem right… but an actor doesn’t either.

Update:

I think I’m getting closer. I created a c++ class derived from the player target start, and I can broadcast from there, and then in a blueprint for one of the components whose material should change I can bind the event. In the tick function of the player target start I broadcast and it is received and printed to the screen from the component.

So now it is just a matter of getting the tcp piece working in this new set up.

I don’t understand how the classes and objects work with it… and maybe that is why I am having trouble now with the tcp piece… but to get it working I had to create add a new actor component in the blueprint.

I would recommend reading up on the four main gameplay classes of the engine, which are UObject, AActor, UActorComponent, And UStruct. I’m going to give you a link to documentation that explains how each one of these classes functions within the engine. I hope this will give you some more insight on how to best use your TCP functionality for you project.

Programming Introduction