Announcement

Collapse
No announcement yet.

Communication Between UE4 and a Python UDP Server

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Communication Between UE4 and a Python UDP Server

    All,

    I have spent a while searching and learning on how to implement the talking between UE4 and Python. I have used Rama's code as well as some random ones on the internet to get me where I am. Below is the code I am using to talk to a Python UDP server that will handle all my non game related network code (e.g. Logging into the server and checking user info, sending remember tokens, retrieving saved characters and anything else you can think of that doesn't rely on the game engine.) I hope this helps.

    Python Code

    UDPServer.py

    You could do the below differently and even put the handler in a different thread.

    Code:
    ##################################
    ## UDP Server to Talk to UE4
    ##################################
    
    ### Imports
    import socket
    import sys
    from UDPRequestHandler import *
    
    HOST = ''
    PORT = 8800
    
    # Create the UDP Socket
    try:
        socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        #socket.setblocking(false)
        print("Socket Created")
    except:
        print("Failed to create socket.")
        sys.exit()
    
    # Bind socket to local host and port
    try:
        socket.bind((HOST, PORT))
    except:
        print("Bind failed.")
        sys.exit()
        
    print("Socket bind complete.")
    
    # Now keep talking to clients
    
    while 1:
    
        # Receive data from client (data, addr)
        d = socket.recvfrom(1024)
        data = str(d[0], "utf-8")
        addr = d[1]
    
        # Print to the server who made a connection.
        print("{} wrote:".format(addr))
        print(data)
            
        # Now have our UDP handler handle the data
        myUDPHandler = UDPRequestHandler()
        myResponse = myUDPHandler.handle(data)
    
        # Respond back
        print(myResponse)
        socket.sendto(bytes(myResponse, 'utf-8'), addr)
        
    socket.close()
    UDPRequestHandler.py

    This is called to handle each connection.

    Code:
    ########################
    ## UDPHandler Class
    ########################
    
    ### Imports
    import socketserver
    import json
    from passlib.hash import pbkdf2_sha256
    from UDPPostgreSQLConnector import *
    
    class UDPRequestHandler:
    
        def checkLogin(self, email, password):
    
            # This checks to see if the player is who they say they are.
            # If so send back a dict with the players ID and remember_token.
    
            myDB = UDPPostgreSQLConnector()
    
            pswdToCheck = pbkdf2_sha256.encrypt(password, rounds=200000,
                                                salt_size=16)
     
            dbResponse = myDB.selectWhere("game.players",
                            "*", "email = '" + email + "'")
            return dbResponse
     
            #if (pbkdf2_sha256.verify(password, dbResponse['password'])):
    
            #   return "Logged In"
            #else:
            #   return "Invalid Password"
    
        def handle(self, data):
            myData = json.loads(data)
                  
    
            if myData['Request'] == 'Login':
    
                ResponseStr = "{'Return': " + self.checkLogin(myData['Email'],
                                                              myData['Password'])
            elif myData['Request'] == 'Test':
    
                ResponseStr = pbkdf2_sha256.encrypt(myData['Password'], rounds=5000,
                                                salt_size=16)
            else:
                ResponseStr = "I hear you"
                                         
            response = ResponseStr
            
            return response
    UDPPostgreSQLConnector.py

    This handles all database work. This is basic and will be expanded as needed. But you should get the idea. Also, sensitive info stripped.

    Code:
    #######################
    ## PostgreSQL Class
    #######################
    
    import psycopg2 as mdb
    import psycopg2.extras
    import sys
    
    
    class UDPPostgreSQLConnector:
    
        connection = None         
    
        def openConnection(self, db):
    
            conString = "host='999.888.77.6' dbname='game' user='secret' password='secret'"
    
            # opens a connection to the db
            self.connection = mdb.connect(conString)
    
        def closeConnection(self):
    
            #Closes the connection
            self.connection.close()
    
        def selectWhere(self, table, columns, where):
    
            # Use like
            #selectWhere("users", "id", "email='lll'")
    
            try:
    
                self.openConnection(self.db)
    
                cur = self.connection.cursor(cursor_factory=mdb.extras.RealDictCursor)
    
                cur.execute("SELECT " + columns + " FROM " + table +
                            " WHERE " + where) 
    
                data = cur.fetchone()
    
                if (data == ""):
                    data = "No users."
    
                cur.close()
    
            except mdb.DatabaseError as e:
    
                data = 'Error %s' % e
                #sys.exit(1)
    
            finally:
    
                if self.connection:
                     self.closeConnection()
    
            return data
    The python code is all stored on my Ubuntu server and not on my development computer. Below is the different classes and code for my test game.

    UE4 C++ Code

    UDP Networking Wrapper (H)

    This is the main object I use in blueprints to send and receive.

    Code:
    #include "Object.h"
    #include "Networking.h"
    #include "Json.h"
    #include "UDPReceiveWorker.h"
    #include "UDPNetworkingWrapper.generated.h"
    
    /**
     * 
     */
    UCLASS(BlueprintType, Blueprintable)
    class GAME_API UUDPNetworkingWrapper : public UObject
    {
    	GENERATED_BODY()
    
    public:
    	//////////////////////////////////////////////////////////////////////////
    	// Construction
    
    	/**
    	* Creates and initializes a new UDPNetworking object.
    	*
    	* @param Description - The description of the socket for debug purposes.
    	* @param SenderSocketName - Name of the sender socket for debug purposes.
    	* @param TheIP - IP of the the machine you want to send a message too.
    	* @param ThePort - The port of the machine you are trying to talk to.
    	* @param BufferSize - The size of the buffer for the socket
    	* @param AllowBroadCast - Allow broadcasting on this socket?
    	* @param Bound - Bind socket to the ip and port?
    	* @param Reusable - Is this socket reusable?
    	* @param Blocking - Is this socket blocking other data?
    	*/
    	UFUNCTION(BlueprintPure, Category = "UDPNetworking")
    		static UUDPNetworkingWrapper* ConstructUDPWrapper(const FString& Description, const FString& SenderSocketName, const FString& TheIP, const int32 ThePort, const int32 BufferSize,
    			const bool AllowBroadcast, const bool Bound, const bool Reusable, const bool Blocking);
    
    	static UUDPNetworkingWrapper* Constructor();
    
    	/**
    	* Sends the supplied message
    	* @param Message The message to be sent.
    	*/
    	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
    		bool sendMessage(FString Message);
    
    	//////////////////////////////////////////////////////////////////////////
    	// Destruction and reset
    
    	/** Destroys all data */
    	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
    		void UDPDestructor();
    
    	//// Grab Data
    	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
    		bool anyMessages();
    
    
    	/** Test Look for message */
    	UFUNCTION(BlueprintCallable, Category = "UDPNetworking")
    		FString GrabWaitingMessage();
    
    	static FString StringFromBinaryArray(const TArray<uint8>&  BinaryArray);
    	
    private:
    
    	// Holds the socket we are sending on
    	FSocket* SenderSocket;
    
    	// Description for debugging
    	FString SocketDescription;
    
    	// Remote Address
    	FIPv4Endpoint RemoteEndPoint;
    	FIPv4Address RemoteAdress;
    
    	// Socket Subsystem
    	ISocketSubsystem* SocketSubsystem;
    
    	//UDPReceiveWorker* myRecieverWorker;
    	// The data
    	TArray<uint8> ReceivedData;
    UDP Networking Wrapper (C)

    CPP

    Code:
    #include "Game.h"
    #include "UDPNetworkingWrapper.h"
    
    UUDPNetworkingWrapper* UUDPNetworkingWrapper::Constructor()
    {
    	return (UUDPNetworkingWrapper*)StaticConstructObject(UUDPNetworkingWrapper::StaticClass());
    }
    UUDPNetworkingWrapper* UUDPNetworkingWrapper::ConstructUDPWrapper(const FString& Description, const FString& SenderSocketName, const FString& TheIP, const int32 ThePort, const int32 BufferSize,
    	const bool AllowBroadcast, const bool Bound, const bool Reusable, const bool Blocking)
    {
    
    	UUDPNetworkingWrapper* wrapper = Constructor();
    
    	wrapper->SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
    	FIPv4Address::Parse(TheIP, wrapper->RemoteAdress);
    
    	wrapper->RemoteEndPoint = FIPv4Endpoint(wrapper->RemoteAdress, ThePort);
    
    	// First set our socket null
    	wrapper->SenderSocket = nullptr;
    
    	if (wrapper->SocketSubsystem != nullptr) // If socket subsytem is good
    	{
    		wrapper->SenderSocket = wrapper->SocketSubsystem->CreateSocket(NAME_DGram, *Description, true);
    
    		if (wrapper->SenderSocket != nullptr) // Is our socket created
    		{
    			// Setup the socket 
    			bool Error = !wrapper->SenderSocket->SetNonBlocking(!Blocking) ||
    				!wrapper->SenderSocket->SetReuseAddr(Reusable) ||
    				!wrapper->SenderSocket->SetBroadcast(AllowBroadcast) ||
    				!wrapper->SenderSocket->SetRecvErr();
    
    			if (!Error)
    			{
    				if (Bound)
    				{
    					Error = !wrapper->SenderSocket->Bind(*wrapper->RemoteEndPoint.ToInternetAddr());
    				}
    			}
    
    			if (!Error)
    			{
    				int32 OutNewSize;
    
    				wrapper->SenderSocket->SetReceiveBufferSize(BufferSize, OutNewSize);
    				wrapper->SenderSocket->SetSendBufferSize(BufferSize, OutNewSize);
    			}
    
    			if (Error)
    			{
    				GLog->Logf(TEXT("FUdpSocketBuilder: Failed to create the socket %s as configured"), *Description);
    
    				wrapper->SocketSubsystem->DestroySocket(wrapper->SenderSocket);
    				wrapper->SenderSocket = nullptr;
    			}
    		}
    
    	}
    
    
    	return wrapper;
    }
    
    bool UUDPNetworkingWrapper::sendMessage(FString Message)
    {
    	if (!SenderSocket) return false;
    
    	int32 BytesSent;
    	FTimespan waitTime = FTimespan(10);
    	TCHAR *serializedChar = Message.GetCharArray().GetData();
    	int32 size = FCString::Strlen(serializedChar);
    	int32 sent = 0;
    
    	// Send to
    	//myRecieverWorker = UDPReceiveWorker::JoyInit(SenderSocket, waitTime);
    	bool success = SenderSocket->SendTo((uint8*)TCHAR_TO_UTF8(serializedChar), size, BytesSent, *RemoteEndPoint.ToInternetAddr());
    	if (success && BytesSent > 0) // Success
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    FString UUDPNetworkingWrapper::GrabWaitingMessage()
    {
    	uint32 Size;
    
    	TSharedRef<FInternetAddr> Sender = SocketSubsystem->CreateInternetAddr();
    	
    	while (SenderSocket->HasPendingData(Size))
    	{
    		int32 Read = 0;
    		ReceivedData.Init(FMath::Min(Size, 65507u));
    		SenderSocket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), Read, *Sender);
    	}
    	
    
    	return StringFromBinaryArray(ReceivedData);
    
    }
    
    bool UUDPNetworkingWrapper::anyMessages()
    {
    
    	uint32 Size;
    
    	if (SenderSocket->HasPendingData(Size))
    	{
    		return true;
    	}
    	
    	return false;
    }
    
    FString UUDPNetworkingWrapper::StringFromBinaryArray(const TArray<uint8>& BinaryArray)
    {
    	//Create a string from a byte array!
    	const std::string cstr(reinterpret_cast<const char*>(BinaryArray.GetData()), BinaryArray.Num());
    
    	//FString can take in the c_str() of a std::string
    	return FString(cstr.c_str());
    
    }
    
    void UUDPNetworkingWrapper::UDPDestructor()
    {
    	SocketSubsystem->DestroySocket(SenderSocket);
    	SenderSocket = nullptr;
    	SocketSubsystem = nullptr;
    	//myRecieverWorker = nullptr;
    }
    Then in the editor just create an object with the UDPNetworkingWrapper class. Make a call to the constructor passing all the info. Then with the object you can send out messages and make a check for any new messages. Below is my test setup in a pawn blueprint.

    Click image for larger version

Name:	Graph.png
Views:	1
Size:	129.4 KB
ID:	1141836

    - Josh

    #2
    Amazing, thanks for sharing!
    Journeyman's Minimap - Available on Marketplace - Forum topic - Video

    Comment


      #3
      Thanks for sharing the code. I've been working on communication with a master server and UE4 for a while, and this code will help a bunch. I noticed that UDPReceiveWorker.h is included in UDPNetworkingWrapper.h but isn't posted; could you post it?

      Comment


        #4
        Seriously, thanks so much for sharing the code. I've managed to start talking with my master server. Up till I found this post I probably spent ~10+ hours trying to make sense out of the internal networking of UE4.

        Comment


          #5
          Josh, thank you very much. This was very helpful. It is much more straightforward that the UdpMessaging plugin.

          Originally posted by Saccaed View Post
          Thanks for sharing the code. I've been working on communication with a master server and UE4 for a while, and this code will help a bunch. I noticed that UDPReceiveWorker.h is included in UDPNetworkingWrapper.h but isn't posted; could you post it?
          Any references to what may have been contained in UDPReceiveWorker.h seem to be commented out. I commented out this include with no side effects.

          Comment


            #6
            No worries glad this helped some people. I honestly spent forever looking as well. As far as the UDPReceiveWorker.h goes, that was something I was working on as to get the handling of receiving messages in another thread so the game does wait for a response. I never could get it to work properly.

            UDPReceiveWorker.h

            Code:
            #include "Networking.h"
            #include "UDPNetworkingWrapper.h"
            #include "NetworkingFunctionLibrary.h"
            #include <string>
            
            
            
            /**
             * This class holds the multi threading data for receiving UDP packets
             */
            class VIRTUALTABLETOP_API UDPReceiveWorker : public FRunnable
            {
            
            	// Singleton instance, can access the thread at anytime.
            	static UDPReceiveWorker* Runnable;
            
            	// The data
            	TArray<uint8>* ReceivedDataRef;
            
            	/** Stop this thread? Uses Thread Safe Counter */
            	FThreadSafeCounter StopTaskCounter;
            
            	
            
            
            public:
            	UDPReceiveWorker(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime);
            	virtual ~UDPReceiveWorker();
            
            
            
            	// Begin FRunnable interface.
            	virtual bool Init();
            	virtual uint32 Run();
            	virtual void Stop();
            	// End FRunnable interface
            
            	void EnsureCompletion();
            
            	static UDPReceiveWorker* JoyInit(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime);
            
            	/** Shuts down the thread. Static so it can easily be called from outside the thread context */
            	static void Shutdown();
            
            private:
            
            	// Holds the network socket.
            	FSocket* Socket;
            
            	// Holds a pointer to the socket sub-system.
            	ISocketSubsystem* SocketSubsystem;
            
            	// Holds a flag indicating that the thread is stopping.
            	bool Stopping;
            
            	// Holds the thread object.
            	FRunnableThread* Thread;
            
            	// Holds the amount of time to wait for inbound packets.
            	FTimespan WaitTime;
            
            	
            };
            UDPReceiverWorker.cpp

            Code:
            include "VirtualTabletop.h"
            #include "UDPReceiveWorker.h"
            
            UDPReceiveWorker* UDPReceiveWorker::Runnable = NULL;
            
            UDPReceiveWorker::UDPReceiveWorker(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime)
            	: Socket(InSocket)
            	, StopTaskCounter(0)
            	, WaitTime(InWaitTime)
            	, Stopping(false)
            {
            
            	const bool bAutoDeleteSelf = false;
            	const bool bAutoDeleteRunnable = false;
            
            	// Link the data
            	ReceivedDataRef = &ReceivedData;
            
            	SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
            	Thread = FRunnableThread::Create(this, TEXT("UDPReceiveWorker"), 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more
            
            }
            
            UDPReceiveWorker::~UDPReceiveWorker()
            {
            	delete Thread;
            	Thread = NULL;
            	SocketSubsystem = nullptr;
            	Socket = nullptr;
            	ReceivedDataRef = nullptr;
            	//myNetworkingWrapper = nullptr;
            }
            
            // Init
            bool UDPReceiveWorker::Init()
            {
            	return true;
            }
            
            //Run
            uint32 UDPReceiveWorker::Run()
            {
            	TSharedRef<FInternetAddr> Sender = SocketSubsystem->CreateInternetAddr();
            
            	while (!Stopping)
            	{
            		if (!Socket->Wait(ESocketWaitConditions::WaitForRead, WaitTime))
            		{
            			continue;
            		}
            
            		uint32 Size;
            
            		while (Socket->HasPendingData(Size))
            		{
            
            			int32 Read = 0;
            			Socket->RecvFrom(ReceivedDataRef->GetData(), ReceivedDataRef->Num(), Read, *Sender);
            
            		}
            
            	}
            
            	
            
            	return 0;
            }
            
            
            // Stop
            void UDPReceiveWorker::Stop()
            {
            	StopTaskCounter.Increment();
            }
            
            UDPReceiveWorker* UDPReceiveWorker::JoyInit(TArray<uint8>& ReceivedData, FSocket* InSocket, FTimespan& InWaitTime)
            {
            	//Create new instance of thread if it does not exist
            	//		and the platform supports multi threading!
            	if (!Runnable && FPlatformProcess::SupportsMultithreading())
            	{
            		Runnable = new UDPReceiveWorker(ReceivedData, InSocket, InWaitTime);
            	}
            	return Runnable;
            }
            
            void UDPReceiveWorker::EnsureCompletion()
            {
            	Stop();
            	Thread->WaitForCompletion();
            }
            
            void UDPReceiveWorker::Shutdown()
            {
            	if (Runnable)
            	{
            		Runnable->EnsureCompletion();
            		delete Runnable;
            		Runnable = NULL;
            	}
            }
            
            //
            //bool UDPReceiveWorker::IsThreadFinished()
            //{
            //	if (Runnable) return Runnable->IsFinished();
            //	return true;
            //}
            If you get this working please share with us.

            Comment


              #7
              So I am using this code in UE 4.7.6 and I have it working to send out data, but it isn't receiving data. Any ideas? I have verified that the packets are reaching my computer, but the message waiting function isn't flagging true.

              Comment


                #8
                I have switched to a TCP setup now. Post what your code looks like and I'll take a look at it.

                Comment


                  #9
                  It's actually working now, I think it may have just been an issue with cached data or how unreal generated the headers because it just started working after a couple reboots.

                  Comment


                    #10
                    Okay cool, glad it's working.

                    Comment


                      #11
                      Is there any way to modify this code to send integers and floats in the UDP packet instead of a string? I just barely understand C++ code enough to keep my head above water, so I wouldn't know how to change the lines myself.

                      - John

                      Comment


                        #12
                        You can convert strings to ints or floats. You can put many values into a single string using delimiters and using parse string to array.

                        Comment


                          #13
                          Originally posted by Dannington View Post
                          You can convert strings to ints or floats. You can put many values into a single string using delimiters and using parse string to array.
                          I think what he means is: sending byte, float, int,etc formats directly instead of a string; for performance reasons - packet size.
                          [On Hold]WIP: Vertex-Based Ledge Detection/Parkour Engine

                          Looking for fellow programmers to develop a project.

                          Comment


                            #14
                            I'm communicating with a server that is looking for a specific packet size and an .xml datagram that is parsing specific data types into its structure, so, like horus mentioned, I need to be able to send floats and ints with a certain packet size for the server to accept the packet.

                            Comment


                              #15
                              Hi, I am having the same issue as above... The Unreal - to - client send is working perfectly, but I cannot send data TO UE. Code as above, C# in place of python though. Sending to another c++ programme works fine, but in UE, the 'anyMessages' bool is never hit. Has anyone had/solved this ?

                              Thanks!

                              Comment

                              Working...
                              X