Files
UnrealEngineUWP/Engine/Source/Runtime/Messaging/Private/Bus/MessageRouter.h
robert manuszewski d1443992e1 Deprecating ANY_PACKAGE.
This change consists of multiple changes:

Core:
- Deprecation of ANY_PACKAGE macro. Added ANY_PACKAGE_DEPRECATED macro which can still be used for backwards compatibility purposes (only used in CoreUObject)
- Deprecation of StaticFindObjectFast* functions that take bAnyPackage parameter
- Added UStruct::GetStructPathName function that returns FTopLevelAssetPath representing the path name (package + object FName, super quick compared to UObject::GetPathName) + wrapper UClass::GetClassPathName to make it look better when used with UClasses
- Added (Static)FindFirstObject* functions that find a first object given its Name (no Outer). These functions are used in places I consider valid to do global UObject (UClass) lookups like parsing command line parameters / checking for unique object names
- Added static UClass::TryFindType function which serves a similar purpose as FindFirstObject however it's going to throw a warning (with a callstack / maybe ensure in the future?) if short class name is provided. This function is used  in places that used to use short class names but now should have been converted to use path names to catch any potential regressions and or edge cases I missed.
- Added static UClass::TryConvertShortNameToPathName utility function
- Added static UClass::TryFixShortClassNameExportPath utility function
- Object text export paths will now also include class path (Texture2D'/Game/Textures/Grass.Grass' -> /Script/Engine.Texture2D'/Game/Textures/Grass.Grass')
- All places that manually generated object export paths for objects will now use FObjectPropertyBase::GetExportPath
- Added a new startup test that checks for short type names in UClass/FProperty MetaData values

AssetRegistry:
- Deprecated any member variables (FAssetData / FARFilter) or functions that use FNames to represent class names and replaced them with FTopLevelAssetPath
- Added new member variables and new function overloads that use FTopLevelAssetPath to represent class names
- This also applies to a few other modules' APIs to match AssetRegistry changes

Everything else:
- Updated code that used ANY_PACKAGE (depending on the use case) to use FindObject(nullptr, PathToObject), UClass::TryFindType (used when path name is expected, warns if it's a short name) or FindFirstObject (usually for finding types based on user input but there's been a few legitimate use cases not related to user input)
- Updated code that used AssetRegistry API to use FTopLevelAssetPaths and USomeClass::StaticClass()->GetClassPathName() instead of GetFName()
- Updated meta data and hardcoded FindObject(ANY_PACKAGE, "EEnumNameOrClassName") calls to use path names

#jira UE-99463
#rb many.people
[FYI] Marcus.Wassmer
#preflight 629248ec2256738f75de9b32

#codereviewnumbers 20320742, 20320791, 20320799, 20320756, 20320809, 20320830, 20320840, 20320846, 20320851, 20320863, 20320780, 20320765, 20320876, 20320786

#ROBOMERGE-OWNER: robert.manuszewski
#ROBOMERGE-AUTHOR: robert.manuszewski
#ROBOMERGE-SOURCE: CL 20430220 via CL 20433854 via CL 20435474 via CL 20435484
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246)

[CL 20448496 by robert manuszewski in ue5-main branch]
2022-06-01 03:46:59 -04:00

337 lines
9.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Queue.h"
#include "HAL/Runnable.h"
#include "Misc/SingleThreadRunnable.h"
#include "Templates/Atomic.h"
#include "IMessageContext.h"
#include "IMessageTracer.h"
#include "Bus/MessageTracer.h"
class IMessageInterceptor;
class IMessageReceiver;
class IMessageSubscription;
class IBusListener;
enum class EMessageBusNotification : uint8;
/**
* Implements a topic-based message router.
*/
class FMessageRouter
: public FRunnable
, private FSingleThreadRunnable
{
DECLARE_DELEGATE(CommandDelegate)
public:
/** Default constructor. */
FMessageRouter();
/** Destructor. */
~FMessageRouter();
public:
/**
* Adds a message interceptor.
*
* @param Interceptor The interceptor to add.
* @param MessageType The type of messages to intercept.
*/
FORCEINLINE void AddInterceptor(const TSharedRef<IMessageInterceptor, ESPMode::ThreadSafe>& Interceptor, const FTopLevelAssetPath& MessageType)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleAddInterceptor, Interceptor, MessageType));
}
/**
* Adds a recipient.
*
* @param Address The address of the recipient to add.
* @param Recipient The recipient.
*/
FORCEINLINE void AddRecipient(const FMessageAddress& Address, const TSharedRef<IMessageReceiver, ESPMode::ThreadSafe>& Recipient)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleAddRecipient, Address, TWeakPtr<IMessageReceiver, ESPMode::ThreadSafe>(Recipient)));
}
/**
* Adds a subscription.
*
* @param Subscription The subscription to add.
*/
FORCEINLINE void AddSubscription(const TSharedRef<IMessageSubscription, ESPMode::ThreadSafe>& Subscription)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleAddSubscriber, Subscription));
}
/**
* Gets the message tracer.
*
* @return Weak pointer to the message tracer.
*/
FORCEINLINE TSharedRef<IMessageTracer, ESPMode::ThreadSafe> GetTracer()
{
return Tracer;
}
/**
* Removes a message interceptor.
*
* @param Interceptor The interceptor to remove.
* @param MessageType The type of messages to stop intercepting.
*/
FORCEINLINE void RemoveInterceptor(const TSharedRef<IMessageInterceptor, ESPMode::ThreadSafe>& Interceptor, const FTopLevelAssetPath& MessageType)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleRemoveInterceptor, Interceptor, MessageType));
}
/**
* Removes a recipient.
*
* @param Address The address of the recipient to remove.
*/
FORCEINLINE void RemoveRecipient(const FMessageAddress& Address)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleRemoveRecipient, Address));
}
/**
* Removes a subscription.
*
* @param Subscriber The subscriber to stop routing messages to.
* @param MessageType The type of message to unsubscribe from (NAME_None = all types).
*/
FORCEINLINE void RemoveSubscription(const TSharedRef<IMessageReceiver, ESPMode::ThreadSafe>& Subscriber, const FTopLevelAssetPath& MessageType)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleRemoveSubscriber, TWeakPtr<IMessageReceiver, ESPMode::ThreadSafe>(Subscriber), MessageType));
}
/**
* Routes a message to the specified recipients.
*
* @param Context The context of the message to route.
*/
FORCEINLINE void RouteMessage(const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Context)
{
Tracer->TraceSentMessage(Context);
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleRouteMessage, Context));
}
/**
* Add a listener to the bus registration events
*
* @param Listener The listener to as to the registration notifications
*/
FORCEINLINE void AddNotificationListener(const TSharedRef<IBusListener, ESPMode::ThreadSafe>& Listener)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleAddListener, TWeakPtr<IBusListener, ESPMode::ThreadSafe>(Listener)));
}
/**
* Remove a listener to the bus registration events
*
* @param Listener The listener to remove from the registration notifications
*/
FORCEINLINE void RemoveNotificationListener(const TSharedRef<IBusListener, ESPMode::ThreadSafe>& Listener)
{
EnqueueCommand(FSimpleDelegate::CreateRaw(this, &FMessageRouter::HandleRemoveListener, TWeakPtr<IBusListener, ESPMode::ThreadSafe>(Listener)));
}
public:
//~ FRunnable interface
virtual FSingleThreadRunnable* GetSingleThreadInterface() override;
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Stop() override;
virtual void Exit() override;
protected:
/**
* Calculates the time that the thread will wait for new work.
*
* @return Wait time.
*/
FTimespan CalculateWaitTime();
/**
* Queues up a router command.
*
* @param Command The command to queue up.
* @return true if the command was enqueued, false otherwise.
*/
FORCEINLINE bool EnqueueCommand(CommandDelegate Command)
{
if (!Commands.Enqueue(Command))
{
return false;
}
WorkEvent->Trigger();
return true;
}
/**
* Filters a collection of subscriptions using the given message context.
*
* @param Subscriptions The subscriptions to filter.
* @param Context The message context to filter by.
* @param OutRecipients Will hold the collection of recipients.
*/
void FilterSubscriptions(
TArray<TSharedPtr<IMessageSubscription, ESPMode::ThreadSafe>>& Subscriptions,
const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Context,
TArray<TSharedPtr<IMessageReceiver, ESPMode::ThreadSafe>>& OutRecipients);
/**
* Filters recipients from the given message context to gather actual recipients.
*
* @param Context The message context to filter by.
* @param OutRecipients Will hold the collection of recipients.
*/
void FilterRecipients(
const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Context,
TArray<TSharedPtr<IMessageReceiver, ESPMode::ThreadSafe>>& OutRecipients);
/**
* Dispatches a single message to its recipients.
*
* @param Message The message to dispatch.
*/
void DispatchMessage(const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& Message);
/**
* Process all queued commands.
*
* @see ProcessDelayedMessages
*/
void ProcessCommands();
/**
* Processes all delayed messages.
*
* @see ProcessCommands
*/
void ProcessDelayedMessages();
protected:
//~ FSingleThreadRunnable interface
virtual void Tick() override;
private:
/** Structure for delayed messages. */
struct FDelayedMessage
{
/** Holds the context of the delayed message. */
TSharedPtr<IMessageContext, ESPMode::ThreadSafe> Context;
/** Holds a sequence number used by the delayed message queue. */
int64 Sequence;
/** Default constructor. */
FDelayedMessage() { }
/** Creates and initializes a new instance. */
FDelayedMessage(const TSharedRef<IMessageContext, ESPMode::ThreadSafe>& InContext, int64 InSequence)
: Context(InContext)
, Sequence(InSequence)
{ }
/** Comparison operator for heap sorting. */
bool operator<(const FDelayedMessage& Other) const
{
const FTimespan Difference = Other.Context->GetTimeSent() - Context->GetTimeSent();
if (Difference.IsZero())
{
return (Sequence < Other.Sequence);
}
return (Difference > FTimespan::Zero());
}
};
private:
/** Handles adding message interceptors. */
void HandleAddInterceptor(TSharedRef<IMessageInterceptor, ESPMode::ThreadSafe> Interceptor, FTopLevelAssetPath MessageType);
/** Handles adding message recipients. */
void HandleAddRecipient(FMessageAddress Address, TWeakPtr<IMessageReceiver, ESPMode::ThreadSafe> RecipientPtr);
/** Handles adding of subscriptions. */
void HandleAddSubscriber(TSharedRef<IMessageSubscription, ESPMode::ThreadSafe> Subscription);
/** Handles the removal of message interceptors. */
void HandleRemoveInterceptor(TSharedRef<IMessageInterceptor, ESPMode::ThreadSafe> Interceptor, FTopLevelAssetPath MessageType);
/** Handles the removal of message recipients. */
void HandleRemoveRecipient(FMessageAddress Address);
/** Handles the removal of subscribers. */
void HandleRemoveSubscriber(TWeakPtr<IMessageReceiver, ESPMode::ThreadSafe> SubscriberPtr, FTopLevelAssetPath MessageType);
/** Handles the routing of messages. */
void HandleRouteMessage(TSharedRef<IMessageContext, ESPMode::ThreadSafe> Context);
/** Handles the addition of a listener. */
void HandleAddListener(TWeakPtr<IBusListener, ESPMode::ThreadSafe> ListenerPtr);
/** Handles the removal of a listener. */
void HandleRemoveListener(TWeakPtr<IBusListener, ESPMode::ThreadSafe> ListenerPtr);
/** Notify listeners about registration */
void NotifyRegistration(const FMessageAddress& Address, EMessageBusNotification Notification);
private:
/** Maps message types to interceptors. */
TMap<FTopLevelAssetPath, TArray<TSharedPtr<IMessageInterceptor, ESPMode::ThreadSafe>>> ActiveInterceptors;
/** Maps message addresses to recipients. */
TMap<FMessageAddress, TWeakPtr<IMessageReceiver, ESPMode::ThreadSafe>> ActiveRecipients;
/** Maps message types to subscriptions. */
TMap<FTopLevelAssetPath, TArray<TSharedPtr<IMessageSubscription, ESPMode::ThreadSafe>>> ActiveSubscriptions;
/** Array of active registration listeners. */
TArray<TWeakPtr<IBusListener, ESPMode::ThreadSafe>> ActiveRegistrationListeners;
/** Holds the router command queue. */
TQueue<CommandDelegate, EQueueMode::Mpsc> Commands;
/** Holds the current time. */
FDateTime CurrentTime;
/** Holds the collection of delayed messages. */
TArray<FDelayedMessage> DelayedMessages;
/** Holds a sequence number for delayed messages. */
int64 DelayedMessagesSequence;
/** Holds a flag indicating that the thread is stopping. */
TAtomic<bool> Stopping;
/** Holds the message tracer. */
TSharedRef<FMessageTracer, ESPMode::ThreadSafe> Tracer;
/** Holds an event signaling that work is available. */
FEvent* WorkEvent;
/** Whether or not to allow delayed messaging */
bool bAllowDelayedMessaging;
};