297 lines
9.7 KiB
C++
297 lines
9.7 KiB
C++
|
|
#include "BlueprintErrors.h"
|
|
#include "Blueprint/BlueprintExceptionInfo.h"
|
|
#include "LuaCall.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, 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();
|
|
|
|
// 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 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::FormatArgumentDataKey(FKey Value, const FString &Name)
|
|
{
|
|
FFormatArgumentData Result;
|
|
Result.ArgumentValueType = EFormatArgumentType::Text;
|
|
Result.ArgumentName = Name;
|
|
Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(Value.GetFName());
|
|
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 UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name)
|
|
{
|
|
FFormatArgumentData Result;
|
|
Result.ArgumentValueType = EFormatArgumentType::Text;
|
|
Result.ArgumentName = Name;
|
|
Result.ArgumentValue = FText::FromString(Value->DebugString());
|
|
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<const UEnum>(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);
|
|
}
|
|
|
|
|