Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #6

Merged
merged 2 commits into from
Oct 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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