cleanup and remove RedisPlugin

This commit is contained in:
Brandon Thomas
2021-02-19 06:23:25 -05:00
parent 602308a861
commit 1a62295187
4 changed files with 47 additions and 227 deletions
+27
View File
@@ -20,3 +20,30 @@ I stand up a websocket+pubsub system like so:
``` ```
PS C:\Users\echel\OneDrive\Documents\Unreal Projects\RedisTestProject\Plugins\EventCenterPlugin> .\python\Scripts\Activate.ps1 PS C:\Users\echel\OneDrive\Documents\Unreal Projects\RedisTestProject\Plugins\EventCenterPlugin> .\python\Scripts\Activate.ps1
``` ```
## Notes
A few collected notes, since I'm still new to Unreal engine and I'm cleaning up my debug code.
To log to the game screen,
```
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Message goes here");
```
Time based handling in Tick(),
```
void AEventSourceActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float time = GetGameTimeSinceCreation();
if (time - timerLastTime > 15.0) {
timerLastTime = time;
// Do the thing
}
}
```
@@ -14,12 +14,6 @@ public class EventCenterPlugin : ModuleRules
new string[] { new string[] {
// ... add public include paths required here ... // ... add public include paths required here ...
// NB(bt): Using the commercial Redis Plugin by GameSeed (AKA "SDRedis")
// Add this in the Unreal Engine Plugins menu.
// If not present, use the Epic Games Launcher's Unreal Library Marketplace.
"RedisPlugin/Public",
"RedisPlugin/Classes",
// NB(bt): Using the commercial BlueprintWebSocket plugin described in README.md // NB(bt): Using the commercial BlueprintWebSocket plugin described in README.md
// Add this in the Unreal Engine Plugins menu. // Add this in the Unreal Engine Plugins menu.
// If not present, use the Epic Games Launcher's Unreal Library Marketplace. // If not present, use the Epic Games Launcher's Unreal Library Marketplace.
@@ -42,11 +36,6 @@ public class EventCenterPlugin : ModuleRules
"Core", "Core",
// ... add other public dependencies that you statically link with here ... // ... add other public dependencies that you statically link with here ...
// NB(bt): Using the commercial Redis Plugin by GameSeed (AKA "SDRedis")
// Add this in the Unreal Engine Plugins menu.
// If not present, use the Epic Games Launcher's Unreal Library Marketplace.
"RedisPlugin",
// NB(bt): Using the commercial BlueprintWebSocket plugin described in README.md // NB(bt): Using the commercial BlueprintWebSocket plugin described in README.md
// Add this in the Unreal Engine Plugins menu. // Add this in the Unreal Engine Plugins menu.
// If not present, use the Epic Games Launcher's Unreal Library Marketplace. // If not present, use the Epic Games Launcher's Unreal Library Marketplace.
@@ -5,49 +5,20 @@
DEFINE_LOG_CATEGORY(EventSourceLog); DEFINE_LOG_CATEGORY(EventSourceLog);
AEventSourceActor::AEventSourceActor() : AEventSourceActor::AEventSourceActor() :
redis(nullptr),
WebSocket(nullptr) WebSocket(nullptr)
{ {
UE_LOG(EventSourceLog, Log, TEXT("CTOR()"));
// Set this actor to call Tick() every frame. // Set this actor to call Tick() every frame.
// You can turn this off to improve performance if you don't need it. // You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bCanEverTick = true;
//redis = NewObject<URedisObject>(this, TEXT("RedisObject"));
//redis = CreateDefaultSubobject<URedisObject>(TEXT("RedisObject"));
//RootComponent = redis;
} }
AEventSourceActor::~AEventSourceActor() AEventSourceActor::~AEventSourceActor()
{ {
// TODO: Memory leak (somehow placing a BP derivation into the editor causes the below lines to segfault)
//delete redis;
//redis = nullptr;
/*if (redis != nullptr) {
delete redis;
redis = nullptr;
}*/
} }
void AEventSourceActor::BeginPlay() void AEventSourceActor::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "BeginPlay() - 2");
UE_LOG(EventSourceLog, Log, TEXT("BeginPlay()"));
if (redis == nullptr) {
UE_LOG(EventSourceLog, Log, TEXT("BeginPlay(): Creating redis instance"));
redis = NewObject<URedisObject>(this, TEXT("RedisObject"));
}
UE_LOG(EventSourceLog, Log, TEXT("BeginPlay(): Redis is null? ? %d"), redis == nullptr);
//UE_LOG(EventSourceLog, Log, TEXT("BeginPlay(): Redis host: %s"), redisHost);
UE_LOG(EventSourceLog, Log, TEXT("BeginPlay(): Redis port: %d"), redisPort);
//UE_LOG(EventSourceLog, Log, TEXT("BeginPlay(): Redis password (no secrets): %s"), redisPassword);
} }
void AEventSourceActor::BeginDestroy() void AEventSourceActor::BeginDestroy()
@@ -59,81 +30,7 @@ void AEventSourceActor::Tick(float DeltaTime)
{ {
Super::Tick(DeltaTime); Super::Tick(DeltaTime);
float time = GetGameTimeSinceCreation(); // TODO(bt): If our callbacks aren't enough to restore broken connections, add some logic here.
if (time - timerLastTime > 15.0) {
timerLastTime = time;
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "seconds elapsed");
//redis->Unsubscribe(TEXT("second"));
//redis->Subscribe(TEXT("second"));
}
// NB: Handled by RedisObject internal delegate
//redis->Tick(DeltaTime);
}
void AEventSourceActor::ConstructClient()
{
if (redis != nullptr) {
UE_LOG(EventSourceLog, Log, TEXT("Construct(): Redis is NOT null"));
return;
}
UE_LOG(EventSourceLog, Log, TEXT("construct(): redis = new Redis()"));
redis = NewObject<URedisObject>(this, TEXT("RedisObject"));
}
void AEventSourceActor::Init()
{
if (redis == nullptr) {
UE_LOG(EventSourceLog, Log, TEXT("Init(): Redis is null"));
return;
}
//UE_LOG(EventSourceLog, Log, TEXT("construct(): redis = new Redis()"));
//redis = NewObject<URedisObject>(this, TEXT("RedisObject"));
UE_LOG(EventSourceLog, Log, TEXT("Init(): redis->Init()"));
redis->Init(redisHost, redisPort, redisPassword);
}
void AEventSourceActor::Subscribe(const FString& Channel)
{
if (redis == nullptr) {
UE_LOG(EventSourceLog, Log, TEXT("Subscribe(): Redis is null"));
return;
}
UE_LOG(EventSourceLog, Log, TEXT("Subscribe(): redis->Subscribe()"));
redis->Subscribe(Channel);
if (!redis->SubscribeReply.IsBound()) {
UE_LOG(EventSourceLog, Log, TEXT("Subscribe(): binding event"));
redis->SubscribeReply.AddDynamic(this, &AEventSourceActor::InternalSubscribeCallback);
}
}
void AEventSourceActor::Unsubscribe(const FString& Channel)
{
if (redis == nullptr) {
UE_LOG(EventSourceLog, Log, TEXT("Unsubscribe(): Redis is null"));
return;
}
UE_LOG(EventSourceLog, Log, TEXT("Unsubscribe(): redis->Unsubscribe()"));
redis->Unsubscribe(Channel);
}
void AEventSourceActor::InternalSubscribeCallback(FString Channel, FString Message)
{
//UE_LOG(EventSourceLog, Log, TEXT("SubscribeCallback(): %s %s"), Channel, Message);
UE_LOG(EventSourceLog, Log, TEXT("SubscribeCallback(): called"));
if (MyDelegate.IsBound()) {
MyDelegate.Broadcast(Channel, Message);
}
} }
// ======================================= web sockets ============================================= // ======================================= web sockets =============================================
@@ -142,33 +39,24 @@ void AEventSourceActor::OnConnected()
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnConnected"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnConnected");
UE_LOG(EventSourceLog, Log, TEXT("OnConnected")); UE_LOG(EventSourceLog, Log, TEXT("OnConnected"));
//WebSocket->SendMessage(TEXT("Hello Server!"));
} }
void AEventSourceActor::OnConnectionError(const FString& Error) void AEventSourceActor::OnConnectionError(const FString& Error)
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnConnectionError"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnConnectionError");
UE_LOG(EventSourceLog, Log, TEXT("OnConnectionError")); UE_LOG(EventSourceLog, Error, TEXT("OnConnectionError. Failed to connect: %s."), *Error);
// Connection failed.
UE_LOG(EventSourceLog, Error, TEXT("Failed to connect: %s."), *Error);
} }
void AEventSourceActor::OnClosed(int64 StatusCode, const FString& Reason, bool bWasClean) void AEventSourceActor::OnClosed(int64 StatusCode, const FString& Reason, bool bWasClean)
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnClosed"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnClosed");
UE_LOG(EventSourceLog, Log, TEXT("OnClosed")); UE_LOG(EventSourceLog, Warning, TEXT("OnClosed. Connection closed: %d:%s. Clean: %d"), StatusCode, *Reason, bWasClean);
UE_LOG(EventSourceLog, Warning, TEXT("Connection closed: %d:%s. Clean: %d"), StatusCode, *Reason, bWasClean);
} }
void AEventSourceActor::OnMessage(const FString& Message) void AEventSourceActor::OnMessage(const FString& Message)
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnMessage"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnMessage");
UE_LOG(EventSourceLog, Log, TEXT("OnMessage")); UE_LOG(EventSourceLog, Log, TEXT("OnMessage. New message: %s"), *Message);
UE_LOG(EventSourceLog, Log, TEXT("New message: %s"), *Message);
FString channel, payload; FString channel, payload;
bool result = Message.Split(TEXT("|"), &channel, &payload); bool result = Message.Split(TEXT("|"), &channel, &payload);
@@ -194,16 +82,13 @@ void AEventSourceActor::OnMessage(const FString& Message)
void AEventSourceActor::OnRawMessage(const TArray<uint8>& Data, int32 BytesRemaining) void AEventSourceActor::OnRawMessage(const TArray<uint8>& Data, int32 BytesRemaining)
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnRawMessage"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnRawMessage");
UE_LOG(EventSourceLog, Log, TEXT("OnRawMessage")); UE_LOG(EventSourceLog, Log, TEXT("OnRawMessage. New binary message: %d bytes and %d bytes remaining."), Data.Num(), BytesRemaining);
UE_LOG(EventSourceLog, Log, TEXT("New binary message: %d bytes and %d bytes remaining."), Data.Num(), BytesRemaining);
} }
void AEventSourceActor::OnMessageSent(const FString& Message) void AEventSourceActor::OnMessageSent(const FString& Message)
{ {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnMessageSent"); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnMessageSent");
UE_LOG(EventSourceLog, Log, TEXT("OnMessageSent")); UE_LOG(EventSourceLog, Log, TEXT("OnMessageSent. We just sent %s to the server."), *Message);
UE_LOG(EventSourceLog, Log, TEXT("We just sent %s to the server."), *Message);
} }
void AEventSourceActor::SetupWebSockets() void AEventSourceActor::SetupWebSockets()
@@ -225,34 +110,6 @@ void AEventSourceActor::SetupWebSockets()
WebSocket->Connect(TEXT("ws://localhost:8765/"), TEXT("ws")); WebSocket->Connect(TEXT("ws://localhost:8765/"), TEXT("ws"));
} }
/*void AEventSourceActor::AssignEvent(FString& eventName, TScriptInterface<IEventHandlerInterface> handler)
{
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "AssignEvent");
UE_LOG(EventSourceLog, Log, TEXT("AssignEvent"));
//TScriptDelegate<TWeakPtr> ourDelegate = TScriptDelegate<TWeakPtr>();
//ourDelegate.BindUFunction(handler.GetObject(), &IEventHandlerInterface::HandleEvent);
IEventHandlerInterface* iface = Cast<IEventHandlerInterface>(handler.GetObject());
//MyDelegate2.AddDynamic(iface, &IEventHandlerInterface::HandleEvent2);
//MyDelegate2.AddDynamic((UObject*)iface, &IEventHandlerInterface::HandleEvent2);
//MyDelegate2.AddDynamic(handler.GetObject(), &IEventHandlerInterface::HandleEvent2);
//MyDelegate2.AddDynamic(handler.GetObject(), "HandleEvent2");
UObject* uobject = Cast<UObject>(handler.GetObject());
//MyDelegate2.AddDynamic(handler->_getUObject(), &IEventHandlerInterface::HandleEvent2);
}*/
/*void AEventSourceActor::AssignEvent2(FString& eventName, UObject& object, FMySubscriberRefDelegate& theDelegate)
{
}*/
void AEventSourceActor::BindSingleDelegateForChannel(const FString& channel, const FMySubscriberRefDelegate& delegateDef) void AEventSourceActor::BindSingleDelegateForChannel(const FString& channel, const FMySubscriberRefDelegate& delegateDef)
{ {
delegateSubscriptions.Add(channel, delegateDef); delegateSubscriptions.Add(channel, delegateDef);
@@ -4,9 +4,6 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "UObject/NoExportTypes.h" #include "UObject/NoExportTypes.h"
// RedisPlugin
#include "RedisObject.h"
// Event system // Event system
#include "EventHandlerInterface.h" #include "EventHandlerInterface.h"
@@ -21,16 +18,16 @@ DECLARE_LOG_CATEGORY_EXTERN(EventSourceLog, Log, All);
// TODO(bt): Make this not an actor. Consider UObject. Also consider a singleton. // TODO(bt): Make this not an actor. Consider UObject. Also consider a singleton.
// NB(bt): We can't use `DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams` multicast
// due to an Unreal Engine limitation
DECLARE_DYNAMIC_DELEGATE_TwoParams(FMySubscriberRefDelegate, const FString&, Channel, const FString&, Message);
/** /**
* AEventSourceActor * AEventSourceActor
* *
* Manages an underlying Redis pubsub subscription and coordinates dispatch to other actors. * Manages an underlying Redis pubsub subscription and coordinates dispatch to other actors.
*/ */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMySubscriberDelegate, FString, Channel, FString, Message);
DECLARE_DYNAMIC_DELEGATE_TwoParams(FMySubscriberRefDelegate, const FString&, Channel, const FString&, Message);
// NB(bt): "Blueprintable" seems to remove the ability to add BP event nodes. // NB(bt): "Blueprintable" seems to remove the ability to add BP event nodes.
UCLASS() UCLASS()
class EVENTCENTERPLUGIN_API AEventSourceActor : public AActor class EVENTCENTERPLUGIN_API AEventSourceActor : public AActor
@@ -42,58 +39,20 @@ public:
AEventSourceActor(); AEventSourceActor();
~AEventSourceActor(); ~AEventSourceActor();
/** Redis Hostname */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EventCenter")
FString redisHost;
/** Redis Port */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EventCenter")
int32 redisPort = 6379;
/** Redis Password */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "EventCenter")
FString redisPassword;
/** Construct Redis Object.*/
UFUNCTION(BlueprintCallable, Category = "EventCenter")
void ConstructClient();
/** Connect to Redis.*/
UFUNCTION(BlueprintCallable, Category = "EventCenter")
void Init();
/** Subscribe to a named Redis PubSub channel. */
UFUNCTION(BlueprintCallable, Category = "EventCenter")
void Subscribe(const FString& Channel);
/** Unsubscribe from a named Redis PubSub channel. */
UFUNCTION(BlueprintCallable, Category = "EventCenter")
void Unsubscribe(const FString& Channel);
/** This allows event binding from blueprint. */
UPROPERTY(BlueprintAssignable)
FMySubscriberDelegate MyDelegate;
/**
* Bind a delegate to a channel. Note that we can't multicast.
*/
UFUNCTION(BlueprintCallable, Category = "EventCenter")
void BindSingleDelegateForChannel(const FString& channel, const FMySubscriberRefDelegate& delegateDef);
/** Set up websockets.*/ /** Set up websockets.*/
UFUNCTION(BlueprintCallable, Category = "EventCenter") UFUNCTION(BlueprintCallable, Category = "EventCenter")
void SetupWebSockets(); void SetupWebSockets();
/** This is an experiment */ /**
//UFUNCTION(BlueprintCallable, Category = "EventCenter") * Bind a delegate to a channel.
//void AssignEvent(FString& eventName, TScriptInterface<IEventHandlerInterface> handler); *
* Note that we can't multicast due to Unreal limitations. As a workaround, bind
* different channels for other events,or have the events delegates themselves
// This won't work because we don't know what the function is * perform the multicasting downstream of us.
//void AssignEvent2(FString& eventName, UObject& object, FMySubscriberRefDelegate& theDelegate); */
UFUNCTION(BlueprintCallable, Category = "EventCenter")
//FMySubscriberRefDelegate MyDelegate2; void BindSingleDelegateForChannel(const FString& channel,
const FMySubscriberRefDelegate& delegateDef);
protected: protected:
// Called when the game starts or when spawned // Called when the game starts or when spawned
@@ -110,26 +69,14 @@ protected:
UFUNCTION() void OnMessageSent(const FString& Message); UFUNCTION() void OnMessageSent(const FString& Message);
private: private:
URedisObject* redis;
UPROPERTY() UPROPERTY()
UBlueprintWebSocket* WebSocket; UBlueprintWebSocket* WebSocket;
// TODO(bt): Value type can be wrapped in a class to add additional routing. // TODO(bt): Value type can be wrapped in a class to add additional routing.
// Multiple delegation signatures, "pause/muting", etc. // Multiple delegation signatures, "pause/muting", etc.
TMap<FString, FMySubscriberRefDelegate> delegateSubscriptions; TMap<FString, FMySubscriberRefDelegate> delegateSubscriptions;
// Handle callbacks.
// NB(bt): Declared 'UFUNCTION()' so reflection enables as a delegate callback.
UFUNCTION()
void InternalSubscribeCallback(FString Channel, FString Message);
// For a timer calculation.
float timerLastTime = 0.0f;
public: public:
// Called every frame // Called every frame
virtual void Tick(float DeltaTime) override; virtual void Tick(float DeltaTime) override;
}; };