#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); } } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Float; Result.ArgumentName = Name; Result.ArgumentValueFloat = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Double; Result.ArgumentName = Name; Result.ArgumentValueDouble = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Gender; Result.ArgumentName = Name; Result.ArgumentValueGender = Value; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(Value); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &Value, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(Value); return Result; } FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText(); return Result; } FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject) { const UEnum *Enum = Cast(PinSubCategoryObject); FFormatArgumentData Result; if (Enum == nullptr) { Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = Value; } else { Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value)); } return Result; } 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); }