All,
I have spent a while searching and learning on how to implement the talking between UE4 and Python. I have used '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.
##################################
## 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.
########################
## 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.
#######################
## 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.
#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
#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.
- Josh