lxGameMode can now trap UE_LOG Error into the debugger

This commit is contained in:
2024-11-22 23:01:05 -05:00
parent c1f2d74351
commit 75dcdf54cc
11 changed files with 348 additions and 223 deletions

View File

@@ -0,0 +1,137 @@
#include "BlueprintErrors.h"
#include "Internationalization/TextFormatter.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet2/KismetDebugUtilities.h"
#include "Kismet/KismetTextLibrary.h"
ELogVerbosity::Type UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(ElxLogVerbosity Verbosity) {
switch (Verbosity) {
case ElxLogVerbosity::Error: return ELogVerbosity::Error;
case ElxLogVerbosity::Warning: return ELogVerbosity::Warning;
case ElxLogVerbosity::Display: return ELogVerbosity::Display;
case ElxLogVerbosity::Log: return ELogVerbosity::Log;
case ElxLogVerbosity::Verbose: return ELogVerbosity::Verbose;
case ElxLogVerbosity::VeryVerbose: return ELogVerbosity::VeryVerbose;
case ElxLogVerbosity::Fatal: return ELogVerbosity::Fatal;
}
}
void UlxBlueprintErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxErrorDisplayDuration DisplayDuration, const FString &InPattern, TArray<FFormatArgumentData> InArgs)
{
// Generate the formatted string.
//
FText InPatternText(FText::FromString(InPattern));
FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false);
FString MessageString = Message.ToString();
// Convert the DisplayDuration enum into a number of seconds.
//
int Seconds = int(DisplayDuration);
if (Seconds > 100) Seconds = (Seconds - 100) * 60;
// Choose a color appropriate to the verbosity level.
//
FLinearColor Color;
switch (Verbosity) {
case ElxLogVerbosity::Fatal : Color = FLinearColor(1.0, 0.6, 0.6); break;
case ElxLogVerbosity::Error : Color = FLinearColor(1.0, 0.6, 0.6); break;
case ElxLogVerbosity::Warning : Color = FLinearColor(0.9, 0.9, 0.6); break;
default: Color = FLinearColor(0.8, 0.8, 0.8); break;
}
// Get the blueprint name.
//
// Normally, the log function expects you to pass in a filename, and a log
// category name. We use the blueprint name for both.
//
// Using the blueprint name as a log category name is not technically
// correct. However, there is no correct way to create log categories
// from inside of blueprints. Doing it this way at least produces a reasonable
// message inside the log. What doesn't work correctly is the log message
// suppression system. Ie, console commands like 'log <category> verbose'
// don't have any effect here. The design of the log message suppression
// system is such that there just is no reasonable way to hook into it from
// inside of blueprints.
//
FString BlueprintNameString = Context->GetClass()->GetName();
auto BlueprintNameAnsi = StringCast<ANSICHAR>(*BlueprintNameString);
FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName());
// Output to Screen, if requested.
//
if (Seconds != 0)
{
UKismetSystemLibrary::PrintText(NULL, Message, true, false, Color, Seconds, NAME_None);
}
// Output to Log
//
ELogVerbosity::Type VerbosityValue = ConvertElxLogVerbosity(Verbosity);
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
{
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
}
}
FlxDebugBlueprintErrorsOutputDevice::FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef)
: Sensitivity(SensitivityRef)
{
GLog->AddOutputDevice(this);
}
FlxDebugBlueprintErrorsOutputDevice::~FlxDebugBlueprintErrorsOutputDevice()
{
GLog->RemoveOutputDevice(this);
}
namespace UBreakPoint {
static volatile int V;
FORCENOINLINE static void OnLogFatal() {
V = 0;
}
FORCENOINLINE static void OnLogError() {
V = 1;
OnLogFatal();
}
FORCENOINLINE static void OnLogWarning() {
V = 2;
OnLogError();
}
}
static const FName LogBlueprintDebugName(TEXT("LogBlueprintDebug"));
void FlxDebugBlueprintErrorsOutputDevice::Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category)
{
// If the error isn't serious enough, do nothing.
//
if (Verbosity > UlxBlueprintErrorLibrary::ConvertElxLogVerbosity(Sensitivity))
{
return;
}
// If the Category is LogBlueprintDebug, then we're inside the debugger already.
//
if (Category == LogBlueprintDebugName)
{
return;
}
// Find out if we're running in a blueprint thread. If not, return.
//
FFrame* Frame = FFrame::GetThreadLocalTopStackFrame();
if (Frame == nullptr) return;
UObject *TopObject = Frame->Object;
if (TopObject == nullptr) return;
// Notify the debugger that there's been an exception.
//
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::Breakpoint, FText::FromStringView(FStringView(V)));
FBlueprintCoreDelegates::ThrowScriptException(TopObject, *Frame, ExceptionInfo);
}