mirror of
https://github.com/storytold/EventCenterPlugin.git
synced 2026-10-09 00:09:42 +00:00
This was quite a fight; at least it compiles now... (no idea if works)
Ugh, the type system in C++ is bad. It's hard to fit these things together. I'll have a ton of cleanup to do when done.
This commit is contained in:
@@ -6,10 +6,12 @@ I tried Redis PubSub first, but the plugin I found was buggy. I'm trying WebSock
|
||||
|
||||
## Installation
|
||||
|
||||
Here are the plugins I use:
|
||||
Install the [BlueprintWebSocket](https://www.unrealengine.com/marketplace/en-US/product/blueprintwebsocket)
|
||||
plugin (paid/commercial) from the Epic/Unreal Marketplace. It's not expensive and has great documentation.
|
||||
|
||||
* [SDRedis](https://www.unrealengine.com/marketplace/en-US/product/sdredis) (paid/commercial) - this is flaky and disconnects. Attempts to reconnect segfault.
|
||||
* [BlueprintWebSocket](https://www.unrealengine.com/marketplace/en-US/product/blueprintwebsocket) (paid/commercial) - much better docs. Untested.
|
||||
## Usage
|
||||
|
||||
It's expected to wire events up in the `Level Blueprint`.
|
||||
|
||||
## Python testing
|
||||
|
||||
|
||||
@@ -183,8 +183,22 @@ void AEventSourceActor::OnMessageSent(const FString& Message)
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "OnMessageSent");
|
||||
UE_LOG(EventSourceLog, Log, TEXT("OnMessageSent"));
|
||||
|
||||
UE_LOG(EventSourceLog, Log, TEXT("We just sent %s to the server."), *Message);
|
||||
|
||||
|
||||
FString channel, payload;
|
||||
bool result = Message.Split(TEXT("|"), &channel, &payload);
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!delegateSubscriptions.Contains(channel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delegateSubscriptions[channel].Execute(channel, payload);
|
||||
|
||||
}
|
||||
|
||||
void AEventSourceActor::SetupWebSockets()
|
||||
@@ -206,3 +220,35 @@ void AEventSourceActor::SetupWebSockets()
|
||||
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)
|
||||
{
|
||||
delegateSubscriptions.Add(channel, delegateDef);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "UObject/Interface.h"
|
||||
|
||||
|
||||
|
||||
#include "EventHandlerInterface.generated.h"
|
||||
|
||||
/**
|
||||
* Interface for all objects that can handle events.
|
||||
*
|
||||
* My C++ foo isn't strong enough to accomplish this with templates, lambdas,
|
||||
* or reuse of unreal's existing delegation system.
|
||||
*
|
||||
* Assuming this works, see:
|
||||
* https://docs.unrealengine.com/en-US/ProgrammingAndScripting/GameplayArchitecture/Interfaces/index.html
|
||||
*/
|
||||
UINTERFACE(BlueprintType)
|
||||
class EVENTCENTERPLUGIN_API UEventHandlerInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/*public:
|
||||
UEventHandlerInterface() {}
|
||||
virtual ~UEventHandlerInterface() {}*/
|
||||
|
||||
};
|
||||
|
||||
|
||||
class EVENTCENTERPLUGIN_API IEventHandlerInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Virtual functions cannot have UFUNCTION specifiers.
|
||||
//virtual void HandleEvent(FString channel, FString message) = 0;
|
||||
|
||||
// ^ NB(bt): Designation of "= 0" makes this a pure virtual method,
|
||||
// and it also makes the class abstract.
|
||||
|
||||
// Event callback for events dispatched with EventCenterPlugin.
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Event Callback")
|
||||
void HandleEvent2(const FString& channel, const FString& message);
|
||||
};
|
||||
@@ -7,6 +7,9 @@
|
||||
// RedisPlugin
|
||||
#include "RedisObject.h"
|
||||
|
||||
// Event system
|
||||
#include "EventHandlerInterface.h"
|
||||
|
||||
// BlueprintWebSocketPlugin
|
||||
// Docs: https://github.com/Pandoa/BlueprintWebSocket
|
||||
#include "BlueprintWebSocketWrapper.h"
|
||||
@@ -16,6 +19,8 @@
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(EventSourceLog, Log, All);
|
||||
|
||||
// TODO(bt): Make this not an actor. Consider UObject. Also consider a singleton.
|
||||
|
||||
/**
|
||||
* AEventSourceActor
|
||||
*
|
||||
@@ -24,6 +29,8 @@ DECLARE_LOG_CATEGORY_EXTERN(EventSourceLog, Log, All);
|
||||
|
||||
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.
|
||||
UCLASS()
|
||||
class EVENTCENTERPLUGIN_API AEventSourceActor : public AActor
|
||||
@@ -63,14 +70,31 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "EventCenter")
|
||||
void Unsubscribe(const FString& Channel);
|
||||
|
||||
/** Again, what do. */
|
||||
/** 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.*/
|
||||
UFUNCTION(BlueprintCallable, Category = "EventCenter")
|
||||
void SetupWebSockets();
|
||||
|
||||
/** This is an experiment */
|
||||
//UFUNCTION(BlueprintCallable, Category = "EventCenter")
|
||||
//void AssignEvent(FString& eventName, TScriptInterface<IEventHandlerInterface> handler);
|
||||
|
||||
|
||||
// This won't work because we don't know what the function is
|
||||
//void AssignEvent2(FString& eventName, UObject& object, FMySubscriberRefDelegate& theDelegate);
|
||||
|
||||
//FMySubscriberRefDelegate MyDelegate2;
|
||||
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
@@ -91,6 +115,11 @@ private:
|
||||
UPROPERTY()
|
||||
UBlueprintWebSocket* WebSocket;
|
||||
|
||||
|
||||
// TODO(bt): Value type can be wrapped in a class to add additional routing.
|
||||
// Multiple delegation signatures, "pause/muting", etc.
|
||||
TMap<FString, FMySubscriberRefDelegate> delegateSubscriptions;
|
||||
|
||||
// Handle callbacks.
|
||||
// NB(bt): Declared 'UFUNCTION()' so reflection enables as a delegate callback.
|
||||
UFUNCTION()
|
||||
|
||||
@@ -76,7 +76,7 @@ async def server(websocket, path):
|
||||
#await websocket.send("Got message: {}".format(msg))
|
||||
|
||||
print('enqueuing websocket message', flush=True)
|
||||
queue.put_nowait(msg)
|
||||
queue.put_nowait("{}|{}".format(channel, msg))
|
||||
|
||||
asyncio.ensure_future(reader(mpsc))
|
||||
|
||||
@@ -98,7 +98,7 @@ async def server(websocket, path):
|
||||
await websocket.send("Websocket ping")
|
||||
|
||||
if not queue.empty():
|
||||
msg = queue.get_nowait() # TODO THROWS EXCEPTION
|
||||
msg = queue.get_nowait() # TODO THROWS EXCEPTION IF EMPTY (data race?)
|
||||
|
||||
print('sending pubsub msg over websocket: {}'.format(msg), flush=True)
|
||||
await websocket.send(msg)
|
||||
|
||||
Reference in New Issue
Block a user