Skip to content

Commit

Permalink
Merge pull request #6 from getnamo/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
getnamo authored Oct 2, 2016
2 parents 8aab05f + 4f51162 commit 0e6f590
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 22 deletions.
2 changes: 1 addition & 1 deletion SocketIOClient.uplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.2.0",
"VersionName": "0.2.1",
"FriendlyName": "SocketIOClient",
"Description": "Socket IO C++ Client ported to UE4",
"Category": "Networking",
Expand Down
105 changes: 94 additions & 11 deletions Source/SocketIOClient/Private/SocketIOClientComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini
bWantsInitializeComponent = true;
bAutoActivate = true;
AddressAndPort = FString(TEXT("http://localhost:3000")); //default to 127.0.0.1
SessionId = FString(TEXT("invalid"));
}

std::string StdString(FString UEString)
{
return std::string(TCHAR_TO_UTF8(*UEString));
return std::string(TCHAR_TO_UTF8(*UEString)); //TCHAR_TO_ANSI try this string instead?
}
FString FStringFromStd(std::string StdString)
{
Expand All @@ -34,27 +35,54 @@ void USocketIOClientComponent::Connect(FString InAddressAndPort)
//Connect to the server on a background thread
FSIOLambdaRunnable::RunLambdaOnBackGroundThread([&]
{
//Attach the specific events
BindLambdaToEvent([&]
{
UE_LOG(LogTemp, Log, TEXT("SocketIO Connected"));
OnConnected.Broadcast();
}, FString(TEXT("connected")), FString(TEXT("/"))); //you need to emit this on the server to receive the event
//Attach the specific connection status events events

PrivateClient.set_open_listener(sio::client::con_listener([&]() {
SessionId = FStringFromStd(PrivateClient.get_sessionid());
UE_LOG(LogTemp, Log, TEXT("SocketIO Connected with session: %s"), *SessionId);
OnConnected.Broadcast(SessionId);
}));

BindLambdaToEvent([&]
PrivateClient.set_close_listener(sio::client::close_listener([&](sio::client::close_reason const& reason)
{
SessionId = FString(TEXT("invalid"));
UE_LOG(LogTemp, Log, TEXT("SocketIO Disconnected"));
OnDisconnected.Broadcast();
}, FString(TEXT("disconnect")), FString(TEXT("/"))); //doesn't get called atm, unsure how to get it called
OnDisconnected.Broadcast((EConnectionCloseReason)reason);
}));

PrivateClient.set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp)
{
FString Namespace = FStringFromStd(nsp);
UE_LOG(LogTemp, Log, TEXT("SocketIO connected to namespace: %s"), *Namespace);
OnSocketNamespaceConnected.Broadcast(Namespace);
}));

PrivateClient.set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp)
{
FString Namespace = FStringFromStd(nsp);
UE_LOG(LogTemp, Log, TEXT("SocketIO disconnected from namespace: %s"), *Namespace);
OnSocketNamespaceDisconnected.Broadcast(FStringFromStd(nsp));
}));

PrivateClient.set_fail_listener(sio::client::con_listener([&]()
{
UE_LOG(LogTemp, Log, TEXT("SocketIO failed to connect."));
OnFail.Broadcast();
}));

PrivateClient.connect(StdAddressString);
});
}


void USocketIOClientComponent::Disconnect()
{
PrivateClient.close();
if (PrivateClient.opened())
{
PrivateClient.socket()->off_all();
PrivateClient.socket()->off_error();
PrivateClient.close();
}
}

void USocketIOClientComponent::Emit(FString Name, FString Data, FString Namespace /* = FString(TEXT("/"))*/)
Expand All @@ -64,6 +92,11 @@ void USocketIOClientComponent::Emit(FString Name, FString Data, FString Namespac
}


void USocketIOClientComponent::EmitBuffer(FString Name, uint8* Data, int32 DataLength, FString Namespace /*= FString(TEXT("/"))*/)
{
PrivateClient.socket(StdString(Namespace))->emit(StdString(Name), std::make_shared<std::string>((char*)Data, DataLength));
}

void USocketIOClientComponent::BindEvent(FString Name, FString Namespace /*= FString(TEXT("/"))*/)
{
BindDataLambdaToEvent([&](const FString& EventName, const FString& EventData)
Expand Down Expand Up @@ -128,6 +161,56 @@ void USocketIOClientComponent::BindDataLambdaToEvent(
}


void USocketIOClientComponent::BindRawMessageLambdaToEvent(TFunction< void(const FString&, const sio::message::ptr&)> InFunction, FString Name, FString Namespace /*= FString(TEXT("/"))*/)
{
const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = InFunction; //copy the function so it remains in context

PrivateClient.socket(StdString(Namespace))->on(
StdString(Name),
sio::socket::event_listener_aux(
[&, SafeFunction](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp)
{
const FString SafeName = FStringFromStd(name);

FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeFunction, SafeName, data]
{
SafeFunction(SafeName, data);
}, TStatId(), nullptr, ENamedThreads::GameThread);
}));
}


void USocketIOClientComponent::BindBinaryMessageLambdaToEvent(TFunction< void(const FString&, const TArray<uint8>&)> InFunction, FString Name, FString Namespace /*= FString(TEXT("/"))*/)
{
const TFunction< void(const FString&, const TArray<uint8>&)> SafeFunction = InFunction; //copy the function so it remains in context

PrivateClient.socket(StdString(Namespace))->on(
StdString(Name),
sio::socket::event_listener_aux(
[&, SafeFunction](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp)
{
const FString SafeName = FStringFromStd(name);

//Construct raw buffer
if (data->get_flag() == sio::message::flag_binary)
{
TArray<uint8> Buffer;
int32 BufferSize = data->get_binary()->size();
auto MessageBuffer = data->get_binary();
Buffer.Append((uint8*)(MessageBuffer->data()), BufferSize);

FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeFunction, SafeName, Buffer]
{
SafeFunction(SafeName, Buffer);
}, TStatId(), nullptr, ENamedThreads::GameThread);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Non-binary message received to binary message lambda, check server message data!"));
}
}));
}

//Todo: add object -> json conversion, or convert it to a UEJSON object that can stringify

sio::message::ptr USocketIOClientComponent::getMessage(const std::string& json)
Expand Down
1 change: 1 addition & 0 deletions Source/SocketIOClient/Private/SocketIOClientPrivatePCH.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "Core.h"
#include "Engine.h"
#include "Object.h"

// You should place include statements to your module's private header files here. You only need to
// add includes for headers that are used in most of your module's source files though.
75 changes: 65 additions & 10 deletions Source/SocketIOClient/Public/SocketIOClientComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,43 @@
#include "Components/ActorComponent.h"
#include "SocketIOClientComponent.generated.h"

UENUM(BlueprintType)
enum EMessageTypeFlag
{
FLAG_INTEGER,
FLAG_DOUBLE,
FLAG_STRING,
FLAG_BINARY,
FLAG_ARRAY,
FLAG_OBJECT,
FLAG_BOOLEAN,
FLAG_NULL
};

//TODO: convert sio::message to UE struct for more flexible use
USTRUCT()
struct FSIOMessage
{
GENERATED_USTRUCT_BODY()

UPROPERTY(BlueprintReadWrite, Category = "SocketIO Message Properties")
TEnumAsByte<EMessageTypeFlag> MessageFlag;

//Internal UE storage
FJsonObject Object;
};

UENUM(BlueprintType)
enum EConnectionCloseReason
{
CLOSE_REASON_NORMAL,
CLOSE_REASON_DROP
};

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSIOCEventSignature);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCSocketEventSignature, FString, Namespace);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCOpenEventSignature, FString, SessionId);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByte<EConnectionCloseReason>, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCNameDataEventSignature, FString, Name, FString, Data);

UCLASS(ClassGroup = "Networking", meta = (BlueprintSpawnableComponent))
Expand All @@ -15,31 +51,43 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent

//Async events
UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCEventSignature OnConnected;
FSIOCOpenEventSignature OnConnected;

UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCCloseEventSignature OnDisconnected;

UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCSocketEventSignature OnSocketNamespaceConnected;

UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCSocketEventSignature OnSocketNamespaceDisconnected;

UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCEventSignature OnDisconnected;
FSIOCEventSignature OnFail;

UPROPERTY(BlueprintAssignable, Category = "SocketIO Events")
FSIOCNameDataEventSignature On;
FSIOCNameDataEventSignature On;

//Default properties
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = defaults)
FString AddressAndPort;
FString AddressAndPort;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = defaults)
bool ShouldAutoConnect;
bool ShouldAutoConnect;

UPROPERTY(BlueprintReadWrite, Category = "SocketIO Properties")
FString SessionId;

/**
* Connect to a socket.io server, optional if autoconnect is set
* Connect to a socket.io server, optional if auto-connect is set
*
* @param AddressAndPort the address in URL format with port
*/
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
void Connect(FString InAddressAndPort);
void Connect(FString InAddressAndPort);

UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
void Disconnect();
void Disconnect();

/**
* Emit a string event with a string action
Expand All @@ -48,7 +96,10 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
* @param Data Data string
*/
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
void Emit(FString Name, FString Data, FString Namespace = FString(TEXT("/")));
void Emit(FString Name, FString Data, FString Namespace = FString(TEXT("/")));

//Binary data version, only available in C++
void EmitBuffer(FString Name, uint8* Data, int32 DataLength, FString Namespace = FString(TEXT("/")));

/**
* Emit a string event with a string action
Expand All @@ -57,7 +108,7 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
* @param Namespace Optional namespace, defaults to default namespace
*/
UFUNCTION(BlueprintCallable, Category = "SocketIO Functions")
void BindEvent(FString Name, FString Namespace = FString(TEXT("/")));
void BindEvent(FString Name, FString Namespace = FString(TEXT("/")));


virtual void InitializeComponent() override;
Expand All @@ -69,6 +120,10 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
//When you care about the data you get
void BindDataLambdaToEvent(TFunction< void(const FString&, const FString&)> InFunction, FString Name, FString Namespace = FString(TEXT("/")));

//Typically for binary data events
void BindRawMessageLambdaToEvent(TFunction< void(const FString&, const sio::message::ptr&)> InFunction, FString Name, FString Namespace = FString(TEXT("/")));
void BindBinaryMessageLambdaToEvent(TFunction< void(const FString&, const TArray<uint8>&)> InFunction, FString Name, FString Namespace = FString(TEXT("/")));

protected:
sio::client PrivateClient;

Expand Down
1 change: 1 addition & 0 deletions Source/SocketIOClient/SocketIOClient.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public SocketIOClient(TargetInfo Target)
new string[]
{
"Core",
"JSON",
// ... add other public dependencies that you statically link with here ...
}
);
Expand Down

0 comments on commit 0e6f590

Please sign in to comment.