// Fill out your copyright notice in the Description page of Project Settings.
#include "MyMessageLogRuntime/Public/MyMessageLogFunctionLibrary.h"
#if WITH_EDITOR
#include "Engine/Engine.h"
#include "Logging/MessageLog.h"
#include "Templates/SharedPointer.h"
#include "SourceCodeNavigation.h"
#include "Misc/UObjectToken.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/KismetDebugUtilities.h"
#endif
class MyMessageLogHelper
{
public:
static void VisitCode(const TSharedRef<IMessageToken>& Token, FString AbsolutePath, int32 Line);
static void VisitObject(const TSharedRef<IMessageToken>& Token);
static bool GetLogSeverity(ELogVerbosity::Type Verbosity, EMessageSeverity::Type& OutSeverity);
static void OutLog(EMessageSeverity::Type& Severity, const FString& Message);
static void OutScreenLog(EMessageSeverity::Type& Severity, const FString& Message);
};
void MyMessageLogHelper::VisitCode(const TSharedRef<IMessageToken>& Token, FString AbsolutePath, int32 Line)
{
#if WITH_EDITOR
FSourceCodeNavigation::OpenSourceFile(AbsolutePath, Line);
#endif
}
void MyMessageLogHelper::VisitObject(const TSharedRef<IMessageToken>& Token)
{
#if WITH_EDITOR
if (Token->GetType() == EMessageToken::Object)
{
const TSharedRef<FUObjectToken> UObjectToken = StaticCastSharedRef<FUObjectToken>(Token);
if (UObjectToken->GetObject().IsValid())
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(UObjectToken->GetObject().Get());
}
}
#endif
}
bool MyMessageLogHelper::GetLogSeverity(ELogVerbosity::Type Verbosity, EMessageSeverity::Type& OutSeverity)
{
const TMap<ELogVerbosity::Type, EMessageSeverity::Type> VerbosityToSeverity =
{
{ELogVerbosity::Warning, EMessageSeverity::Warning},
{ELogVerbosity::Error, EMessageSeverity::Error},
{ELogVerbosity::Fatal, EMessageSeverity::CriticalError},
};
const EMessageSeverity::Type* Found = VerbosityToSeverity.Find(Verbosity);
if (Found)
{
OutSeverity = *Found;
return true;
}
return false;
}
void MyMessageLogHelper::OutLog(EMessageSeverity::Type& Severity, const FString& Message)
{
switch (Severity)
{
case EMessageSeverity::CriticalError:
UE_LOG(LogTemp, Fatal, TEXT("%s"), *Message);
break;
case EMessageSeverity::Error:
UE_LOG(LogTemp, Error, TEXT("%s"), *Message);
break;
case EMessageSeverity::PerformanceWarning:
case EMessageSeverity::Warning:
UE_LOG(LogTemp, Warning, TEXT("%s"), *Message);
break;
case EMessageSeverity::Info:
UE_LOG(LogTemp, Log, TEXT("%s"), *Message);
break;
default:
break;
}
}
void MyMessageLogHelper::OutScreenLog(EMessageSeverity::Type& Severity, const FString& Message)
{
if (GEngine)
{
if (Severity == ELogVerbosity::Warning)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, Message);
}
else if (Severity == ELogVerbosity::Error)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, Message);
}
}
}
//--------------------------------------------------------------------------------------------------------------------------------
void UMyMessageLogFunctionLibrary::OutMessageLogInternal(ELogVerbosity::Type Verbosity, const FName& LogName, const FString& File, int32 Line, const FString& Message)
{
EMessageSeverity::Type Severity = EMessageSeverity::Info;
MyMessageLogHelper::GetLogSeverity(Verbosity, Severity);
MyMessageLogHelper::OutScreenLog(Severity, Message);
#if WITH_EDITOR
TSharedRef<FTokenizedMessage> TokenizedMessage = FMessageLog(LogName).Message(Severity);
TokenizedMessage->AddToken(FTextToken::Create(FText::FromString(Message))
->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&MyMessageLogHelper::VisitCode, File, Line))
);
#endif
}
TSharedPtr<FTokenizedMessage> UMyMessageLogFunctionLibrary::OutMessageLogBP(EMessageSeverity::Type Severity, const FName& LogName, const UObject* ActiveObject, const FFrame& StackFrame, const FString& Message)
{
MyMessageLogHelper::OutLog(Severity, Message);
MyMessageLogHelper::OutScreenLog(Severity, Message);
#if WITH_EDITOR && WITH_EDITORONLY_DATA // to protect access to GeneratedClass->DebugData
if (LogName != NAME_None)
{
UMyMessageLogFunctionLibrary::RegisterLogListing(LogName, FText::FromName(LogName));
UClass* ClassContainingCode = FKismetDebugUtilities::FindClassForNode(ActiveObject, StackFrame.Node);
UBlueprint* BlueprintObj = (ClassContainingCode ? Cast<UBlueprint>(ClassContainingCode->ClassGeneratedBy) : nullptr);
if (BlueprintObj)
{
UObject* ObjectBeingDebugged = BlueprintObj->GetObjectBeingDebugged();
const FString& PathToDebug = BlueprintObj->GetObjectPathToDebug();
if (ObjectBeingDebugged == nullptr)
{
// Check if we need to update the object being debugged
UObject* ObjectToDebug = FindObjectSafe<UObject>(nullptr, *PathToDebug);
if (ObjectToDebug)
{
// If the path to debug matches a newly-spawned object, set the hard reference now
ObjectBeingDebugged = ObjectToDebug;
BlueprintObj->SetObjectBeingDebugged(ObjectBeingDebugged);
}
}
const int32 BreakpointOffset = StackFrame.Code - StackFrame.Node->Script.GetData() - 1;
if (GIsEditor && GIsPlayInEditorWorld)
{
TSharedRef<FTokenizedMessage> TokenizedMessage = FMessageLog(LogName).Message(Severity);
UBlueprintGeneratedClass* GeneratedClass = Cast<UBlueprintGeneratedClass>(ClassContainingCode);
if ((GeneratedClass != nullptr) && GeneratedClass->DebugData.IsValid())
{
UEdGraphNode* BlueprintNode = GeneratedClass->DebugData.FindSourceNodeFromCodeLocation(StackFrame.Node, BreakpointOffset, true);
if (BlueprintNode != nullptr)
{
TokenizedMessage
->AddToken(FTextToken::Create(BlueprintNode->GetNodeTitle(ENodeTitleType::ListView)))
->AddToken(FTextToken::Create(FText::FromString(TEXT(":"))))
->AddToken(FUObjectToken::Create(BlueprintNode, FText::FromString(Message))
->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&MyMessageLogHelper::VisitObject)));
}
}
return TokenizedMessage;
}
}
}
#endif
return nullptr;
}
DEFINE_FUNCTION(UMyMessageLogFunctionLibrary::execOutPIELogBP)
{
P_GET_PROPERTY(FByteProperty, Severity);
P_GET_PROPERTY(FStrProperty, Message);
P_FINISH;
P_NATIVE_BEGIN;
UMyMessageLogFunctionLibrary::OutMessageLogBP(EMessageSeverity::Type(Severity), FName(TEXT("PIE")), P_THIS, Stack, Message);
P_NATIVE_END;
}