From 5ea2af69228416c177d59f8dc488c3399abca679 Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 21:40:28 +0000 Subject: [PATCH 01/23] basic plugin scoped implementation -remove async disconnect bool, this is not relevant for components anymore -add scoped ID and plugin scope bool (unlinked atm) -add game world connection limit -add verbose disconnect log --- .../SocketIOClient/Private/SocketIOClient.cpp | 39 ++++++++------- .../Private/SocketIOClientComponent.cpp | 49 +++++++++++-------- .../SocketIOClient/Private/SocketIONative.cpp | 8 ++- .../Public/SocketIOClientComponent.h | 13 +++-- 4 files changed, 65 insertions(+), 44 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index b1c75ed9..a6749953 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -33,16 +33,17 @@ void FSocketIOClientModule::ShutdownModule() // we call this function before unloading the module. FScopeLock Lock(&DeleteSection); - ModulePointers.Empty(); - + /*for (auto& Pointer : ModulePointers) { - if (Pointer) - { - delete Pointer; - Pointer = nullptr; - } + if (Pointer) + { + delete Pointer; + Pointer = nullptr; + } }*/ + + ModulePointers.Empty(); } FSocketIONative* FSocketIOClientModule::NewValidNativePointer() @@ -55,23 +56,25 @@ FSocketIONative* FSocketIOClientModule::NewValidNativePointer() void FSocketIOClientModule::ReleaseNativePointer(FSocketIONative* PointerToRelease) { - PointerToRelease->OnConnectedCallback = [PointerToRelease](const FString& SessionId) - { - //If we're still connected, disconnect us - if (PointerToRelease) - { - PointerToRelease->SyncDisconnect(); - } - }; - //Release the pointer on the background thread FSIOLambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this] { FScopeLock Lock(&DeleteSection); - + if (PointerToRelease) { - delete PointerToRelease; + //Disconnect + if (PointerToRelease->bIsConnected) + { + PointerToRelease->SyncDisconnect(); + } + + //Delete pointer + if (PointerToRelease) + { + ModulePointers.Remove(PointerToRelease); + delete PointerToRelease; + } } }); } diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 58a92eb7..2e683df1 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -3,6 +3,7 @@ #include "SocketIOClientComponent.h" #include "SIOLambdaRunnable.h" #include "SIOJConvert.h" +#include "SocketIOClient.h" USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &init) : UActorComponent(init) { @@ -10,17 +11,20 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini bWantsInitializeComponent = true; bAutoActivate = true; NativeClient = nullptr; - bAsyncQuitDisconnect = true; + bLimitConnectionToGameWorld = true; AddressAndPort = FString(TEXT("http://localhost:3000")); //default to 127.0.0.1 SessionId = FString(TEXT("invalid")); + + //Plugin scoped utilities + bPluginScopedConnection = false; + PluginScopedId = TEXT("Not Scoped"); //default for non-scoped id } void USocketIOClientComponent::InitializeComponent() { Super::InitializeComponent(); { - FScopeLock lock(&AllocationSection); - NativeClient = MakeShareable(new FSocketIONative); + NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); } } @@ -36,23 +40,9 @@ void USocketIOClientComponent::BeginPlay() void USocketIOClientComponent::UninitializeComponent() { - //This may lock up so run it on a background thread - if (bAsyncQuitDisconnect) - { - if (bIsConnected) - { - FSIOLambdaRunnable::RunLambdaOnBackGroundThread([&] - { - FScopeLock lock(&AllocationSection); - NativeClient = nullptr; - }); - } - } - else - { - FScopeLock lock(&AllocationSection); - NativeClient = nullptr;; - } + //Let the plugin release the pointer on it's own time + ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); + NativeClient = nullptr; //UE_LOG(SocketIOLog, Log, TEXT("UninitializeComponent() call")); Super::UninitializeComponent(); @@ -197,6 +187,25 @@ bool USocketIOClientComponent::CallBPFunctionWithMessage(UObject* Target, const void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJsonObject* Query /*= nullptr*/, USIOJsonObject* Headers /*= nullptr*/) { + //Check if we're limiting this component + if(bLimitConnectionToGameWorld) + { + UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull); + if (World) + { + bool bIsGameWorld = (World->IsGameWorld() || World->IsPreviewWorld()); + if(!bIsGameWorld) + { + UE_LOG(SocketIOLog, Log, TEXT("USocketIOClientComponent::Connect attempt in non-game world blocked by bLimitConnectionToGameWorld.")); + return; + } + } + else + { + UE_LOG(SocketIOLog, Warning, TEXT("USocketIOClientComponent::Connect attempt while in invalid world.")); + return; + } + } TSharedPtr QueryFJson; TSharedPtr HeadersFJson; diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index f6020ee1..4f9080a4 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -37,8 +37,9 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< PrivateClient->set_close_listener(sio::client::close_listener([&](sio::client::close_reason const& reason) { bIsConnected = false; + + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %d"), *SessionId, (int32)reason); SessionId = FString(TEXT("invalid")); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected: %d"), (int32)reason); if (OnDisconnectedCallback) { @@ -120,7 +121,10 @@ void FSocketIONative::Disconnect() void FSocketIONative::SyncDisconnect() { - OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); + if (OnDisconnectedCallback) + { + OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); + } ClearCallbacks(); PrivateClient->sync_close(); } diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 3cd11048..4fde4984 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -51,7 +51,13 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent bool bShouldAutoConnect; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") - bool bAsyncQuitDisconnect; + bool bLimitConnectionToGameWorld; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + bool bPluginScopedConnection; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + FString PluginScopedId; UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") bool bIsConnected; @@ -293,7 +299,6 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent bool CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr Message); FCriticalSection AllocationSection; - TSharedPtr NativeClient; - - //FSocketIONative* NativeClient; + //TSharedPtr NativeClient; + FSocketIONative* NativeClient; }; \ No newline at end of file From 9e71523c2296385b9e57a89206f179d10f80843c Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 21:51:43 +0000 Subject: [PATCH 02/23] add close reason verbosity -added enum to string from https://answers.unrealengine.com/questions/330496/conversion-of-enum-to-string.html --- Source/SIOJson/Private/SIOJConvert.cpp | 3 +-- Source/SIOJson/Public/SIOJConvert.h | 9 +++++++++ Source/SocketIOClient/Private/SocketIONative.cpp | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/SIOJson/Private/SIOJConvert.cpp b/Source/SIOJson/Private/SIOJConvert.cpp index e6fe0943..7b01fef2 100644 --- a/Source/SIOJson/Private/SIOJConvert.cpp +++ b/Source/SIOJson/Private/SIOJConvert.cpp @@ -461,5 +461,4 @@ void USIOJConvert::ReplaceJsonValueNamesWithMap(TSharedPtr& JsonValu ReplaceJsonValueNamesWithMap(Item, KeyMap); } } -} - +} \ No newline at end of file diff --git a/Source/SIOJson/Public/SIOJConvert.h b/Source/SIOJson/Public/SIOJConvert.h index dcfc1991..2ed1bcfb 100644 --- a/Source/SIOJson/Public/SIOJConvert.h +++ b/Source/SIOJson/Public/SIOJConvert.h @@ -61,4 +61,13 @@ class SIOJSON_API USIOJConvert : public UObject static void SetTrimmedKeyMapForStruct(TSharedPtr& InMap, UStruct* Struct); static void SetTrimmedKeyMapForProp(TSharedPtr& InMap, UProperty* ArrayInnerProp); static void ReplaceJsonValueNamesWithMap(TSharedPtr& InValue, TSharedPtr KeyMap); + + template + static FString EnumToString(const FString& enumName, const T value) + { + UEnum* pEnum = FindObject(ANY_PACKAGE, *enumName); + return *(pEnum ? pEnum->GetNameStringByIndex(static_cast(value)) : "null"); + } }; + + diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 4f9080a4..dc915dc2 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -38,7 +38,9 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< { bIsConnected = false; - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %d"), *SessionId, (int32)reason); + ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; + FString DisconnectReasonString = USIOJConvert::EnumToString(TEXT("ESIOConnectionCloseReason"), DisconnectReason); + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); SessionId = FString(TEXT("invalid")); if (OnDisconnectedCallback) From a2f2f11957d5956809909812b8039e990bf171e2 Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 22:20:08 +0000 Subject: [PATCH 03/23] clean module exit --- .../SocketIOClient/Private/SocketIOClient.cpp | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index a6749953..47ef25ef 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -16,7 +16,8 @@ class FSocketIOClientModule : public ISocketIOClientModule private: FCriticalSection DeleteSection; - TArray ModulePointers; + TArray PluginNativePointers; + FThreadSafeBool bHasActiveNativePointers; }; @@ -24,32 +25,29 @@ void FSocketIOClientModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - ModulePointers.Empty(); + PluginNativePointers.Empty(); } void FSocketIOClientModule::ShutdownModule() { // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. - FScopeLock Lock(&DeleteSection); - - /*for (auto& Pointer : ModulePointers) - { - if (Pointer) + //Wait for all pointers to release + while (bHasActiveNativePointers) { - delete Pointer; - Pointer = nullptr; + FPlatformProcess::Sleep(0.01f); } - }*/ - ModulePointers.Empty(); + //Native pointers will be automatically released by uninitialize components + PluginNativePointers.Empty(); } FSocketIONative* FSocketIOClientModule::NewValidNativePointer() { FSocketIONative* NewPointer = new FSocketIONative; - ModulePointers.Add(NewPointer); + PluginNativePointers.Add(NewPointer); + bHasActiveNativePointers = true; return NewPointer; } @@ -59,8 +57,6 @@ void FSocketIOClientModule::ReleaseNativePointer(FSocketIONative* PointerToRelea //Release the pointer on the background thread FSIOLambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this] { - FScopeLock Lock(&DeleteSection); - if (PointerToRelease) { //Disconnect @@ -69,11 +65,14 @@ void FSocketIOClientModule::ReleaseNativePointer(FSocketIONative* PointerToRelea PointerToRelease->SyncDisconnect(); } - //Delete pointer + //Delete pointer ensure this get's hit safely if (PointerToRelease) { - ModulePointers.Remove(PointerToRelease); + FScopeLock Lock(&DeleteSection); + + PluginNativePointers.Remove(PointerToRelease); delete PointerToRelease; + bHasActiveNativePointers = PluginNativePointers.Num() > 0; } } }); From f154d7e25c83b2d7be1fb153e6fa05c1a25087dc Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 22:23:49 +0000 Subject: [PATCH 04/23] add a timeout to exit potential infinite loop waiting --- Source/SocketIOClient/Private/SocketIOClient.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index 47ef25ef..6b0cc46b 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -34,9 +34,18 @@ void FSocketIOClientModule::ShutdownModule() // we call this function before unloading the module. //Wait for all pointers to release + float Elapsed = 0.f; while (bHasActiveNativePointers) { FPlatformProcess::Sleep(0.01f); + Elapsed += 0.01f; + + //if it takes more than 5 seconds, just quit + if (Elapsed > 5.f) + { + UE_LOG(SocketIOLog, Warning, TEXT("FSocketIOClientModule::ShutdownModule force quit due to long quit.")); + break; + } } //Native pointers will be automatically released by uninitialize components From 5c2b20077bcefa4a8f988a9ddccd4199dba178d6 Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 22:27:11 +0000 Subject: [PATCH 05/23] use shared pointers locally again --- .../SocketIOClient/Private/SocketIOClientComponent.cpp | 9 ++++++--- Source/SocketIOClient/Public/SocketIOClientComponent.h | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 2e683df1..ba94ae53 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -24,7 +24,9 @@ void USocketIOClientComponent::InitializeComponent() { Super::InitializeComponent(); { - NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); + //Because our connections can last longer than game world + //end, we let plugin-scoped structures manage our memory + NativeClient = MakeShareable(ISocketIOClientModule::Get().NewValidNativePointer()); } } @@ -40,8 +42,9 @@ void USocketIOClientComponent::BeginPlay() void USocketIOClientComponent::UninitializeComponent() { - //Let the plugin release the pointer on it's own time - ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); + //Because our connections can last longer than game world + //end, we let plugin-scoped structures manage our memory. + ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient.Get()); NativeClient = nullptr; //UE_LOG(SocketIOLog, Log, TEXT("UninitializeComponent() call")); diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 4fde4984..36114db8 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -299,6 +299,5 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent bool CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr Message); FCriticalSection AllocationSection; - //TSharedPtr NativeClient; - FSocketIONative* NativeClient; + TSharedPtr NativeClient; }; \ No newline at end of file From d0bbafb4bc6752cb6cd273fd9615bd11241329ed Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 22:38:07 +0000 Subject: [PATCH 06/23] make it work with sharedptrs --- .../SocketIOClient/Private/SocketIOClient.cpp | 21 ++++++++++--------- .../Private/SocketIOClientComponent.cpp | 4 ++-- Source/SocketIOClient/Public/SocketIOClient.h | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index 6b0cc46b..187ae19c 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -7,8 +7,8 @@ class FSocketIOClientModule : public ISocketIOClientModule { public: - virtual FSocketIONative* NewValidNativePointer() override; - void ReleaseNativePointer(FSocketIONative* PointerToRelease) override; + virtual TSharedPtr NewValidNativePointer() override; + void ReleaseNativePointer(TSharedPtr PointerToRelease) override; /** IModuleInterface implementation */ virtual void StartupModule() override; @@ -16,7 +16,7 @@ class FSocketIOClientModule : public ISocketIOClientModule private: FCriticalSection DeleteSection; - TArray PluginNativePointers; + TArray> PluginNativePointers; FThreadSafeBool bHasActiveNativePointers; }; @@ -52,21 +52,21 @@ void FSocketIOClientModule::ShutdownModule() PluginNativePointers.Empty(); } -FSocketIONative* FSocketIOClientModule::NewValidNativePointer() +TSharedPtr FSocketIOClientModule::NewValidNativePointer() { - FSocketIONative* NewPointer = new FSocketIONative; + TSharedPtr NewPointer = MakeShareable(new FSocketIONative); PluginNativePointers.Add(NewPointer); bHasActiveNativePointers = true; return NewPointer; } -void FSocketIOClientModule::ReleaseNativePointer(FSocketIONative* PointerToRelease) +void FSocketIOClientModule::ReleaseNativePointer(TSharedPtr PointerToRelease) { //Release the pointer on the background thread FSIOLambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this] { - if (PointerToRelease) + if (PointerToRelease.IsValid()) { //Disconnect if (PointerToRelease->bIsConnected) @@ -74,13 +74,14 @@ void FSocketIOClientModule::ReleaseNativePointer(FSocketIONative* PointerToRelea PointerToRelease->SyncDisconnect(); } - //Delete pointer ensure this get's hit safely - if (PointerToRelease) + //Last operation took a while, ensure it's still true + if (PointerToRelease.IsValid()) { FScopeLock Lock(&DeleteSection); PluginNativePointers.Remove(PointerToRelease); - delete PointerToRelease; + //delete PointerToRelease; + //PointerToRelease = nullptr; bHasActiveNativePointers = PluginNativePointers.Num() > 0; } } diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index ba94ae53..de687f3a 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -26,7 +26,7 @@ void USocketIOClientComponent::InitializeComponent() { //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory - NativeClient = MakeShareable(ISocketIOClientModule::Get().NewValidNativePointer()); + NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); } } @@ -44,7 +44,7 @@ void USocketIOClientComponent::UninitializeComponent() { //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory. - ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient.Get()); + ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); NativeClient = nullptr; //UE_LOG(SocketIOLog, Log, TEXT("UninitializeComponent() call")); diff --git a/Source/SocketIOClient/Public/SocketIOClient.h b/Source/SocketIOClient/Public/SocketIOClient.h index acdec41a..ed6d777c 100644 --- a/Source/SocketIOClient/Public/SocketIOClient.h +++ b/Source/SocketIOClient/Public/SocketIOClient.h @@ -33,6 +33,6 @@ class SOCKETIOCLIENT_API ISocketIOClientModule : public IModuleInterface /** * Utility to get module scoped pointers that get deleted when the module unloads. */ - virtual FSocketIONative* NewValidNativePointer() { return nullptr; }; - virtual void ReleaseNativePointer(FSocketIONative* PointerToRelease) {}; + virtual TSharedPtr NewValidNativePointer() { return nullptr; }; + virtual void ReleaseNativePointer(TSharedPtr PointerToRelease) {}; }; \ No newline at end of file From 92a862a7aedfd45cf98e1b37f497168119c88cbd Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 22:47:33 +0000 Subject: [PATCH 07/23] comment and bump to 7.0 --- SocketIOClient.uplugin | 2 +- Source/SocketIOClient/Private/SocketIOClientComponent.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SocketIOClient.uplugin b/SocketIOClient.uplugin index 3baa29f8..18408681 100644 --- a/SocketIOClient.uplugin +++ b/SocketIOClient.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "0.6.9", + "VersionName": "0.7.0", "FriendlyName": "Socket.IO Client", "Description": "Real-time networking library Socket.IO Client usable from blueprints and c++.", "Category": "Networking", diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index de687f3a..9594c8ef 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -44,6 +44,7 @@ void USocketIOClientComponent::UninitializeComponent() { //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory. + //We must ensure we set our pointer to null however. ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); NativeClient = nullptr; From 8c708866c79db6dc65aba36bbfc0d4b66333acd7 Mon Sep 17 00:00:00 2001 From: getnamo Date: Fri, 10 Nov 2017 23:08:37 +0000 Subject: [PATCH 08/23] cleanup and documentation --- .../Private/SIOLambdaRunnable.cpp | 9 +-------- .../SocketIOClient/Private/SocketIOClient.cpp | 19 +++++++++++++++---- .../Private/SocketIOClientComponent.cpp | 17 ++++++++++++----- Source/SocketIOClient/Public/SocketIOClient.h | 7 ++++++- .../Public/SocketIOClientComponent.h | 7 +++++++ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Source/SocketIOClient/Private/SIOLambdaRunnable.cpp b/Source/SocketIOClient/Private/SIOLambdaRunnable.cpp index ad41b354..3183dde7 100644 --- a/Source/SocketIOClient/Private/SIOLambdaRunnable.cpp +++ b/Source/SocketIOClient/Private/SIOLambdaRunnable.cpp @@ -97,12 +97,5 @@ FGraphEventRef FSIOLambdaRunnable::RunShortLambdaOnGameThread(TFunction< void()> void FSIOLambdaRunnable::ShutdownThreads() { - /*for (auto Runnable : Runnables) - { - if (Runnable != nullptr) - { - delete Runnable; - } - Runnable = nullptr; - }*/ + } \ No newline at end of file diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index 187ae19c..2e45bc85 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -33,6 +33,17 @@ void FSocketIOClientModule::ShutdownModule() // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. + /* + Ensure we call release pointers, this will catch all the plugin scoped + connections pointers which don't get auto-released between game worlds. + */ + auto AllActivePointers = PluginNativePointers; + for (auto& Pointer : AllActivePointers) + { + ReleaseNativePointer(Pointer); + } + AllActivePointers.Empty(); + //Wait for all pointers to release float Elapsed = 0.f; while (bHasActiveNativePointers) @@ -43,7 +54,7 @@ void FSocketIOClientModule::ShutdownModule() //if it takes more than 5 seconds, just quit if (Elapsed > 5.f) { - UE_LOG(SocketIOLog, Warning, TEXT("FSocketIOClientModule::ShutdownModule force quit due to long quit.")); + UE_LOG(SocketIOLog, Warning, TEXT("FSocketIOClientModule::ShutdownModule force quit due to long wait to quit.")); break; } } @@ -77,11 +88,11 @@ void FSocketIOClientModule::ReleaseNativePointer(TSharedPtr Poi //Last operation took a while, ensure it's still true if (PointerToRelease.IsValid()) { + //Ensure only one thread at a time removes from array FScopeLock Lock(&DeleteSection); - PluginNativePointers.Remove(PointerToRelease); - //delete PointerToRelease; - //PointerToRelease = nullptr; + + //Update our active status bHasActiveNativePointers = PluginNativePointers.Num() > 0; } } diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 9594c8ef..12a88426 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -17,7 +17,7 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini //Plugin scoped utilities bPluginScopedConnection = false; - PluginScopedId = TEXT("Not Scoped"); //default for non-scoped id + PluginScopedId = TEXT("Default"); } void USocketIOClientComponent::InitializeComponent() @@ -34,9 +34,11 @@ void USocketIOClientComponent::InitializeComponent() void USocketIOClientComponent::BeginPlay() { Super::BeginPlay(); - if (bShouldAutoConnect) + + //Auto-connect to default address if supported and not already connected + if (bShouldAutoConnect && !bIsConnected) { - Connect(AddressAndPort); //connect to default address + Connect(AddressAndPort); } } @@ -45,8 +47,13 @@ void USocketIOClientComponent::UninitializeComponent() //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory. //We must ensure we set our pointer to null however. - ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); - NativeClient = nullptr; + + //If we're a regular connection we should close and release when we quit + if (!bPluginScopedConnection) + { + ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient); + NativeClient = nullptr; + } //UE_LOG(SocketIOLog, Log, TEXT("UninitializeComponent() call")); Super::UninitializeComponent(); diff --git a/Source/SocketIOClient/Public/SocketIOClient.h b/Source/SocketIOClient/Public/SocketIOClient.h index ed6d777c..037886a4 100644 --- a/Source/SocketIOClient/Public/SocketIOClient.h +++ b/Source/SocketIOClient/Public/SocketIOClient.h @@ -31,8 +31,13 @@ class SOCKETIOCLIENT_API ISocketIOClientModule : public IModuleInterface } /** - * Utility to get module scoped pointers that get deleted when the module unloads. + * Request a new plugin scoped pointer as a shared ptr. */ virtual TSharedPtr NewValidNativePointer() { return nullptr; }; + + /** + * Releases the given plugin scoped pointer using a background thread + * After calling this function make sure to set your pointer to nullptr. + */ virtual void ReleaseNativePointer(TSharedPtr PointerToRelease) {}; }; \ No newline at end of file diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 36114db8..85fb2ac1 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -53,9 +53,16 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") bool bLimitConnectionToGameWorld; + /** + * Toggle which enables plugin scoped connections. + * If you enable this the connection will remain until you manually call disconnect + * or close the app. Additionally any connections with the same PluginScopedId will use the same connection + * and receive the same events. + */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") bool bPluginScopedConnection; + /** If you leave this as is all plugin scoped connection components will share same connection*/ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") FString PluginScopedId; From df40565941e0d9b1ec1c862400d5493f645a9794 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 03:03:12 +0000 Subject: [PATCH 09/23] we need to wrap function for them to work inside tsets --- .../SocketIOClient/Private/SocketIOClient.cpp | 2 + .../Private/SocketIOClientComponent.cpp | 151 +++++++++++------- .../SocketIOClient/Private/SocketIONative.cpp | 79 ++++++--- .../Public/SocketIOClientComponent.h | 12 +- Source/SocketIOClient/Public/SocketIONative.h | 30 +++- 5 files changed, 188 insertions(+), 86 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index 2e45bc85..cba93b7a 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -4,6 +4,8 @@ #define LOCTEXT_NAMESPACE "FSocketIOClientModule" +//struct + class FSocketIOClientModule : public ISocketIOClientModule { public: diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 12a88426..aaf1ab4a 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -18,6 +18,8 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini //Plugin scoped utilities bPluginScopedConnection = false; PluginScopedId = TEXT("Default"); + + ClearCallbacks(); } void USocketIOClientComponent::InitializeComponent() @@ -27,6 +29,7 @@ void USocketIOClientComponent::InitializeComponent() //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); + SetupCallbacks(); } } @@ -48,6 +51,8 @@ void USocketIOClientComponent::UninitializeComponent() //end, we let plugin-scoped structures manage our memory. //We must ensure we set our pointer to null however. + ClearCallbacks(); + //If we're a regular connection we should close and release when we quit if (!bPluginScopedConnection) { @@ -59,6 +64,94 @@ void USocketIOClientComponent::UninitializeComponent() Super::UninitializeComponent(); } +void USocketIOClientComponent::SetupCallbacks() +{ + OnConnectedCallback = [this](const FString& InSessionId) + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] + { + if (this) + { + bIsConnected = true; + SessionId = InSessionId; + OnConnected.Broadcast(SessionId); + } + }); + }; + FFunctionWrapper < TFunction> Wrapper; + Wrapper.Function = OnConnectedCallback; + NativeClient->OnConnectedCallbacks.Add(Wrapper); + + const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected; + + OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnDisconnectedSafe, this, Reason] + { + if (this && OnDisconnectedSafe.IsBound()) + { + bIsConnected = false; + OnDisconnectedSafe.Broadcast(Reason); + } + }); + }; + + //NativeClient->OnDisconnectedCallbacks.Add(&OnDisconnectedCallback); + + OnNamespaceConnectedCallback = [this](const FString& Namespace) + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] + { + if (this) + { + OnSocketNamespaceConnected.Broadcast(Namespace); + } + }); + }; + //NativeClient->OnNamespaceConnectedCallbacks.Add(&OnNamespaceConnectedCallback); + + const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected; + + OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnSocketNamespaceDisconnectedSafe, this, Namespace] + { + if (this && OnSocketNamespaceDisconnectedSafe.IsBound()) + { + OnSocketNamespaceDisconnectedSafe.Broadcast(Namespace); + } + }); + }; + //NativeClient->OnNamespaceDisconnectedCallbacks.Add(&OnNamespaceDisconnectedCallback); + + OnFailCallback = [this]() + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([this] + { + OnFail.Broadcast(); + }); + }; + //NativeClient->OnFailCallbacks.Add(&OnFailCallback); +} + +void USocketIOClientComponent::ClearCallbacks() +{ + if (NativeClient.IsValid()) + { + /*NativeClient->OnConnectedCallbacks.Remove(&OnConnectedCallback); + NativeClient->OnDisconnectedCallbacks.Remove(&OnConnectedCallback); + NativeClient->OnNamespaceConnectedCallbacks.Remove(&OnNamespaceConnectedCallback); + NativeClient->OnNamespaceDisconnectedCallbacks.Remove(&OnNamespaceDisconnectedCallback); + NativeClient->OnFailCallbacks.Remove(&OnFailCallback);*/ + } + + OnConnectedCallback = nullptr; + OnDisconnectedCallback = nullptr; + OnNamespaceConnectedCallback = nullptr; + OnNamespaceDisconnectedCallback = nullptr; + OnFailCallback = nullptr; +} + bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response) { UFunction* Function = Target->FindFunction(FName(*FunctionName)); @@ -236,63 +329,7 @@ void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJson void USocketIOClientComponent::ConnectNative(const FString& InAddressAndPort, const TSharedPtr& Query /*= nullptr*/, const TSharedPtr& Headers /*= nullptr*/) { //Set native callback functions - NativeClient->OnConnectedCallback = [this](const FString& InSessionId) - { - FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] - { - if (this) - { - bIsConnected = true; - SessionId = InSessionId; - OnConnected.Broadcast(SessionId); - } - }); - }; - const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected; - - NativeClient->OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) - { - FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnDisconnectedSafe, this, Reason] - { - if (this && OnDisconnectedSafe.IsBound()) - { - bIsConnected = false; - OnDisconnectedSafe.Broadcast(Reason); - } - }); - }; - - NativeClient->OnNamespaceConnectedCallback = [this](const FString& Namespace) - { - FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] - { - if (this) - { - OnSocketNamespaceConnected.Broadcast(Namespace); - } - }); - }; - - const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected; - - NativeClient->OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) - { - FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnSocketNamespaceDisconnectedSafe, this, Namespace] - { - if (this && OnSocketNamespaceDisconnectedSafe.IsBound()) - { - OnSocketNamespaceDisconnectedSafe.Broadcast(Namespace); - } - }); - }; - - NativeClient->OnFailCallback = [this]() - { - FSIOLambdaRunnable::RunShortLambdaOnGameThread([this] - { - OnFail.Broadcast(); - }); - }; + { NativeClient->Connect(InAddressAndPort, Query, Headers); diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index dc915dc2..b3cb99b0 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -43,10 +43,16 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); SessionId = FString(TEXT("invalid")); - if (OnDisconnectedCallback) + /*if (OnDisconnectedCallbacks.Num()>0) { - OnDisconnectedCallback((ESIOConnectionCloseReason)reason); - } + for (auto Callback : OnDisconnectedCallbacks) + { + if (Callback) + { + Callback(DisconnectReason); + } + } + }*/ })); PrivateClient->set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp) @@ -63,19 +69,31 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); - if (OnConnectedCallback) + if (OnConnectedCallbacks.Num() > 0) { - OnConnectedCallback(SessionId); + for (auto& Wrapper : OnConnectedCallbacks) + { + if (Wrapper.Function) + { + Wrapper.Function(SessionId); + } + } } } FString Namespace = USIOMessageConvert::FStringFromStd(nsp); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO connected to namespace: %s"), *Namespace); + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); - if (OnNamespaceConnectedCallback) + /*if (OnNamespaceConnectedCallbacks.Num() > 0) { - OnNamespaceConnectedCallback(Namespace); - } + for (auto Callback : OnNamespaceConnectedCallbacks) + { + if (Callback) + { + Callback(Namespace); + } + } + }*/ })); PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp) @@ -83,19 +101,31 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< FString Namespace = USIOMessageConvert::FStringFromStd(nsp); UE_LOG(SocketIOLog, Log, TEXT("SocketIO disconnected from namespace: %s"), *Namespace); - if (OnNamespaceDisconnectedCallback) + /*if (OnNamespaceDisconnectedCallbacks.Num() > 0) { - OnNamespaceDisconnectedCallback(USIOMessageConvert::FStringFromStd(nsp)); - } + for (auto Callback : OnNamespaceDisconnectedCallbacks) + { + if (Callback) + { + Callback(USIOMessageConvert::FStringFromStd(nsp)); + } + } + }*/ })); PrivateClient->set_fail_listener(sio::client::con_listener([&]() { UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); - if (OnFailCallback) + /*if (OnFailCallbacks.Num() > 0) { - OnFailCallback(); - } + for (auto Callback : OnFailCallbacks) + { + if (Callback) + { + Callback(); + } + } + }*/ })); std::map QueryMap = {}; @@ -123,21 +153,24 @@ void FSocketIONative::Disconnect() void FSocketIONative::SyncDisconnect() { - if (OnDisconnectedCallback) + /*if (OnDisconnectedCallbacks.Num() > 0) { - OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); - } + for (auto Callback : OnDisconnectedCallbacks) + { + Callback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); + } + }*/ ClearCallbacks(); PrivateClient->sync_close(); } void FSocketIONative::ClearCallbacks() { - OnConnectedCallback = nullptr; - OnDisconnectedCallback = nullptr; - OnNamespaceConnectedCallback = nullptr; - OnNamespaceDisconnectedCallback = nullptr; - OnFailCallback = nullptr; + OnConnectedCallbacks.Empty(); + /*OnDisconnectedCallbacks.Empty(); + OnNamespaceConnectedCallbacks.Empty(); + OnNamespaceDisconnectedCallbacks.Empty(); + OnFailCallbacks.Empty();*/ } void FSocketIONative::Emit(const FString& EventName, const TSharedPtr& Message /*= nullptr*/, TFunction< void(const TArray>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/) diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 85fb2ac1..b520ff70 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -88,7 +88,8 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent USIOJsonObject* Headers = nullptr); /** - * Disconnect from current socket.io server, optional method. + * Disconnect from current socket.io server. Subscribe to OnDisconnected to know + * when you can continue from a disconnected state. * * @param AddressAndPort the address in URL format with port */ @@ -302,6 +303,15 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent protected: + //Internal Nativecallbacks + TFunction OnConnectedCallback; + TFunction OnDisconnectedCallback; + TFunction OnNamespaceConnectedCallback; + TFunction OnNamespaceDisconnectedCallback; + TFunction OnFailCallback; + void SetupCallbacks(); + void ClearCallbacks(); + bool CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response); bool CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr Message); diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index ee48fbe8..67b342c8 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -4,6 +4,7 @@ #include "SIOJsonObject.h" #include "SIOJsonValue.h" #include "SIOJConvert.h" +#include "CoreMinimal.h" UENUM(BlueprintType) enum ESIOConnectionCloseReason @@ -12,16 +13,35 @@ enum ESIOConnectionCloseReason CLOSE_REASON_DROP }; +template +struct FFunctionWrapper +{ + FuncType Function; + + bool operator==(const FFunctionWrapper& Other) const + { + return &Other == this; + } + + friend FORCEINLINE uint32 GetTypeHash(const FFunctionWrapper& Key) + { + return GetTypeHash((void*)&Key); + } +}; + + SOCKETIOCLIENT_API class FSocketIONative { public: //Native Callbacks - TFunction OnConnectedCallback; - TFunction OnDisconnectedCallback; - TFunction OnNamespaceConnectedCallback; - TFunction OnNamespaceDisconnectedCallback; - TFunction OnFailCallback; + TSet>> OnConnectedCallbacks; //TFunction + //TSet> OnDisconnectedCallbacks; //TFunction + //TSet> OnNamespaceConnectedCallbacks; //TFunction + //TSet> OnNamespaceDisconnectedCallbacks; //TFunction + //TSet> OnFailCallbacks; //TFunction + + //TODO: fix tset of functions, we may want to use a struct inbetween function binds e.g. struct -> function and return a hash. /** Default connection address string in form e.g. http://localhost:80. */ FString AddressAndPort; From b50f12dd60f66c2d924998d26078d56cea412aaa Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 03:26:56 +0000 Subject: [PATCH 10/23] link new wrapper structure --- .../Private/SocketIOClientComponent.cpp | 44 +++++++++---------- .../SocketIOClient/Private/SocketIONative.cpp | 44 +++++++++---------- .../Public/SocketIOClientComponent.h | 10 ++--- Source/SocketIOClient/Public/SocketIONative.h | 28 +++++++----- 4 files changed, 66 insertions(+), 60 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index aaf1ab4a..78960fa3 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -66,7 +66,7 @@ void USocketIOClientComponent::UninitializeComponent() void USocketIOClientComponent::SetupCallbacks() { - OnConnectedCallback = [this](const FString& InSessionId) + OnConnectedCallback.Function = [this](const FString& InSessionId) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] { @@ -78,13 +78,12 @@ void USocketIOClientComponent::SetupCallbacks() } }); }; - FFunctionWrapper < TFunction> Wrapper; - Wrapper.Function = OnConnectedCallback; - NativeClient->OnConnectedCallbacks.Add(Wrapper); + + NativeClient->OnConnectedCallbacks.Add(OnConnectedCallback); const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected; - OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) + OnDisconnectedCallback.Function = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnDisconnectedSafe, this, Reason] { @@ -96,9 +95,9 @@ void USocketIOClientComponent::SetupCallbacks() }); }; - //NativeClient->OnDisconnectedCallbacks.Add(&OnDisconnectedCallback); + NativeClient->OnDisconnectedCallbacks.Add(OnDisconnectedCallback); - OnNamespaceConnectedCallback = [this](const FString& Namespace) + OnNamespaceConnectedCallback.Function = [this](const FString& Namespace) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] { @@ -108,11 +107,12 @@ void USocketIOClientComponent::SetupCallbacks() } }); }; - //NativeClient->OnNamespaceConnectedCallbacks.Add(&OnNamespaceConnectedCallback); + + NativeClient->OnNamespaceConnectedCallbacks.Add(OnNamespaceConnectedCallback); const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected; - OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) + OnNamespaceDisconnectedCallback.Function = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnSocketNamespaceDisconnectedSafe, this, Namespace] { @@ -122,34 +122,34 @@ void USocketIOClientComponent::SetupCallbacks() } }); }; - //NativeClient->OnNamespaceDisconnectedCallbacks.Add(&OnNamespaceDisconnectedCallback); + NativeClient->OnNamespaceDisconnectedCallbacks.Add(OnNamespaceDisconnectedCallback); - OnFailCallback = [this]() + OnFailCallback.Function = [this]() { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this] { OnFail.Broadcast(); }); }; - //NativeClient->OnFailCallbacks.Add(&OnFailCallback); + NativeClient->OnFailCallbacks.Add(OnFailCallback); } void USocketIOClientComponent::ClearCallbacks() { if (NativeClient.IsValid()) { - /*NativeClient->OnConnectedCallbacks.Remove(&OnConnectedCallback); - NativeClient->OnDisconnectedCallbacks.Remove(&OnConnectedCallback); - NativeClient->OnNamespaceConnectedCallbacks.Remove(&OnNamespaceConnectedCallback); - NativeClient->OnNamespaceDisconnectedCallbacks.Remove(&OnNamespaceDisconnectedCallback); - NativeClient->OnFailCallbacks.Remove(&OnFailCallback);*/ + NativeClient->OnConnectedCallbacks.Remove(OnConnectedCallback); + NativeClient->OnDisconnectedCallbacks.Remove(OnDisconnectedCallback); + NativeClient->OnNamespaceConnectedCallbacks.Remove(OnNamespaceConnectedCallback); + NativeClient->OnNamespaceDisconnectedCallbacks.Remove(OnNamespaceDisconnectedCallback); + NativeClient->OnFailCallbacks.Remove(OnFailCallback); } - OnConnectedCallback = nullptr; - OnDisconnectedCallback = nullptr; - OnNamespaceConnectedCallback = nullptr; - OnNamespaceDisconnectedCallback = nullptr; - OnFailCallback = nullptr; + OnConnectedCallback.Function = nullptr; + OnDisconnectedCallback.Function = nullptr; + OnNamespaceConnectedCallback.Function = nullptr; + OnNamespaceDisconnectedCallback.Function = nullptr; + OnFailCallback.Function = nullptr; } bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index b3cb99b0..e5782bb1 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -43,16 +43,16 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); SessionId = FString(TEXT("invalid")); - /*if (OnDisconnectedCallbacks.Num()>0) + if (OnDisconnectedCallbacks.Num()>0) { - for (auto Callback : OnDisconnectedCallbacks) + for (auto& Wrapper : OnDisconnectedCallbacks) { - if (Callback) + if (Wrapper.Function) { - Callback(DisconnectReason); + Wrapper.Function(DisconnectReason); } } - }*/ + } })); PrivateClient->set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp) @@ -84,16 +84,16 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< FString Namespace = USIOMessageConvert::FStringFromStd(nsp); UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); - /*if (OnNamespaceConnectedCallbacks.Num() > 0) + if (OnNamespaceConnectedCallbacks.Num() > 0) { - for (auto Callback : OnNamespaceConnectedCallbacks) + for (auto& Wrapper : OnNamespaceConnectedCallbacks) { - if (Callback) + if (Wrapper.Function) { - Callback(Namespace); + Wrapper.Function(Namespace); } } - }*/ + } })); PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp) @@ -101,31 +101,31 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< FString Namespace = USIOMessageConvert::FStringFromStd(nsp); UE_LOG(SocketIOLog, Log, TEXT("SocketIO disconnected from namespace: %s"), *Namespace); - /*if (OnNamespaceDisconnectedCallbacks.Num() > 0) + if (OnNamespaceDisconnectedCallbacks.Num() > 0) { - for (auto Callback : OnNamespaceDisconnectedCallbacks) + for (auto& Wrapper : OnNamespaceDisconnectedCallbacks) { - if (Callback) + if (Wrapper.Function) { - Callback(USIOMessageConvert::FStringFromStd(nsp)); + Wrapper.Function(USIOMessageConvert::FStringFromStd(nsp)); } } - }*/ + } })); PrivateClient->set_fail_listener(sio::client::con_listener([&]() { UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); - /*if (OnFailCallbacks.Num() > 0) + if (OnFailCallbacks.Num() > 0) { - for (auto Callback : OnFailCallbacks) + for (auto Wrapper : OnFailCallbacks) { - if (Callback) + if (Wrapper.Function) { - Callback(); + Wrapper.Function(); } } - }*/ + } })); std::map QueryMap = {}; @@ -167,10 +167,10 @@ void FSocketIONative::SyncDisconnect() void FSocketIONative::ClearCallbacks() { OnConnectedCallbacks.Empty(); - /*OnDisconnectedCallbacks.Empty(); + OnDisconnectedCallbacks.Empty(); OnNamespaceConnectedCallbacks.Empty(); OnNamespaceDisconnectedCallbacks.Empty(); - OnFailCallbacks.Empty();*/ + OnFailCallbacks.Empty(); } void FSocketIONative::Emit(const FString& EventName, const TSharedPtr& Message /*= nullptr*/, TFunction< void(const TArray>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/) diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index b520ff70..390c0c08 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -304,11 +304,11 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent protected: //Internal Nativecallbacks - TFunction OnConnectedCallback; - TFunction OnDisconnectedCallback; - TFunction OnNamespaceConnectedCallback; - TFunction OnNamespaceDisconnectedCallback; - TFunction OnFailCallback; + TSetFunctionWrapper> OnConnectedCallback; + TSetFunctionWrapper> OnDisconnectedCallback; + TSetFunctionWrapper> OnNamespaceConnectedCallback; + TSetFunctionWrapper> OnNamespaceDisconnectedCallback; + TSetFunctionWrapper> OnFailCallback; void SetupCallbacks(); void ClearCallbacks(); diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index 67b342c8..2eb51d69 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -13,19 +13,25 @@ enum ESIOConnectionCloseReason CLOSE_REASON_DROP }; -template -struct FFunctionWrapper +template +struct TSetFunctionWrapper { - FuncType Function; + T Function; - bool operator==(const FFunctionWrapper& Other) const + bool operator==(const TSetFunctionWrapper& Other) const { return &Other == this; } - friend FORCEINLINE uint32 GetTypeHash(const FFunctionWrapper& Key) + friend FORCEINLINE uint32 GetTypeHash(const TSetFunctionWrapper& Key) { - return GetTypeHash((void*)&Key); + return ::PointerHash(&Key); + } + + TSetFunctionWrapper() {} + TSetFunctionWrapper(T InFunction) + { + Function = InFunction; } }; @@ -35,11 +41,11 @@ SOCKETIOCLIENT_API class FSocketIONative public: //Native Callbacks - TSet>> OnConnectedCallbacks; //TFunction - //TSet> OnDisconnectedCallbacks; //TFunction - //TSet> OnNamespaceConnectedCallbacks; //TFunction - //TSet> OnNamespaceDisconnectedCallbacks; //TFunction - //TSet> OnFailCallbacks; //TFunction + TSet>> OnConnectedCallbacks; //TFunction + TSet>> OnDisconnectedCallbacks; //TFunction + TSet>> OnNamespaceConnectedCallbacks; //TFunction + TSet>> OnNamespaceDisconnectedCallbacks; //TFunction + TSet>> OnFailCallbacks; //TFunction //TODO: fix tset of functions, we may want to use a struct inbetween function binds e.g. struct -> function and return a hash. From 4930294983f130e37652f8d966fba78df752f191 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 03:54:59 +0000 Subject: [PATCH 11/23] monstrocity supporting multi single connection callbacks - I'm not convinced the complexity is worth it. --- .../SocketIOClient/Private/SocketIONative.cpp | 27 ++++++++++++++++--- Source/SocketIOClient/Public/SocketIONative.h | 6 ++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index e5782bb1..e05ca0b1 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -262,18 +262,37 @@ void FSocketIONative::OnEvent(const FString& EventName, TFunction< void(const FS void FSocketIONative::OnRawEvent(const FString& EventName, TFunction< void(const FString&, const sio::message::ptr&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/) { - const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = CallbackFunction; //copy the function so it remains in context + //const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = CallbackFunction; //copy the function so it remains in context + if (!EventFunctionMap.Contains(EventName)) + { + TArray> FunctionArray; + EventFunctionMap.Add(EventName, FunctionArray); + } + + TArray>& FunctionArray = EventFunctionMap[EventName]; + FunctionArray.Add(CallbackFunction); PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->on( USIOMessageConvert::StdString(EventName), sio::socket::event_listener_aux( - [&, SafeFunction](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) + [&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) { const FString SafeName = USIOMessageConvert::FStringFromStd(name); - FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeFunction, SafeName, data] + FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeName, data] { - SafeFunction(SafeName, data); + auto& FunctionArray = EventFunctionMap[SafeName]; + + for (auto& Callback : FunctionArray) + { + Callback(SafeName, data); + } + /*for (auto& Pair : EventFunctionMap) + { + Pair.Value(SafeName, data); + }*/ + //SafeFunction(SafeName, data); + }, TStatId(), nullptr, ENamedThreads::GameThread); })); } diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index 2eb51d69..57b9d35d 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -13,6 +13,7 @@ enum ESIOConnectionCloseReason CLOSE_REASON_DROP }; +//Wrapper function for TFunctions which can be hashed based on pointers. I.e. no duplicate functions allowed template struct TSetFunctionWrapper { @@ -20,7 +21,7 @@ struct TSetFunctionWrapper bool operator==(const TSetFunctionWrapper& Other) const { - return &Other == this; + return GetTypeHash(Other) == GetTypeHash(this); } friend FORCEINLINE uint32 GetTypeHash(const TSetFunctionWrapper& Key) @@ -46,8 +47,7 @@ SOCKETIOCLIENT_API class FSocketIONative TSet>> OnNamespaceConnectedCallbacks; //TFunction TSet>> OnNamespaceDisconnectedCallbacks; //TFunction TSet>> OnFailCallbacks; //TFunction - - //TODO: fix tset of functions, we may want to use a struct inbetween function binds e.g. struct -> function and return a hash. + TMap>> EventFunctionMap; /** Default connection address string in form e.g. http://localhost:80. */ FString AddressAndPort; From dff1734b6ea8c6f050d5e8be5a90d266fac34b81 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 04:02:01 +0000 Subject: [PATCH 12/23] add target not found verbosity --- Source/SocketIOClient/Private/SocketIOClientComponent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 78960fa3..944a8265 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -154,6 +154,12 @@ void USocketIOClientComponent::ClearCallbacks() bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response) { + if (!Target->IsValidLowLevel()) + { + UE_LOG(SocketIOLog, Warning, TEXT("CallFunctionByNameWithArguments: Target not found for '%s'"), *FunctionName); + return false; + } + UFunction* Function = Target->FindFunction(FName(*FunctionName)); if (nullptr == Function) { From c079c25887e17daa9c9e2e554e950f78a43729f3 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 04:40:05 +0000 Subject: [PATCH 13/23] add shared connections via plugin scoping --- .../SocketIOClient/Private/SocketIOClient.cpp | 42 +++++++++++++++++++ .../Private/SocketIOClientComponent.cpp | 13 +++++- Source/SocketIOClient/Public/SocketIOClient.h | 5 +++ Source/SocketIOClient/Public/SocketIONative.h | 11 +++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index cba93b7a..c0340eda 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -9,7 +9,9 @@ class FSocketIOClientModule : public ISocketIOClientModule { public: + //virtual TSharedPtr NewValidNativePointer() override; virtual TSharedPtr NewValidNativePointer() override; + virtual TSharedPtr ValidSharedNativePointer(FString SharedId) override; void ReleaseNativePointer(TSharedPtr PointerToRelease) override; /** IModuleInterface implementation */ @@ -18,7 +20,14 @@ class FSocketIOClientModule : public ISocketIOClientModule private: FCriticalSection DeleteSection; + + //All native pointers manages by the plugin TArray> PluginNativePointers; + + //Shared pointers, these will typically be alive past game world lifecycles + TMap> SharedNativePointers; + TSet> AllSharedPtrs; //reverse lookup + FThreadSafeBool bHasActiveNativePointers; }; @@ -68,14 +77,47 @@ void FSocketIOClientModule::ShutdownModule() TSharedPtr FSocketIOClientModule::NewValidNativePointer() { TSharedPtr NewPointer = MakeShareable(new FSocketIONative); + PluginNativePointers.Add(NewPointer); + bHasActiveNativePointers = true; return NewPointer; } +TSharedPtr FSocketIOClientModule::ValidSharedNativePointer(FString SharedId) +{ + //Found key? return it + if (SharedNativePointers.Contains(SharedId)) + { + return SharedNativePointers[SharedId]; + } + //Otherwise request a new id and return it + else + { + TSharedPtr NewNativePtr = NewValidNativePointer(); + SharedNativePointers.Add(SharedId, NewNativePtr); + AllSharedPtrs.Add(NewNativePtr); + return NewNativePtr; + } +} + void FSocketIOClientModule::ReleaseNativePointer(TSharedPtr PointerToRelease) { + //Remove shared ptr references + if (AllSharedPtrs.Contains(PointerToRelease)) + { + AllSharedPtrs.Remove(PointerToRelease); + for (auto& Pair : SharedNativePointers) + { + if (Pair.Value == PointerToRelease) + { + SharedNativePointers.Remove(Pair.Key); + break; + } + } + } + //Release the pointer on the background thread FSIOLambdaRunnable::RunLambdaOnBackGroundThread([PointerToRelease, this] { diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 944a8265..57136419 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -28,7 +28,15 @@ void USocketIOClientComponent::InitializeComponent() { //Because our connections can last longer than game world //end, we let plugin-scoped structures manage our memory - NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); + if (bPluginScopedConnection) + { + NativeClient = ISocketIOClientModule::Get().ValidSharedNativePointer(PluginScopedId); + } + else + { + NativeClient = ISocketIOClientModule::Get().NewValidNativePointer(); + } + SetupCallbacks(); } @@ -66,6 +74,9 @@ void USocketIOClientComponent::UninitializeComponent() void USocketIOClientComponent::SetupCallbacks() { + //Sync current connected state + bIsConnected = NativeClient->bIsConnected; + OnConnectedCallback.Function = [this](const FString& InSessionId) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] diff --git a/Source/SocketIOClient/Public/SocketIOClient.h b/Source/SocketIOClient/Public/SocketIOClient.h index 037886a4..a5c09d88 100644 --- a/Source/SocketIOClient/Public/SocketIOClient.h +++ b/Source/SocketIOClient/Public/SocketIOClient.h @@ -35,6 +35,11 @@ class SOCKETIOCLIENT_API ISocketIOClientModule : public IModuleInterface */ virtual TSharedPtr NewValidNativePointer() { return nullptr; }; + /** + * Request a shared FSocketIONative instance for a given id. May allocate a new pointer. + */ + virtual TSharedPtr ValidSharedNativePointer(FString SharedId) { return nullptr; }; + /** * Releases the given plugin scoped pointer using a background thread * After calling this function make sure to set your pointer to nullptr. diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index 57b9d35d..78340d3f 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -263,6 +263,17 @@ SOCKETIOCLIENT_API class FSocketIONative TFunction< void(const FString&, const TArray&)> CallbackFunction, const FString& Namespace = FString(TEXT("/"))); + //TSet functions + /*bool operator==(const FSocketIONative& Other) const + { + return GetTypeHash(Other) == GetTypeHash(this); + } + + friend FORCEINLINE uint32 GetTypeHash(const FSocketIONative& Key) + { + return ::PointerHash(&Key); + }*/ + protected: TSharedPtr PrivateClient; class FSIOLambdaRunnable* ConnectionThread; From f7de2711ca6e74bc73e41a65c922302eb684eb93 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 05:07:05 +0000 Subject: [PATCH 14/23] nevermind on very complex idea -the complexity isn't worth it. If users want component multicasting for the same connection they should implement it downstream -added namespace session id --- .../Private/SocketIOClientComponent.cpp | 33 +---- .../SocketIOClient/Private/SocketIONative.cpp | 120 ++++++------------ .../Public/SocketIOClientComponent.h | 7 - Source/SocketIOClient/Public/SocketIONative.h | 60 ++++----- 4 files changed, 78 insertions(+), 142 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 57136419..3f94e4a1 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -77,7 +77,7 @@ void USocketIOClientComponent::SetupCallbacks() //Sync current connected state bIsConnected = NativeClient->bIsConnected; - OnConnectedCallback.Function = [this](const FString& InSessionId) + NativeClient->OnConnectedCallback = [this](const FString& InSessionId) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] { @@ -90,11 +90,9 @@ void USocketIOClientComponent::SetupCallbacks() }); }; - NativeClient->OnConnectedCallbacks.Add(OnConnectedCallback); - const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected; - OnDisconnectedCallback.Function = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) + NativeClient->OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnDisconnectedSafe, this, Reason] { @@ -106,9 +104,7 @@ void USocketIOClientComponent::SetupCallbacks() }); }; - NativeClient->OnDisconnectedCallbacks.Add(OnDisconnectedCallback); - - OnNamespaceConnectedCallback.Function = [this](const FString& Namespace) + NativeClient->OnNamespaceConnectedCallback = [this](const FString& Namespace) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] { @@ -119,11 +115,9 @@ void USocketIOClientComponent::SetupCallbacks() }); }; - NativeClient->OnNamespaceConnectedCallbacks.Add(OnNamespaceConnectedCallback); - const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected; - OnNamespaceDisconnectedCallback.Function = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) + NativeClient->OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) { FSIOLambdaRunnable::RunShortLambdaOnGameThread([OnSocketNamespaceDisconnectedSafe, this, Namespace] { @@ -133,34 +127,19 @@ void USocketIOClientComponent::SetupCallbacks() } }); }; - NativeClient->OnNamespaceDisconnectedCallbacks.Add(OnNamespaceDisconnectedCallback); - OnFailCallback.Function = [this]() + NativeClient->OnFailCallback = [this]() { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this] { OnFail.Broadcast(); }); }; - NativeClient->OnFailCallbacks.Add(OnFailCallback); } void USocketIOClientComponent::ClearCallbacks() { - if (NativeClient.IsValid()) - { - NativeClient->OnConnectedCallbacks.Remove(OnConnectedCallback); - NativeClient->OnDisconnectedCallbacks.Remove(OnDisconnectedCallback); - NativeClient->OnNamespaceConnectedCallbacks.Remove(OnNamespaceConnectedCallback); - NativeClient->OnNamespaceDisconnectedCallbacks.Remove(OnNamespaceDisconnectedCallback); - NativeClient->OnFailCallbacks.Remove(OnFailCallback); - } - - OnConnectedCallback.Function = nullptr; - OnDisconnectedCallback.Function = nullptr; - OnNamespaceConnectedCallback.Function = nullptr; - OnNamespaceDisconnectedCallback.Function = nullptr; - OnFailCallback.Function = nullptr; + NativeClient->ClearCallbacks(); } bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index e05ca0b1..1df93a4a 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -8,8 +8,9 @@ FSocketIONative::FSocketIONative() { ConnectionThread = nullptr; PrivateClient = nullptr; - AddressAndPort = FString(TEXT("http://localhost:3000")); //default to 127.0.0.1 - SessionId = FString(TEXT("invalid")); + AddressAndPort = TEXT("http://localhost:3000"); //default to 127.0.0.1 + SessionId = TEXT("Invalid"); + LastSessionId = TEXT("None"); bIsConnected = false; ClearCallbacks(); @@ -41,17 +42,12 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; FString DisconnectReasonString = USIOJConvert::EnumToString(TEXT("ESIOConnectionCloseReason"), DisconnectReason); UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); - SessionId = FString(TEXT("invalid")); + LastSessionId = SessionId; + SessionId = TEXT("Invalid"); - if (OnDisconnectedCallbacks.Num()>0) + if (OnDisconnectedCallback) { - for (auto& Wrapper : OnDisconnectedCallbacks) - { - if (Wrapper.Function) - { - Wrapper.Function(DisconnectReason); - } - } + OnDisconnectedCallback(DisconnectReason); } })); @@ -69,62 +65,42 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); - if (OnConnectedCallbacks.Num() > 0) + if (OnConnectedCallback) { - for (auto& Wrapper : OnConnectedCallbacks) - { - if (Wrapper.Function) - { - Wrapper.Function(SessionId); - } - } + OnConnectedCallback(SessionId); } } FString Namespace = USIOMessageConvert::FStringFromStd(nsp); UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); - if (OnNamespaceConnectedCallbacks.Num() > 0) + if (OnNamespaceConnectedCallback) { - for (auto& Wrapper : OnNamespaceConnectedCallbacks) - { - if (Wrapper.Function) - { - Wrapper.Function(Namespace); - } - } + OnNamespaceConnectedCallback(Namespace); } })); PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp) { FString Namespace = USIOMessageConvert::FStringFromStd(nsp); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO disconnected from namespace: %s"), *Namespace); - - if (OnNamespaceDisconnectedCallbacks.Num() > 0) + FString NamespaceSession = SessionId; + if(NamespaceSession.Equals(TEXT("Invalid"))) { - for (auto& Wrapper : OnNamespaceDisconnectedCallbacks) - { - if (Wrapper.Function) - { - Wrapper.Function(USIOMessageConvert::FStringFromStd(nsp)); - } - } + NamespaceSession = LastSessionId; + } + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace); + if (OnNamespaceDisconnectedCallback) + { + OnNamespaceDisconnectedCallback(USIOMessageConvert::FStringFromStd(nsp)); } })); PrivateClient->set_fail_listener(sio::client::con_listener([&]() { UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); - if (OnFailCallbacks.Num() > 0) + if (OnFailCallback) { - for (auto Wrapper : OnFailCallbacks) - { - if (Wrapper.Function) - { - Wrapper.Function(); - } - } + OnFailCallback(); } })); @@ -153,24 +129,22 @@ void FSocketIONative::Disconnect() void FSocketIONative::SyncDisconnect() { - /*if (OnDisconnectedCallbacks.Num() > 0) + if (OnDisconnectedCallback) { - for (auto Callback : OnDisconnectedCallbacks) - { - Callback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); - } - }*/ + OnDisconnectedCallback(ESIOConnectionCloseReason::CLOSE_REASON_NORMAL); + } ClearCallbacks(); PrivateClient->sync_close(); } void FSocketIONative::ClearCallbacks() { - OnConnectedCallbacks.Empty(); - OnDisconnectedCallbacks.Empty(); - OnNamespaceConnectedCallbacks.Empty(); - OnNamespaceDisconnectedCallbacks.Empty(); - OnFailCallbacks.Empty(); + EventFunctionMap.Empty(); + OnConnectedCallback = nullptr; + OnDisconnectedCallback = nullptr; + OnNamespaceConnectedCallback = nullptr; + OnNamespaceDisconnectedCallback = nullptr; + OnFailCallback = nullptr; } void FSocketIONative::Emit(const FString& EventName, const TSharedPtr& Message /*= nullptr*/, TFunction< void(const TArray>&)> CallbackFunction /*= nullptr*/, const FString& Namespace /*= FString(TEXT("/"))*/) @@ -255,6 +229,8 @@ void FSocketIONative::EmitRawBinary(const FString& EventName, uint8* Data, int32 void FSocketIONative::OnEvent(const FString& EventName, TFunction< void(const FString&, const TSharedPtr&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/) { + EventFunctionMap.Add(EventName, CallbackFunction); + OnRawEvent(EventName, [&, CallbackFunction](const FString& Event, const sio::message::ptr& RawMessage) { CallbackFunction(Event, USIOMessageConvert::ToJsonValue(RawMessage)); }, Namespace); @@ -262,37 +238,18 @@ void FSocketIONative::OnEvent(const FString& EventName, TFunction< void(const FS void FSocketIONative::OnRawEvent(const FString& EventName, TFunction< void(const FString&, const sio::message::ptr&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/) { - //const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = CallbackFunction; //copy the function so it remains in context - if (!EventFunctionMap.Contains(EventName)) - { - TArray> FunctionArray; - EventFunctionMap.Add(EventName, FunctionArray); - } - - TArray>& FunctionArray = EventFunctionMap[EventName]; - FunctionArray.Add(CallbackFunction); + const TFunction< void(const FString&, const sio::message::ptr&)> SafeFunction = CallbackFunction; //copy the function so it remains in context PrivateClient->socket(USIOMessageConvert::StdString(Namespace))->on( USIOMessageConvert::StdString(EventName), sio::socket::event_listener_aux( - [&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) + [&, SafeFunction](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) { const FString SafeName = USIOMessageConvert::FStringFromStd(name); - FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeName, data] + FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeFunction, SafeName, data] { - auto& FunctionArray = EventFunctionMap[SafeName]; - - for (auto& Callback : FunctionArray) - { - Callback(SafeName, data); - } - /*for (auto& Pair : EventFunctionMap) - { - Pair.Value(SafeName, data); - }*/ - //SafeFunction(SafeName, data); - + SafeFunction(SafeName, data); }, TStatId(), nullptr, ENamedThreads::GameThread); })); } @@ -328,3 +285,8 @@ void FSocketIONative::OnBinaryEvent(const FString& EventName, TFunction< void(co })); } +void FSocketIONative::UnbindEvent(const FString& EventName, const FString& Namespace /*= TEXT("/")*/) +{ + OnRawEvent(EventName, nullptr, Namespace); +} + diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 390c0c08..190d5498 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -302,13 +302,6 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent virtual void BeginPlay() override; protected: - - //Internal Nativecallbacks - TSetFunctionWrapper> OnConnectedCallback; - TSetFunctionWrapper> OnDisconnectedCallback; - TSetFunctionWrapper> OnNamespaceConnectedCallback; - TSetFunctionWrapper> OnNamespaceDisconnectedCallback; - TSetFunctionWrapper> OnFailCallback; void SetupCallbacks(); void ClearCallbacks(); diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index 78340d3f..4f7df2f2 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -14,6 +14,7 @@ enum ESIOConnectionCloseReason }; //Wrapper function for TFunctions which can be hashed based on pointers. I.e. no duplicate functions allowed +//NB: Not currently used template struct TSetFunctionWrapper { @@ -42,12 +43,14 @@ SOCKETIOCLIENT_API class FSocketIONative public: //Native Callbacks - TSet>> OnConnectedCallbacks; //TFunction - TSet>> OnDisconnectedCallbacks; //TFunction - TSet>> OnNamespaceConnectedCallbacks; //TFunction - TSet>> OnNamespaceDisconnectedCallbacks; //TFunction - TSet>> OnFailCallbacks; //TFunction - TMap>> EventFunctionMap; + TFunction OnConnectedCallback; //TFunction + TFunction OnDisconnectedCallback; //TFunction + TFunction OnNamespaceConnectedCallback; //TFunction + TFunction OnNamespaceDisconnectedCallback; //TFunction + TFunction OnFailCallback; + + //Map for all native functions bound to this socket + TMap&)>> EventFunctionMap; /** Default connection address string in form e.g. http://localhost:80. */ FString AddressAndPort; @@ -58,6 +61,9 @@ SOCKETIOCLIENT_API class FSocketIONative /** When connected this session id will be valid and contain a unique Id. */ FString SessionId; + //This will remain valid even after we disconnect. Replaced on disconnect. + FString LastSessionId; + FSocketIONative(); /** @@ -97,7 +103,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, const TSharedPtr& Message = nullptr, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with a Json Object message @@ -111,7 +117,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, const TSharedPtr& ObjectMessage = nullptr, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with a string message @@ -125,7 +131,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, const FString& StringMessage = FString(), TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with a number (double) message @@ -139,7 +145,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, double NumberMessage, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with a bool message @@ -153,7 +159,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, bool BooleanMessage, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with a binary message @@ -167,7 +173,7 @@ SOCKETIOCLIENT_API class FSocketIONative (const FString& EventName, const TArray& BinaryMessage, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with an array message @@ -181,7 +187,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, const TArray>& ArrayMessage, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * (Overloaded) Emit an event with an UStruct message @@ -197,7 +203,7 @@ SOCKETIOCLIENT_API class FSocketIONative UStruct* Struct, const void* StructPtr, TFunction< void(const TArray>&)> CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * Emit a raw sio::message event @@ -211,7 +217,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, const sio::message::list& MessageList = nullptr, TFunction CallbackFunction = nullptr, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * Emit an optimized binary message @@ -225,7 +231,7 @@ SOCKETIOCLIENT_API class FSocketIONative const FString& EventName, uint8* Data, int32 DataLength, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** @@ -238,7 +244,7 @@ SOCKETIOCLIENT_API class FSocketIONative void OnEvent( const FString& EventName, TFunction< void(const FString&, const TSharedPtr&)> CallbackFunction, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * Call function callback on receiving raw event. C++ only. @@ -250,7 +256,7 @@ SOCKETIOCLIENT_API class FSocketIONative void OnRawEvent( const FString& EventName, TFunction< void(const FString&, const sio::message::ptr&)> CallbackFunction, - const FString& Namespace = FString(TEXT("/"))); + const FString& Namespace = TEXT("/")); /** * Call function callback on receiving binary event. C++ only. * @@ -261,18 +267,14 @@ SOCKETIOCLIENT_API class FSocketIONative void OnBinaryEvent( const FString& EventName, TFunction< void(const FString&, const TArray&)> CallbackFunction, - const FString& Namespace = FString(TEXT("/"))); - - //TSet functions - /*bool operator==(const FSocketIONative& Other) const - { - return GetTypeHash(Other) == GetTypeHash(this); - } + const FString& Namespace = TEXT("/")); - friend FORCEINLINE uint32 GetTypeHash(const FSocketIONative& Key) - { - return ::PointerHash(&Key); - }*/ + /** + * Unbinds currently bound callback from given event. + * + * @param EventName Event name + */ + void UnbindEvent(const FString& EventName, const FString& Namespace = TEXT("/")); protected: TSharedPtr PrivateClient; From 9184efd81b5fc4483a4132fd9cc3b0adf3fba8bd Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 05:19:54 +0000 Subject: [PATCH 15/23] move internal callbacks to separate function --- .../Private/SocketIOClientComponent.cpp | 5 +- .../SocketIOClient/Private/SocketIONative.cpp | 154 +++++++++--------- Source/SocketIOClient/Public/SocketIONative.h | 2 + 3 files changed, 85 insertions(+), 76 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 3f94e4a1..3fb789dc 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -139,7 +139,10 @@ void USocketIOClientComponent::SetupCallbacks() void USocketIOClientComponent::ClearCallbacks() { - NativeClient->ClearCallbacks(); + if (NativeClient.IsValid()) + { + NativeClient->ClearCallbacks(); + } } bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray> Response) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 1df93a4a..584afa6a 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -29,81 +29,6 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< //Connect to the server on a background thread so it never blocks ConnectionThread = FSIOLambdaRunnable::RunLambdaOnBackGroundThread([&, Query, Headers] { - //Attach the specific connection status events events - - PrivateClient->set_open_listener(sio::client::con_listener([&]() { - //too early to get session id here so we defer the connection event until we connect to a namespace - })); - - PrivateClient->set_close_listener(sio::client::close_listener([&](sio::client::close_reason const& reason) - { - bIsConnected = false; - - ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; - FString DisconnectReasonString = USIOJConvert::EnumToString(TEXT("ESIOConnectionCloseReason"), DisconnectReason); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); - LastSessionId = SessionId; - SessionId = TEXT("Invalid"); - - if (OnDisconnectedCallback) - { - OnDisconnectedCallback(DisconnectReason); - } - })); - - PrivateClient->set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp) - { - //Special case, we have a latent connection after already having been disconnected - if (!PrivateClient.IsValid()) - { - return; - } - if (!bIsConnected) - { - bIsConnected = true; - SessionId = USIOMessageConvert::FStringFromStd(PrivateClient->get_sessionid()); - - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); - - if (OnConnectedCallback) - { - OnConnectedCallback(SessionId); - } - } - - FString Namespace = USIOMessageConvert::FStringFromStd(nsp); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); - - if (OnNamespaceConnectedCallback) - { - OnNamespaceConnectedCallback(Namespace); - } - })); - - PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp) - { - FString Namespace = USIOMessageConvert::FStringFromStd(nsp); - FString NamespaceSession = SessionId; - if(NamespaceSession.Equals(TEXT("Invalid"))) - { - NamespaceSession = LastSessionId; - } - UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace); - if (OnNamespaceDisconnectedCallback) - { - OnNamespaceDisconnectedCallback(USIOMessageConvert::FStringFromStd(nsp)); - } - })); - - PrivateClient->set_fail_listener(sio::client::con_listener([&]() - { - UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); - if (OnFailCallback) - { - OnFailCallback(); - } - })); - std::map QueryMap = {}; std::map HeadersMap = {}; @@ -139,7 +64,10 @@ void FSocketIONative::SyncDisconnect() void FSocketIONative::ClearCallbacks() { + PrivateClient->clear_socket_listeners(); + SetupInternalCallbacks(); //if clear socket listeners cleared our internal callbacks. reset them EventFunctionMap.Empty(); + OnConnectedCallback = nullptr; OnDisconnectedCallback = nullptr; OnNamespaceConnectedCallback = nullptr; @@ -290,3 +218,79 @@ void FSocketIONative::UnbindEvent(const FString& EventName, const FString& Names OnRawEvent(EventName, nullptr, Namespace); } +void FSocketIONative::SetupInternalCallbacks() +{ + PrivateClient->set_open_listener(sio::client::con_listener([&]() { + //too early to get session id here so we defer the connection event until we connect to a namespace + })); + + PrivateClient->set_close_listener(sio::client::close_listener([&](sio::client::close_reason const& reason) + { + bIsConnected = false; + + ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; + FString DisconnectReasonString = USIOJConvert::EnumToString(TEXT("ESIOConnectionCloseReason"), DisconnectReason); + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); + LastSessionId = SessionId; + SessionId = TEXT("Invalid"); + + if (OnDisconnectedCallback) + { + OnDisconnectedCallback(DisconnectReason); + } + })); + + PrivateClient->set_socket_open_listener(sio::client::socket_listener([&](std::string const& nsp) + { + //Special case, we have a latent connection after already having been disconnected + if (!PrivateClient.IsValid()) + { + return; + } + if (!bIsConnected) + { + bIsConnected = true; + SessionId = USIOMessageConvert::FStringFromStd(PrivateClient->get_sessionid()); + + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); + + if (OnConnectedCallback) + { + OnConnectedCallback(SessionId); + } + } + + FString Namespace = USIOMessageConvert::FStringFromStd(nsp); + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); + + if (OnNamespaceConnectedCallback) + { + OnNamespaceConnectedCallback(Namespace); + } + })); + + PrivateClient->set_socket_close_listener(sio::client::socket_listener([&](std::string const& nsp) + { + FString Namespace = USIOMessageConvert::FStringFromStd(nsp); + FString NamespaceSession = SessionId; + if (NamespaceSession.Equals(TEXT("Invalid"))) + { + NamespaceSession = LastSessionId; + } + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace); + if (OnNamespaceDisconnectedCallback) + { + OnNamespaceDisconnectedCallback(USIOMessageConvert::FStringFromStd(nsp)); + } + })); + + PrivateClient->set_fail_listener(sio::client::con_listener([&]() + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); + if (OnFailCallback) + { + OnFailCallback(); + } + })); +} + diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index 4f7df2f2..f3227bd7 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -277,6 +277,8 @@ SOCKETIOCLIENT_API class FSocketIONative void UnbindEvent(const FString& EventName, const FString& Namespace = TEXT("/")); protected: + void SetupInternalCallbacks(); + TSharedPtr PrivateClient; class FSIOLambdaRunnable* ConnectionThread; }; \ No newline at end of file From c4d77628db957ff44e5a77a32fe3e1c87de5229b Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 05:21:42 +0000 Subject: [PATCH 16/23] order fix --- Source/SocketIOClient/Private/SocketIONative.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 584afa6a..8c2f920a 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -13,9 +13,9 @@ FSocketIONative::FSocketIONative() LastSessionId = TEXT("None"); bIsConnected = false; - ClearCallbacks(); - PrivateClient = MakeShareable(new sio::client); + + ClearCallbacks(); } void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr& Query /*= nullptr*/, const TSharedPtr& Headers /*= nullptr*/) From a3da5cbfc13a5b5b1b1ef2935ca1a0656952a4ed Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 05:25:56 +0000 Subject: [PATCH 17/23] sync session id and address if already connected --- Source/SocketIOClient/Private/SocketIOClientComponent.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 3fb789dc..d73551ab 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -76,6 +76,11 @@ void USocketIOClientComponent::SetupCallbacks() { //Sync current connected state bIsConnected = NativeClient->bIsConnected; + if (bIsConnected) + { + SessionId = NativeClient->SessionId; + AddressAndPort = NativeClient->AddressAndPort; + } NativeClient->OnConnectedCallback = [this](const FString& InSessionId) { From 21e8caa93500f3459e006db2c8a990aff39c6326 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 06:34:49 +0000 Subject: [PATCH 18/23] add control over reconnection and callback -add also log verbosity option -we can timeout our reconnection in many different ways -todo: add close_reason_stopped_trying link --- .../SocketIOClient/Private/SocketIOClient.cpp | 2 +- .../Private/SocketIOClientComponent.cpp | 39 +++++++++++- .../SocketIOClient/Private/SocketIONative.cpp | 44 +++++++++++-- .../Public/SocketIOClientComponent.h | 61 +++++++++++++++---- Source/SocketIOClient/Public/SocketIONative.h | 13 +++- 5 files changed, 136 insertions(+), 23 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClient.cpp b/Source/SocketIOClient/Private/SocketIOClient.cpp index c0340eda..2b4323f5 100644 --- a/Source/SocketIOClient/Private/SocketIOClient.cpp +++ b/Source/SocketIOClient/Private/SocketIOClient.cpp @@ -104,7 +104,7 @@ TSharedPtr FSocketIOClientModule::ValidSharedNativePointer(FStr void FSocketIOClientModule::ReleaseNativePointer(TSharedPtr PointerToRelease) { - //Remove shared ptr references + //Remove shared ptr references if any if (AllSharedPtrs.Contains(PointerToRelease)) { AllSharedPtrs.Remove(PointerToRelease); diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index d73551ab..09a85433 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -13,11 +13,13 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini NativeClient = nullptr; bLimitConnectionToGameWorld = true; AddressAndPort = FString(TEXT("http://localhost:3000")); //default to 127.0.0.1 - SessionId = FString(TEXT("invalid")); + SessionId = FString(TEXT("Invalid")); //Plugin scoped utilities bPluginScopedConnection = false; PluginScopedId = TEXT("Default"); + bVerboseConnectionLog = true; + ReconnectionTimeout = 0.f; ClearCallbacks(); } @@ -113,7 +115,7 @@ void USocketIOClientComponent::SetupCallbacks() { FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] { - if (this) + if (this && OnSocketNamespaceConnected.IsBound()) { OnSocketNamespaceConnected.Broadcast(Namespace); } @@ -132,6 +134,32 @@ void USocketIOClientComponent::SetupCallbacks() } }); }; + NativeClient->OnReconnectionCallback = [this](const uint32 AttemptCount, const uint32 DelayInMs) + { + FSIOLambdaRunnable::RunShortLambdaOnGameThread([this, AttemptCount, DelayInMs] + { + //First time we know about this problem? + if (!bIsHavingConnectionProblems) + { + TimeWhenConnectionProblemsStarted = FDateTime::Now(); + bIsHavingConnectionProblems = true; + } + + FTimespan Difference = FDateTime::Now() - TimeWhenConnectionProblemsStarted; + float ElapsedInSec = Difference.GetTotalSeconds(); + + if (ReconnectionTimeout > 0 && ElapsedInSec>ReconnectionTimeout) + { + //Let's stop trying and disconnect if we're using timeouts + Disconnect(); + } + + if (this && OnConnectionProblems.IsBound()) + { + OnConnectionProblems.Broadcast(AttemptCount, ElapsedInSec); + } + }); + }; NativeClient->OnFailCallback = [this]() { @@ -295,6 +323,8 @@ bool USocketIOClientComponent::CallBPFunctionWithMessage(UObject* Target, const void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJsonObject* Query /*= nullptr*/, USIOJsonObject* Headers /*= nullptr*/) { + + //Check if we're limiting this component if(bLimitConnectionToGameWorld) { @@ -326,6 +356,11 @@ void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJson { HeadersFJson = Headers->GetRootObject(); } + + //Ensure we sync our native max/reconnection attempts before connecting + NativeClient->MaxReconnectionAttempts = MaxReconnectionAttempts; + NativeClient->ReconnectionDelay = ReconnectionDelay; + NativeClient->VerboseLog = bVerboseConnectionLog; ConnectNative(InAddressAndPort, QueryFJson, HeadersFJson); } diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 8c2f920a..55550e98 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -12,6 +12,8 @@ FSocketIONative::FSocketIONative() SessionId = TEXT("Invalid"); LastSessionId = TEXT("None"); bIsConnected = false; + MaxReconnectionAttempts = -1; + ReconnectionDelay = 5000; PrivateClient = MakeShareable(new sio::client); @@ -43,6 +45,9 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr< QueryMap = USIOMessageConvert::JsonObjectToStdStringMap(Query); } + PrivateClient->set_reconnect_attempts(MaxReconnectionAttempts); + PrivateClient->set_reconnect_delay(ReconnectionDelay); + PrivateClient->connect(StdAddressString, QueryMap, HeadersMap); }); } @@ -72,6 +77,7 @@ void FSocketIONative::ClearCallbacks() OnDisconnectedCallback = nullptr; OnNamespaceConnectedCallback = nullptr; OnNamespaceDisconnectedCallback = nullptr; + OnReconnectionCallback = nullptr; OnFailCallback = nullptr; } @@ -230,7 +236,10 @@ void FSocketIONative::SetupInternalCallbacks() ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; FString DisconnectReasonString = USIOJConvert::EnumToString(TEXT("ESIOConnectionCloseReason"), DisconnectReason); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString); + } LastSessionId = SessionId; SessionId = TEXT("Invalid"); @@ -252,8 +261,10 @@ void FSocketIONative::SetupInternalCallbacks() bIsConnected = true; SessionId = USIOMessageConvert::FStringFromStd(PrivateClient->get_sessionid()); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); - + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO Connected with session: %s"), *SessionId); + } if (OnConnectedCallback) { OnConnectedCallback(SessionId); @@ -261,8 +272,11 @@ void FSocketIONative::SetupInternalCallbacks() } FString Namespace = USIOMessageConvert::FStringFromStd(nsp); - UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s connected to namespace: %s"), *SessionId, *Namespace); + } if (OnNamespaceConnectedCallback) { OnNamespaceConnectedCallback(Namespace); @@ -277,7 +291,10 @@ void FSocketIONative::SetupInternalCallbacks() { NamespaceSession = LastSessionId; } - UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace); + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s disconnected from namespace: %s"), *NamespaceSession, *Namespace); + } if (OnNamespaceDisconnectedCallback) { OnNamespaceDisconnectedCallback(USIOMessageConvert::FStringFromStd(nsp)); @@ -286,11 +303,26 @@ void FSocketIONative::SetupInternalCallbacks() PrivateClient->set_fail_listener(sio::client::con_listener([&]() { - UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO failed to connect.")); + } if (OnFailCallback) { OnFailCallback(); } })); + + PrivateClient->set_reconnect_listener(sio::client::reconnect_listener([&](unsigned num, unsigned delay) + { + if (VerboseLog) + { + UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s appears to have lost connection, reconnecting attempt %d with delay %d"), *SessionId, num, delay); + } + if (OnReconnectionCallback) + { + OnReconnectionCallback(num, delay); + } + })); } diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 190d5498..cd2ab703 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -9,6 +9,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCSocketEventSignature, FString, DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCOpenEventSignature, FString, SessionId); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByte, Reason); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCEventJsonSignature, FString, Event, class USIOJsonValue*, MessageJson); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOConnectionProblemSignature, int32, Attempts, float, TimeSinceConnected); UCLASS(ClassGroup = "Networking", meta = (BlueprintSpawnableComponent)) class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent @@ -18,29 +19,42 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent //Async events - /** Event received on socket.io connection established. */ + /** On bound event received. */ + UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") + FSIOCEventJsonSignature OnEvent; + + /** Received on socket.io connection established. */ UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") FSIOCOpenEventSignature OnConnected; - /** Event received on socket.io connection disconnected. */ + /** + * Received on socket.io connection disconnected. This may never get + * called in default settings, see OnConnectionProblems event for details. + */ UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") FSIOCCloseEventSignature OnDisconnected; - /** Event received on having joined namespace. */ + /** + * Received when connection problems arise. In default settings the + * connection will keep repeating trying to reconnect an infinite + * amount of times and you may never get OnDisconnected callback + * unless you call it. + */ + UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") + FSIOConnectionProblemSignature OnConnectionProblems; + + /** Received on having joined namespace. */ UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") FSIOCSocketEventSignature OnSocketNamespaceConnected; - /** Event received on having left namespace. */ + /** Received on having left namespace. */ UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") FSIOCSocketEventSignature OnSocketNamespaceDisconnected; - /** Event received on connection failure. */ + /** Received on connection failure. */ UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") FSIOCEventSignature OnFail; - /** On bound event received. */ - UPROPERTY(BlueprintAssignable, Category = "SocketIO Events") - FSIOCEventJsonSignature OnEvent; /** Default connection address string in form e.g. http://localhost:80. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") @@ -53,11 +67,28 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") bool bLimitConnectionToGameWorld; + /** in ms */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + int32 ReconnectionDelay; + + /** default: infinity, this means you never truly disconnect, just suffer connection problems */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + int32 MaxReconnectionAttempts; + + /** Optional parameter to limit reconnections default: infinity. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + float ReconnectionTimeout; + + FDateTime TimeWhenConnectionProblemsStarted; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + bool bVerboseConnectionLog; + /** * Toggle which enables plugin scoped connections. * If you enable this the connection will remain until you manually call disconnect - * or close the app. Additionally any connections with the same PluginScopedId will use the same connection - * and receive the same events. + * or close the app. The latest connection with the same PluginScopedId will use the same connection + * as the previous one and receive the same events. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") bool bPluginScopedConnection; @@ -70,9 +101,12 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent bool bIsConnected; /** When connected this session id will be valid and contain a unique Id. */ - UPROPERTY(BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") FString SessionId; + UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") + bool bIsHavingConnectionProblems; + /** * Connect to a socket.io server, optional method if auto-connect is set to true. * Query and headers are defined by a {'stringKey':'stringValue'} SIOJson Object @@ -88,8 +122,9 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent USIOJsonObject* Headers = nullptr); /** - * Disconnect from current socket.io server. Subscribe to OnDisconnected to know - * when you can continue from a disconnected state. + * Disconnect from current socket.io server. This is an asynchronous action, + * subscribe to OnDisconnected to know when you can safely continue from a + * disconnected state. * * @param AddressAndPort the address in URL format with port */ diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index f3227bd7..f4457e94 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -10,7 +10,8 @@ UENUM(BlueprintType) enum ESIOConnectionCloseReason { CLOSE_REASON_NORMAL, - CLOSE_REASON_DROP + CLOSE_REASON_DROP, + CLOSE_REASON_STOPPED_TRYING }; //Wrapper function for TFunctions which can be hashed based on pointers. I.e. no duplicate functions allowed @@ -47,6 +48,7 @@ SOCKETIOCLIENT_API class FSocketIONative TFunction OnDisconnectedCallback; //TFunction TFunction OnNamespaceConnectedCallback; //TFunction TFunction OnNamespaceDisconnectedCallback; //TFunction + TFunction OnReconnectionCallback; TFunction OnFailCallback; //Map for all native functions bound to this socket @@ -55,6 +57,12 @@ SOCKETIOCLIENT_API class FSocketIONative /** Default connection address string in form e.g. http://localhost:80. */ FString AddressAndPort; + /** The number of attempts before giving up. 0 = infinity. Set before connecting*/ + uint32 MaxReconnectionAttempts; + + /** in milliseconds, default is 5000 */ + uint32 ReconnectionDelay; + /** If true will auto-connect on begin play to address specified in AddressAndPort. */ bool bIsConnected; @@ -64,6 +72,9 @@ SOCKETIOCLIENT_API class FSocketIONative //This will remain valid even after we disconnect. Replaced on disconnect. FString LastSessionId; + /** If set to true, each state change callback will log to console*/ + bool VerboseLog; + FSocketIONative(); /** From 233a3ee203bdb789d2b52d47496d7269230c9f2d Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 06:45:21 +0000 Subject: [PATCH 19/23] fix defaults and add bool to connected signature --- .../Private/SocketIOClientComponent.cpp | 7 +++- .../Public/SocketIOClientComponent.h | 37 ++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 09a85433..4aadcbc2 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -20,6 +20,8 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini PluginScopedId = TEXT("Default"); bVerboseConnectionLog = true; ReconnectionTimeout = 0.f; + MaxReconnectionAttempts = -1.f; + ReconnectionDelayInMs = 5000; ClearCallbacks(); } @@ -92,7 +94,8 @@ void USocketIOClientComponent::SetupCallbacks() { bIsConnected = true; SessionId = InSessionId; - OnConnected.Broadcast(SessionId); + OnConnected.Broadcast(SessionId, bIsHavingConnectionProblems); + bIsHavingConnectionProblems = false; } }); }; @@ -359,7 +362,7 @@ void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJson //Ensure we sync our native max/reconnection attempts before connecting NativeClient->MaxReconnectionAttempts = MaxReconnectionAttempts; - NativeClient->ReconnectionDelay = ReconnectionDelay; + NativeClient->ReconnectionDelay = ReconnectionDelayInMs; NativeClient->VerboseLog = bVerboseConnectionLog; ConnectNative(InAddressAndPort, QueryFJson, HeadersFJson); diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index cd2ab703..9e247e6c 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -6,7 +6,7 @@ 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_TwoParams(FSIOCOpenEventSignature, FString, SessionId, bool, bIsReconnection); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByte, Reason); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCEventJsonSignature, FString, Event, class USIOJsonValue*, MessageJson); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOConnectionProblemSignature, int32, Attempts, float, TimeSinceConnected); @@ -57,31 +57,34 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent /** Default connection address string in form e.g. http://localhost:80. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") FString AddressAndPort; /** If true will auto-connect on begin play to address specified in AddressAndPort. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") bool bShouldAutoConnect; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") bool bLimitConnectionToGameWorld; - /** in ms */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") - int32 ReconnectionDelay; + /** Delay between reconnection attempts */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") + int32 ReconnectionDelayInMs; - /** default: infinity, this means you never truly disconnect, just suffer connection problems */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + /** + * Number of times the connection should try before giving up. + * Default: infinity, this means you never truly disconnect, just suffer connection problems + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") int32 MaxReconnectionAttempts; - /** Optional parameter to limit reconnections default: infinity. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + /** Optional parameter to limit reconnections by elapsed time. Default: infinity. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") float ReconnectionTimeout; FDateTime TimeWhenConnectionProblemsStarted; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") bool bVerboseConnectionLog; /** @@ -90,21 +93,21 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent * or close the app. The latest connection with the same PluginScopedId will use the same connection * as the previous one and receive the same events. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Plugin Scope Properties") bool bPluginScopedConnection; /** If you leave this as is all plugin scoped connection components will share same connection*/ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Plugin Scope Properties") FString PluginScopedId; - UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") + UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties") bool bIsConnected; /** When connected this session id will be valid and contain a unique Id. */ - UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") + UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties") FString SessionId; - UPROPERTY(BlueprintReadOnly, Category = "SocketIO Properties") + UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties") bool bIsHavingConnectionProblems; /** From 94e8f997249b90e4fa9df532bfbb121480d6fe54 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 06:47:43 +0000 Subject: [PATCH 20/23] move descriptions --- .../Private/SocketIOClientComponent.cpp | 2 -- .../SocketIOClient/Public/SocketIOClientComponent.h | 11 ++++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index 4aadcbc2..e069a795 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -326,8 +326,6 @@ bool USocketIOClientComponent::CallBPFunctionWithMessage(UObject* Target, const void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJsonObject* Query /*= nullptr*/, USIOJsonObject* Headers /*= nullptr*/) { - - //Check if we're limiting this component if(bLimitConnectionToGameWorld) { diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 9e247e6c..4e41f6cc 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -64,9 +64,6 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") bool bShouldAutoConnect; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") - bool bLimitConnectionToGameWorld; - /** Delay between reconnection attempts */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") int32 ReconnectionDelayInMs; @@ -87,17 +84,21 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") bool bVerboseConnectionLog; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") + bool bLimitConnectionToGameWorld; + /** * Toggle which enables plugin scoped connections. * If you enable this the connection will remain until you manually call disconnect * or close the app. The latest connection with the same PluginScopedId will use the same connection * as the previous one and receive the same events. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Plugin Scope Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties") bool bPluginScopedConnection; /** If you leave this as is all plugin scoped connection components will share same connection*/ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Plugin Scope Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties") FString PluginScopedId; UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties") From f6fc9041974cd7031f97ee16ccf869f5ad6ed7cd Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 06:59:16 +0000 Subject: [PATCH 21/23] if we're having connection problems we're not connected --- Source/SocketIOClient/Private/SocketIONative.cpp | 5 ++++- Source/SocketIOClient/Public/SocketIONative.h | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 55550e98..b4c6084d 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -163,6 +163,7 @@ void FSocketIONative::EmitRawBinary(const FString& EventName, uint8* Data, int32 void FSocketIONative::OnEvent(const FString& EventName, TFunction< void(const FString&, const TSharedPtr&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/) { + //Keep track of all the bound functions EventFunctionMap.Add(EventName, CallbackFunction); OnRawEvent(EventName, [&, CallbackFunction](const FString& Event, const sio::message::ptr& RawMessage) { @@ -183,7 +184,7 @@ void FSocketIONative::OnRawEvent(const FString& EventName, TFunction< void(const FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeFunction, SafeName, data] { - SafeFunction(SafeName, data); + SafeFunction(SafeName, data); }, TStatId(), nullptr, ENamedThreads::GameThread); })); } @@ -315,6 +316,8 @@ void FSocketIONative::SetupInternalCallbacks() PrivateClient->set_reconnect_listener(sio::client::reconnect_listener([&](unsigned num, unsigned delay) { + bIsConnected = false; + if (VerboseLog) { UE_LOG(SocketIOLog, Log, TEXT("SocketIO %s appears to have lost connection, reconnecting attempt %d with delay %d"), *SessionId, num, delay); diff --git a/Source/SocketIOClient/Public/SocketIONative.h b/Source/SocketIOClient/Public/SocketIONative.h index f4457e94..049657d0 100644 --- a/Source/SocketIOClient/Public/SocketIONative.h +++ b/Source/SocketIOClient/Public/SocketIONative.h @@ -10,8 +10,7 @@ UENUM(BlueprintType) enum ESIOConnectionCloseReason { CLOSE_REASON_NORMAL, - CLOSE_REASON_DROP, - CLOSE_REASON_STOPPED_TRYING + CLOSE_REASON_DROP }; //Wrapper function for TFunctions which can be hashed based on pointers. I.e. no duplicate functions allowed From 0268c23fff5920c6825694b92d1d915ef2271cf0 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 07:17:05 +0000 Subject: [PATCH 22/23] add delay param in bp callbacks --- .../SocketIOClient/Private/SocketIOClientComponent.cpp | 9 ++------- Source/SocketIOClient/Public/SocketIOClientComponent.h | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp index e069a795..66a98e0b 100644 --- a/Source/SocketIOClient/Private/SocketIOClientComponent.cpp +++ b/Source/SocketIOClient/Private/SocketIOClientComponent.cpp @@ -159,7 +159,7 @@ void USocketIOClientComponent::SetupCallbacks() if (this && OnConnectionProblems.IsBound()) { - OnConnectionProblems.Broadcast(AttemptCount, ElapsedInSec); + OnConnectionProblems.Broadcast(AttemptCount, DelayInMs, ElapsedInSec); } }); }; @@ -368,12 +368,7 @@ void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJson void USocketIOClientComponent::ConnectNative(const FString& InAddressAndPort, const TSharedPtr& Query /*= nullptr*/, const TSharedPtr& Headers /*= nullptr*/) { - //Set native callback functions - - - { - NativeClient->Connect(InAddressAndPort, Query, Headers); - } + NativeClient->Connect(InAddressAndPort, Query, Headers); } void USocketIOClientComponent::Disconnect() diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 4e41f6cc..10cea4bd 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -9,7 +9,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCSocketEventSignature, FString, DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCOpenEventSignature, FString, SessionId, bool, bIsReconnection); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByte, Reason); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCEventJsonSignature, FString, Event, class USIOJsonValue*, MessageJson); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOConnectionProblemSignature, int32, Attempts, float, TimeSinceConnected); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSIOConnectionProblemSignature, int32, Attempts, int32, NextAttemptInMs, float, TimeSinceConnected); UCLASS(ClassGroup = "Networking", meta = (BlueprintSpawnableComponent)) class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent From e4d618b270d8f68100cf757de1dea0361ae5b0a8 Mon Sep 17 00:00:00 2001 From: getnamo Date: Sat, 11 Nov 2017 07:32:15 +0000 Subject: [PATCH 23/23] change default grouping --- Source/SocketIOClient/Public/SocketIOClientComponent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SocketIOClient/Public/SocketIOClientComponent.h b/Source/SocketIOClient/Public/SocketIOClientComponent.h index 10cea4bd..442cb1d3 100644 --- a/Source/SocketIOClient/Public/SocketIOClientComponent.h +++ b/Source/SocketIOClient/Public/SocketIOClientComponent.h @@ -85,7 +85,7 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent bool bVerboseConnectionLog; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Connection Properties") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SocketIO Scope Properties") bool bLimitConnectionToGameWorld; /**