RealtimeStyleTransferRuntime/Plugins/GameplayMessageRouter/Source/GameplayMessageRuntime/Private/GameFramework/GameplayMessageSubsystem.cpp

185 lines
6.3 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GameFramework/GameplayMessageSubsystem.h"
#include "Engine/Engine.h"
DEFINE_LOG_CATEGORY(LogGameplayMessageSubsystem);
namespace UE
{
namespace GameplayMessageSubsystem
{
static int32 ShouldLogMessages = 0;
static FAutoConsoleVariableRef CVarShouldLogMessages(TEXT("GameplayMessageSubsystem.LogMessages"),
ShouldLogMessages,
TEXT("Should messages broadcast through the gameplay message subsystem be logged?"));
}
}
//////////////////////////////////////////////////////////////////////
// FGameplayMessageListenerHandle
void FGameplayMessageListenerHandle::Unregister()
{
if (UGameplayMessageSubsystem* StrongSubsystem = Subsystem.Get())
{
StrongSubsystem->UnregisterListener(*this);
Subsystem.Reset();
Channel = FGameplayTag();
ID = 0;
}
}
//////////////////////////////////////////////////////////////////////
// UGameplayMessageSubsystem
UGameplayMessageSubsystem& UGameplayMessageSubsystem::Get(const UObject* WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::Assert);
check(World);
UGameplayMessageSubsystem* Router = UGameInstance::GetSubsystem<UGameplayMessageSubsystem>(World->GetGameInstance());
check(Router);
return *Router;
}
bool UGameplayMessageSubsystem::HasInstance(const UObject* WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::Assert);
UGameplayMessageSubsystem* Router = World != nullptr ? UGameInstance::GetSubsystem<UGameplayMessageSubsystem>(World->GetGameInstance()) : nullptr;
return Router != nullptr;
}
void UGameplayMessageSubsystem::Deinitialize()
{
ListenerMap.Reset();
Super::Deinitialize();
}
void UGameplayMessageSubsystem::BroadcastMessageInternal(FGameplayTag Channel, const UScriptStruct* StructType, const void* MessageBytes)
{
// Log the message if enabled
if (UE::GameplayMessageSubsystem::ShouldLogMessages != 0)
{
FString* pContextString = nullptr;
#if WITH_EDITOR
if (GIsEditor)
{
extern ENGINE_API FString GPlayInEditorContextString;
pContextString = &GPlayInEditorContextString;
}
#endif
FString HumanReadableMessage;
StructType->ExportText(/*out*/ HumanReadableMessage, MessageBytes, /*Defaults=*/ nullptr, /*OwnerObject=*/ nullptr, PPF_None, /*ExportRootScope=*/ nullptr);
UE_LOG(LogGameplayMessageSubsystem, Log, TEXT("BroadcastMessage(%s, %s, %s)"), pContextString ? **pContextString : *GetPathNameSafe(this), *Channel.ToString(), *HumanReadableMessage);
}
// Broadcast the message
bool bOnInitialTag = true;
for (FGameplayTag Tag = Channel; Tag.IsValid(); Tag = Tag.RequestDirectParent())
{
if (const FChannelListenerList* pList = ListenerMap.Find(Tag))
{
// Copy in case there are removals while handling callbacks
TArray<FGameplayMessageListenerData> ListenerArray(pList->Listeners);
for (const FGameplayMessageListenerData& Listener : ListenerArray)
{
if (bOnInitialTag || (Listener.MatchType == EGameplayMessageMatch::PartialMatch))
{
if (Listener.bHadValidType && !Listener.ListenerStructType.IsValid())
{
UE_LOG(LogGameplayMessageSubsystem, Warning, TEXT("Listener struct type has gone invalid on Channel %s. Removing listener from list"), *Channel.ToString());
UnregisterListenerInternal(Channel, Listener.HandleID);
continue;
}
// The receiving type must be either a parent of the sending type or completely ambiguous (for internal use)
if (!Listener.bHadValidType || StructType->IsChildOf(Listener.ListenerStructType.Get()))
{
Listener.ReceivedCallback(Channel, StructType, MessageBytes);
}
else
{
UE_LOG(LogGameplayMessageSubsystem, Error, TEXT("Struct type mismatch on channel %s (broadcast type %s, listener at %s was expecting type %s)"),
*Channel.ToString(),
*StructType->GetPathName(),
*Tag.ToString(),
*Listener.ListenerStructType->GetPathName());
}
}
}
}
bOnInitialTag = false;
}
}
void UGameplayMessageSubsystem::K2_BroadcastMessage(FGameplayTag Channel, const int32& Message)
{
// This will never be called, the exec version below will be hit instead
checkNoEntry();
}
DEFINE_FUNCTION(UGameplayMessageSubsystem::execK2_BroadcastMessage)
{
P_GET_STRUCT(FGameplayTag, Channel);
Stack.MostRecentPropertyAddress = nullptr;
Stack.StepCompiledIn<FStructProperty>(nullptr);
void* MessagePtr = Stack.MostRecentPropertyAddress;
FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);
P_FINISH;
if (ensure((StructProp != nullptr) && (StructProp->Struct != nullptr) && (MessagePtr != nullptr)))
{
P_THIS->BroadcastMessageInternal(Channel, StructProp->Struct, MessagePtr);
}
}
FGameplayMessageListenerHandle UGameplayMessageSubsystem::RegisterListenerInternal(FGameplayTag Channel, TFunction<void(FGameplayTag, const UScriptStruct*, const void*)>&& Callback, const UScriptStruct* StructType, EGameplayMessageMatch MatchType)
{
FChannelListenerList& List = ListenerMap.FindOrAdd(Channel);
FGameplayMessageListenerData& Entry = List.Listeners.AddDefaulted_GetRef();
Entry.ReceivedCallback = MoveTemp(Callback);
Entry.ListenerStructType = StructType;
Entry.bHadValidType = StructType != nullptr;
Entry.HandleID = ++List.HandleID;
Entry.MatchType = MatchType;
return FGameplayMessageListenerHandle(this, Channel, Entry.HandleID);
}
void UGameplayMessageSubsystem::UnregisterListener(FGameplayMessageListenerHandle Handle)
{
if (Handle.IsValid())
{
check(Handle.Subsystem == this);
UnregisterListenerInternal(Handle.Channel, Handle.ID);
}
else
{
UE_LOG(LogGameplayMessageSubsystem, Warning, TEXT("Trying to unregister an invalid Handle."));
}
}
void UGameplayMessageSubsystem::UnregisterListenerInternal(FGameplayTag Channel, int32 HandleID)
{
if (FChannelListenerList* pList = ListenerMap.Find(Channel))
{
int32 MatchIndex = pList->Listeners.IndexOfByPredicate([ID = HandleID](const FGameplayMessageListenerData& Other) { return Other.HandleID == ID; });
if (MatchIndex != INDEX_NONE)
{
pList->Listeners.RemoveAtSwap(MatchIndex);
}
if (pList->Listeners.Num() == 0)
{
ListenerMap.Remove(Channel);
}
}
}