Skip to content

Commit

Permalink
add static construct feature
Browse files Browse the repository at this point in the history
- Can be called in cases with an actor owner (e.g. game mode) and cases with a non-actor owner (e.g. game instance).
- In non-actor owner case, connect needs to be handled manually since the component cannot be registered
  • Loading branch information
getnamo committed Jul 28, 2019
1 parent 1172804 commit 2f5ed34
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 21 deletions.
2 changes: 1 addition & 1 deletion SocketIOClient.uplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0.7",
"VersionName": "1.0.8",
"EngineVersion" : "4.22.0",
"FriendlyName": "Socket.IO Client",
"Description": "Real-time networking library Socket.IO Client usable from blueprints and c++.",
Expand Down
69 changes: 53 additions & 16 deletions Source/SocketIOClient/Private/SocketIOClientComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,50 @@ USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &ini
MaxReconnectionAttempts = -1.f;
ReconnectionDelayInMs = 5000;

bStaticallyInitialized = false;

ClearCallbacks();
}

void USocketIOClientComponent::StaticInitialization(UObject* WorldContextObject, bool bValidOwnerWorld /*= false*/)
{
bStaticallyInitialized = true;

if (!bValidOwnerWorld)
{
//Need to allow connections to non-game world setups
bLimitConnectionToGameWorld = false;

//The auto-connect will never happen in this case, so disable for clarity
bShouldAutoConnect = false;
}

//We statically initialize for all cases
InitializeNative();
}

void USocketIOClientComponent::InitializeComponent()
{
Super::InitializeComponent();

if (!bStaticallyInitialized)
{
//Because our connections can last longer than game world
//end, we let plugin-scoped structures manage our memory
if (bPluginScopedConnection)
{
NativeClient = ISocketIOClientModule::Get().ValidSharedNativePointer(PluginScopedId);
}
else
{
NativeClient = ISocketIOClientModule::Get().NewValidNativePointer();
}

SetupCallbacks();
InitializeNative();
}
}

void USocketIOClientComponent::InitializeNative()
{
if (bPluginScopedConnection)
{
NativeClient = ISocketIOClientModule::Get().ValidSharedNativePointer(PluginScopedId);
}
else
{
NativeClient = ISocketIOClientModule::Get().NewValidNativePointer();
}

SetupCallbacks();
}

void USocketIOClientComponent::BeginPlay()
Expand All @@ -61,6 +84,18 @@ void USocketIOClientComponent::BeginPlay()
}
}

USocketIOClientComponent::~USocketIOClientComponent()
{
ClearCallbacks();

//If we're a regular connection we should close and release when we quit
if (!bPluginScopedConnection && NativeClient.IsValid())
{
ISocketIOClientModule::Get().ReleaseNativePointer(NativeClient);
NativeClient = nullptr;
}
}

void USocketIOClientComponent::UninitializeComponent()
{
//Because our connections can last longer than game world
Expand Down Expand Up @@ -185,6 +220,8 @@ void USocketIOClientComponent::ClearCallbacks()
}
}



bool USocketIOClientComponent::CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray<TSharedPtr<FJsonValue>> Response)
{
if (!Target->IsValidLowLevel())
Expand Down Expand Up @@ -530,12 +567,12 @@ void USocketIOClientComponent::EmitNative(const FString& EventName, UStruct* Str

void USocketIOClientComponent::BindEvent(const FString& EventName, const FString& Namespace)
{
NativeClient->OnRawEvent(EventName, [&](const FString& Event, const sio::message::ptr& RawMessage) {
NativeClient->OnEvent(EventName, [&](const FString& Event, const TSharedPtr<FJsonValue>& EventValue)
{
USIOJsonValue* NewValue = NewObject<USIOJsonValue>();
auto Value = USIOMessageConvert::ToJsonValue(RawMessage);
NewValue->SetRootValue(Value);
TSharedPtr<FJsonValue> NonConstValue = EventValue;
NewValue->SetRootValue(NonConstValue);
OnEvent.Broadcast(Event, NewValue);

}, Namespace);
}

Expand Down
36 changes: 36 additions & 0 deletions Source/SocketIOClient/Private/SocketIOFunctionLibrary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2019-current Getnamo. All Rights Reserved

#include "SocketIOFunctionLibrary.h"
#include "LambdaRunnable.h"
#include "Engine/Engine.h"

USocketIOClientComponent* USocketIOFunctionLibrary::ConstructSocketIOComponent(UObject* WorldContextObject)
{
USocketIOClientComponent* SpawnedComponent = NewObject<USocketIOClientComponent>(WorldContextObject, TEXT("SocketIOClientComponent"));

if (SpawnedComponent)
{
//Check if we got spawned by an object with an owner context (e.g. game mode) or not (e.g. game instance)
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
AActor* MyOwner = SpawnedComponent->GetOwner();
bool bOwnerHasValidWorld = (MyOwner ? MyOwner->GetWorld() : nullptr) != nullptr;

SpawnedComponent->StaticInitialization(WorldContextObject, bOwnerHasValidWorld);

//Register component if it was spawned by world owning context object, e.g. game mode
if (bOwnerHasValidWorld)
{
//Delay by 1 tick so that we can adjust bShouldAutoConnect/etc
FLambdaRunnable::RunShortLambdaOnGameThread([SpawnedComponent]()
{
if (SpawnedComponent->IsValidLowLevel())
{
SpawnedComponent->RegisterComponent();
}
});
}

//Otherwise we don't need to register our component for the networking logic to work.
}
return SpawnedComponent;
}
9 changes: 7 additions & 2 deletions Source/SocketIOClient/Private/SocketIONative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void FSocketIONative::Connect(const FString& InAddressAndPort, const TSharedPtr<
PrivateClient->connect(StdAddressString, QueryMap, HeadersMap);

});

}

void FSocketIONative::Connect(const FString& InAddressAndPort)
Expand Down Expand Up @@ -195,8 +196,11 @@ void FSocketIONative::EmitRawBinary(const FString& EventName, uint8* Data, int32

void FSocketIONative::OnEvent(const FString& EventName, TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> CallbackFunction, const FString& Namespace /*= FString(TEXT("/"))*/)
{
//Keep track of all the bound functions
EventFunctionMap.Add(EventName, CallbackFunction);
//Keep track of all the bound native JsonValue functions
FSIOBoundEvent BoundEvent;
BoundEvent.Function = CallbackFunction;
BoundEvent.Namespace = Namespace;
EventFunctionMap.Add(EventName, BoundEvent);

OnRawEvent(EventName, [&, CallbackFunction](const FString& Event, const sio::message::ptr& RawMessage) {
CallbackFunction(Event, USIOMessageConvert::ToJsonValue(RawMessage));
Expand Down Expand Up @@ -255,6 +259,7 @@ void FSocketIONative::OnBinaryEvent(const FString& EventName, TFunction< void(co
void FSocketIONative::UnbindEvent(const FString& EventName, const FString& Namespace /*= TEXT("/")*/)
{
OnRawEvent(EventName, nullptr, Namespace);
EventFunctionMap.Remove(EventName);
}

void FSocketIONative::SetupInternalCallbacks()
Expand Down
13 changes: 12 additions & 1 deletion Source/SocketIOClient/Public/SocketIOClientComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOCCloseEventSignature, TEnumAsByt
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSIOCEventJsonSignature, FString, Event, class USIOJsonValue*, MessageJson);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSIOConnectionProblemSignature, int32, Attempts, int32, NextAttemptInMs, float, TimeSinceConnected);

UCLASS(ClassGroup = "Networking", meta = (BlueprintSpawnableComponent))
UCLASS(BlueprintType, ClassGroup = "Networking", meta = (BlueprintSpawnableComponent))
class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
{
GENERATED_UCLASS_BODY()
Expand Down Expand Up @@ -115,6 +115,10 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
bool bIsHavingConnectionProblems;

/** If this component has been statically initialized. Largely exposed for traceability. */
UPROPERTY(BlueprintReadOnly, Category = "SocketIO Connection Properties")
bool bStaticallyInitialized;

/**
* 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
Expand Down Expand Up @@ -356,13 +360,20 @@ class SOCKETIOCLIENT_API USocketIOClientComponent : public UActorComponent
TFunction< void(const FString&, const TArray<uint8>&)> CallbackFunction,
const FString& Namespace = FString(TEXT("/")));


/** Only call this if you used ConstructSocketIOComponent from a non-world parent e.g. game instance*/
void StaticInitialization(UObject* WorldContextObject, bool bValidOwnerWorld);

virtual void InitializeComponent() override;
virtual void UninitializeComponent() override;
virtual void BeginPlay() override;

~USocketIOClientComponent();

protected:
void SetupCallbacks();
void ClearCallbacks();
void InitializeNative();

bool CallBPFunctionWithResponse(UObject* Target, const FString& FunctionName, TArray<TSharedPtr<FJsonValue>> Response);
bool CallBPFunctionWithMessage(UObject* Target, const FString& FunctionName, TSharedPtr<FJsonValue> Message);
Expand Down
21 changes: 21 additions & 0 deletions Source/SocketIOClient/Public/SocketIOFunctionLibrary.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2019-current Getnamo. All Rights Reserved

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "SocketIOClientComponent.h"
#include "SocketIOFunctionLibrary.generated.h"

/**
* Static spawning support library
*/
UCLASS()
class SOCKETIOCLIENT_API USocketIOFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

/** Static function to spawn a component that doesn't attach */
UFUNCTION(BlueprintCallable, Category = "SocketIO Client Static", meta = (WorldContext = "WorldContextObject"))
static USocketIOClientComponent* ConstructSocketIOComponent(UObject* WorldContextObject);
};
9 changes: 8 additions & 1 deletion Source/SocketIOClient/Public/SocketIONative.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ struct TSetFunctionWrapper
}
};

//used for early binds
struct FSIOBoundEvent
{
TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)> Function;
FString Namespace;
};


class SOCKETIOCLIENT_API FSocketIONative
{
Expand All @@ -54,7 +61,7 @@ class SOCKETIOCLIENT_API FSocketIONative
TFunction<void()> OnFailCallback;

//Map for all native functions bound to this socket
TMap<FString, TFunction< void(const FString&, const TSharedPtr<FJsonValue>&)>> EventFunctionMap;
TMap<FString, FSIOBoundEvent> EventFunctionMap;

/** Default connection address string in form e.g. http://localhost:80. */
FString AddressAndPort;
Expand Down

0 comments on commit 2f5ed34

Please sign in to comment.