#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 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 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(*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); }