lxGameMode can now trap UE_LOG Error into the debugger
This commit is contained in:
BIN
Content/Luprex/lxGameMode.uasset
LFS
Executable file → Normal file
BIN
Content/Luprex/lxGameMode.uasset
LFS
Executable file → Normal file
Binary file not shown.
Binary file not shown.
@@ -81,8 +81,9 @@ void UlxAnimationStepLibrary::UnpackAnimationStep(bool &bChanged, FString &Actio
|
|||||||
bChanged = false;
|
bChanged = false;
|
||||||
Action = TEXT("");
|
Action = TEXT("");
|
||||||
|
|
||||||
if (prefix.IsEmpty()) {
|
if (prefix.IsEmpty())
|
||||||
UlxUtilityLibrary::Assert(false, TEXT("You may not pass an empty string for prefix"));
|
{
|
||||||
|
UE_LOG(LogBlueprint, Error, TEXT("UnpackAnimationStep: You may not pass an empty string for prefix"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,8 +93,7 @@ void UlxAnimationStepLibrary::UnpackAnimationStep(bool &bChanged, FString &Actio
|
|||||||
|
|
||||||
FStructProperty* stepproperty = FindAnimationStepProperty(uclass, prefix);
|
FStructProperty* stepproperty = FindAnimationStepProperty(uclass, prefix);
|
||||||
if (stepproperty == nullptr) {
|
if (stepproperty == nullptr) {
|
||||||
UE_LOG(LogBlueprint, Error, TEXT("Target object: %s Prefix: %s"), *(target->GetName()), *prefix);
|
UE_LOG(LogBlueprint, Error, TEXT("UnpackAnimationStep: Target object does not have a variable named: '%s Animation Step'"), *prefix);
|
||||||
UlxUtilityLibrary::Assert(false, TEXT("Target object does not have an variable named '<prefix> Animation Step'"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
137
Source/Integration/BlueprintErrors.cpp
Normal file
137
Source/Integration/BlueprintErrors.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
181
Source/Integration/BlueprintErrors.h
Normal file
181
Source/Integration/BlueprintErrors.h
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
//
|
||||||
|
// BlueprintErrors: Better error handling for blueprint errors.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Containers/Array.h"
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "HAL/Platform.h"
|
||||||
|
#include "Misc/OutputDeviceError.h"
|
||||||
|
#include "UObject/NameTypes.h"
|
||||||
|
#include "UObject/ObjectMacros.h"
|
||||||
|
#include "UObject/UObjectGlobals.h"
|
||||||
|
|
||||||
|
#include "BlueprintErrors.generated.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enum class ElxLogVerbosity, below, contains all the same error severity levels
|
||||||
|
* as ELogVerbosity, but in a form that the blueprint editor can manipulate.
|
||||||
|
*
|
||||||
|
* We deliberately moved 'Fatal' to the end of the list, and made 'Error' option 0.
|
||||||
|
* We did that because we want the editor to default to 'Error' in most cases.
|
||||||
|
* Unfortunately, that means the numeric values of the two enums don't match up,
|
||||||
|
* so we will need a conversion function.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** Log Verbosity: The importance of an error message, which affects which logs the error
|
||||||
|
* message gets written to, and how that message gets filtered.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ElxLogVerbosity : uint8 {
|
||||||
|
|
||||||
|
/* Prints an error to the console and log file. The editor collects and reports errors. */
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/* Prints a warning to the console and log file. The editor collects and report warnings. */
|
||||||
|
Warning,
|
||||||
|
|
||||||
|
/* Prints a message to the console and log file. */
|
||||||
|
Display,
|
||||||
|
|
||||||
|
/* Prints a message to the log file, however, it does not print to the console. */
|
||||||
|
Log,
|
||||||
|
|
||||||
|
/* Prints a message to a log file only if Verbose logging is enabled for the given category. This is usually used for detailed logging. */
|
||||||
|
Verbose,
|
||||||
|
|
||||||
|
/* Prints a message to a log file. If VeryVerbose logging is enabled, then this is used for detailed logging that would otherwise spam output. */
|
||||||
|
VeryVerbose,
|
||||||
|
|
||||||
|
/* Danger! Prints a fatal error to the console and log file, then crashes (this crashes the editor too). */
|
||||||
|
Fatal,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Display Duration: How long to display an error message in the game's viewport.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class ElxErrorDisplayDuration : uint8 {
|
||||||
|
|
||||||
|
/* Do not display the message in the viewport */
|
||||||
|
No_Show = 0,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 1 seconds */
|
||||||
|
Show_1_Seconds = 1,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 2 seconds */
|
||||||
|
Show_2_Seconds = 2,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 3 seconds */
|
||||||
|
Show_3_Seconds = 3,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 4 seconds */
|
||||||
|
Show_4_Seconds = 4,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 5 seconds */
|
||||||
|
Show_5_Seconds = 5,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 10 seconds */
|
||||||
|
Show_10_Seconds = 10,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 20 seconds */
|
||||||
|
Show_20_Seconds = 20,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 30 seconds */
|
||||||
|
Show_30_Seconds = 30,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 40 seconds */
|
||||||
|
Show_40_Seconds = 40,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 50 seconds */
|
||||||
|
Show_50_Seconds = 50,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 60 seconds */
|
||||||
|
Show_60_Seconds = 60,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 70 seconds */
|
||||||
|
Show_70_Seconds = 70,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 80 seconds */
|
||||||
|
Show_80_Seconds = 80,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 90 seconds */
|
||||||
|
Show_90_Seconds = 90,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 1 minutes */
|
||||||
|
Show_1_Minutes = 101,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 2 minutes */
|
||||||
|
Show_2_Minutes = 102,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 3 minutes */
|
||||||
|
Show_3_Minutes = 103,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 4 minutes */
|
||||||
|
Show_4_Minutes = 104,
|
||||||
|
|
||||||
|
/* Display the message in the viewport for 5 minutes */
|
||||||
|
Show_5_Minutes = 105,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A library containing assorted useful functions for blueprint error handling.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS(MinimalAPI)
|
||||||
|
class UlxBlueprintErrorLibrary : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The Format Error Message blueprint node macroexpands, the following
|
||||||
|
// function is the core of the expansion. The actual K2Node itself is in
|
||||||
|
// its own source file.
|
||||||
|
//
|
||||||
|
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
|
||||||
|
static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxErrorDisplayDuration DisplayDuration, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
|
||||||
|
|
||||||
|
// Convert an ElxLogVerbosity to an ELogVerbosity::Type
|
||||||
|
//
|
||||||
|
static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Debug Blueprint Errors output device.
|
||||||
|
*
|
||||||
|
* When an error message gets written to the log, using "Format Error Message,"
|
||||||
|
* or any other means that writes an error message to the log,
|
||||||
|
* we can optionally notify the blueprint debugger to pause execution.
|
||||||
|
* This only affects errors that are generated during blueprint execution.
|
||||||
|
* Errors in other threads do not pause the blueprint.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct FlxDebugBlueprintErrorsOutputDevice : public FOutputDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The constructor and destructor automatically register this output device with GLog.
|
||||||
|
//
|
||||||
|
// This struct doesn't store the sensitivity threshold. It relies on some blueprint
|
||||||
|
// class to do that, so that the threshold can be easily edited with the blueprint
|
||||||
|
// editor. This struct must be initialized with a reference to the threshold variable.
|
||||||
|
//
|
||||||
|
FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef);
|
||||||
|
~FlxDebugBlueprintErrorsOutputDevice();
|
||||||
|
|
||||||
|
// Inspect a log message.
|
||||||
|
//
|
||||||
|
INTEGRATION_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
|
||||||
|
|
||||||
|
// If the device is marked 'CanBeUsedOnMultipleThreads,' then UE_LOG will
|
||||||
|
// call Serialize from the current thread, otherwise, it will call Serialize from
|
||||||
|
// the logging thread. Using the logging thread would defeat the purpose of this
|
||||||
|
// device, so it's imperative that we set this flag.
|
||||||
|
//
|
||||||
|
INTEGRATION_API virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ElxLogVerbosity &Sensitivity;
|
||||||
|
};
|
||||||
@@ -15,12 +15,10 @@
|
|||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "HAL/PlatformCrt.h"
|
#include "HAL/PlatformCrt.h"
|
||||||
#include "Internationalization/Internationalization.h"
|
#include "Internationalization/Internationalization.h"
|
||||||
#include "Internationalization/TextFormatter.h"
|
|
||||||
#include "K2Node_CallFunction.h"
|
#include "K2Node_CallFunction.h"
|
||||||
#include "K2Node_MakeArray.h"
|
#include "K2Node_MakeArray.h"
|
||||||
#include "K2Node_MakeStruct.h"
|
#include "K2Node_MakeStruct.h"
|
||||||
#include "Kismet/KismetMathLibrary.h"
|
#include "Kismet/KismetMathLibrary.h"
|
||||||
#include "Kismet/KismetSystemLibrary.h"
|
|
||||||
#include "Kismet/KismetTextLibrary.h"
|
#include "Kismet/KismetTextLibrary.h"
|
||||||
#include "Kismet2/BlueprintEditorUtils.h"
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||||||
#include "Kismet2/CompilerResultsLog.h"
|
#include "Kismet2/CompilerResultsLog.h"
|
||||||
@@ -115,7 +113,7 @@ void UK2Node_FormatError::CreateCorrectPins()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) {
|
if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) {
|
||||||
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxDisplayDuration>(), DisplayDurationPinName);
|
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxErrorDisplayDuration>(), DisplayDurationPinName);
|
||||||
P->DefaultValue = TEXT("No_Display");
|
P->DefaultValue = TEXT("No_Display");
|
||||||
P->AutogeneratedDefaultValue = P->DefaultValue;
|
P->AutogeneratedDefaultValue = P->DefaultValue;
|
||||||
}
|
}
|
||||||
@@ -318,7 +316,7 @@ void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerConte
|
|||||||
|
|
||||||
// This is the node that does all the Format work and outputs the message.
|
// This is the node that does all the Format work and outputs the message.
|
||||||
UK2Node_CallFunction* CallFormatFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
|
UK2Node_CallFunction* CallFormatFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
|
||||||
UFunction *FormatFunction = UlxFormatErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatErrorLibrary, FormatErrorInternal));
|
UFunction *FormatFunction = UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatErrorInternal));
|
||||||
CallFormatFunction->SetFromFunction(FormatFunction);
|
CallFormatFunction->SetFromFunction(FormatFunction);
|
||||||
CallFormatFunction->AllocateDefaultPins();
|
CallFormatFunction->AllocateDefaultPins();
|
||||||
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFormatFunction, this);
|
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFormatFunction, this);
|
||||||
@@ -608,75 +606,5 @@ FText UK2Node_FormatError::GetMenuCategory() const
|
|||||||
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text);
|
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UlxFormatErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxDisplayDuration 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert verbosity to an internal value.
|
|
||||||
//
|
|
||||||
ELogVerbosity::Type VerbosityValue;
|
|
||||||
switch (Verbosity) {
|
|
||||||
case ElxLogVerbosity::Error: VerbosityValue = ELogVerbosity::Error; break;
|
|
||||||
case ElxLogVerbosity::Warning: VerbosityValue = ELogVerbosity::Warning; break;
|
|
||||||
case ElxLogVerbosity::Display: VerbosityValue = ELogVerbosity::Display; break;
|
|
||||||
case ElxLogVerbosity::Log: VerbosityValue = ELogVerbosity::Log; break;
|
|
||||||
case ElxLogVerbosity::Verbose: VerbosityValue = ELogVerbosity::Verbose; break;
|
|
||||||
case ElxLogVerbosity::VeryVerbose: VerbosityValue = ELogVerbosity::VeryVerbose; break;
|
|
||||||
case ElxLogVerbosity::Fatal: VerbosityValue = ELogVerbosity::Fatal; 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
|
|
||||||
//
|
|
||||||
if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
|
|
||||||
{
|
|
||||||
FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#undef LOCTEXT_NAMESPACE
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "BlueprintErrors.h"
|
||||||
#include "Containers/Array.h"
|
#include "Containers/Array.h"
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "EdGraph/EdGraphNode.h"
|
#include "EdGraph/EdGraphNode.h"
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
#include "UObject/NameTypes.h"
|
#include "UObject/NameTypes.h"
|
||||||
#include "UObject/ObjectMacros.h"
|
#include "UObject/ObjectMacros.h"
|
||||||
#include "UObject/UObjectGlobals.h"
|
#include "UObject/UObjectGlobals.h"
|
||||||
|
#include "BlueprintErrors.h"
|
||||||
|
|
||||||
#include "FormatError.generated.h"
|
#include "FormatError.generated.h"
|
||||||
|
|
||||||
@@ -23,121 +25,6 @@ class UObject;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// The following UENUM contains all the ELogVerbosity levels, in a form
|
|
||||||
// that the blueprint editor can manipulate.
|
|
||||||
//
|
|
||||||
// We deliberately moved Fatal to the end of the list, because the editor
|
|
||||||
// will display these values in the order shown here, and we want Error to
|
|
||||||
// be the value that is 'promoted', and we want Fatal to be buried as a
|
|
||||||
// rarely-used option.
|
|
||||||
//
|
|
||||||
|
|
||||||
/** Log Verbosity: The importance of the message, which affects where the message goes and how it is filtered. */
|
|
||||||
UENUM(BlueprintType)
|
|
||||||
enum class ElxLogVerbosity : uint8 {
|
|
||||||
|
|
||||||
/* Prints an error to the console and log file. The editor collects and reports errors. */
|
|
||||||
Error,
|
|
||||||
|
|
||||||
/* Prints a warning to the console and log file. The editor collects and report warnings. */
|
|
||||||
Warning,
|
|
||||||
|
|
||||||
/* Prints a message to the console and log file. */
|
|
||||||
Display,
|
|
||||||
|
|
||||||
/* Prints a message to the log file, however, it does not print to the console. */
|
|
||||||
Log,
|
|
||||||
|
|
||||||
/* Prints a message to a log file only if Verbose logging is enabled for the given category. This is usually used for detailed logging. */
|
|
||||||
Verbose,
|
|
||||||
|
|
||||||
/* Prints a message to a log file. If VeryVerbose logging is enabled, then this is used for detailed logging that would otherwise spam output. */
|
|
||||||
VeryVerbose,
|
|
||||||
|
|
||||||
/* Danger! Prints a fatal error to the console and log file, then crashes (this crashes the editor too). */
|
|
||||||
Fatal,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Display Duration: How long to display a message in the game's viewport */
|
|
||||||
UENUM(BlueprintType)
|
|
||||||
enum class ElxDisplayDuration : uint8 {
|
|
||||||
|
|
||||||
/* Do not display the message in the viewport */
|
|
||||||
No_Show = 0,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 1 seconds */
|
|
||||||
Show_1_Seconds = 1,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 2 seconds */
|
|
||||||
Show_2_Seconds = 2,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 3 seconds */
|
|
||||||
Show_3_Seconds = 3,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 4 seconds */
|
|
||||||
Show_4_Seconds = 4,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 5 seconds */
|
|
||||||
Show_5_Seconds = 5,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 10 seconds */
|
|
||||||
Show_10_Seconds = 10,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 20 seconds */
|
|
||||||
Show_20_Seconds = 20,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 30 seconds */
|
|
||||||
Show_30_Seconds = 30,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 40 seconds */
|
|
||||||
Show_40_Seconds = 40,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 50 seconds */
|
|
||||||
Show_50_Seconds = 50,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 60 seconds */
|
|
||||||
Show_60_Seconds = 60,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 70 seconds */
|
|
||||||
Show_70_Seconds = 70,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 80 seconds */
|
|
||||||
Show_80_Seconds = 80,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 90 seconds */
|
|
||||||
Show_90_Seconds = 90,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 1 minutes */
|
|
||||||
Show_1_Minutes = 101,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 2 minutes */
|
|
||||||
Show_2_Minutes = 102,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 3 minutes */
|
|
||||||
Show_3_Minutes = 103,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 4 minutes */
|
|
||||||
Show_4_Minutes = 104,
|
|
||||||
|
|
||||||
/* Display the message in the viewport for 5 minutes */
|
|
||||||
Show_5_Minutes = 105,
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Library functions used by Format Error Message.
|
|
||||||
//
|
|
||||||
UCLASS(MinimalAPI)
|
|
||||||
class UlxFormatErrorLibrary : public UBlueprintFunctionLibrary
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
|
|
||||||
static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxDisplayDuration DisplayDuration, const FString &InPattern, TArray<FFormatArgumentData> InArgs);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// The Format Error Message K2Node.
|
// The Format Error Message K2Node.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ void AIntegrationGameModeBase::ResetToInitialState()
|
|||||||
w->release(w.Get());
|
w->release(w.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop trapping log errors to the debugger.
|
||||||
|
BreakToDebuggerLogVerbosityDevice.Reset();
|
||||||
|
|
||||||
// Clear the lua call assembly buffer.
|
// Clear the lua call assembly buffer.
|
||||||
LuaCallBuffer.clear();
|
LuaCallBuffer.clear();
|
||||||
|
|
||||||
@@ -316,6 +319,10 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
// Initialize the tangible manager.
|
// Initialize the tangible manager.
|
||||||
TangibleManager = NewObject<UlxTangibleManager>();
|
TangibleManager = NewObject<UlxTangibleManager>();
|
||||||
TangibleManager->Init(GetWorld(), this);
|
TangibleManager->Init(GetWorld(), this);
|
||||||
|
|
||||||
|
// If somebody generates a log message that's severe enough, break to debugger.
|
||||||
|
BreakToDebuggerLogVerbosityDevice.Reset(
|
||||||
|
new FlxDebugBlueprintErrorsOutputDevice(BreakToDebuggerLogVerbosity));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "TangibleManager.h"
|
#include "TangibleManager.h"
|
||||||
#include "LuprexSockets.h"
|
#include "LuprexSockets.h"
|
||||||
#include "TriggeredTask.h"
|
#include "TriggeredTask.h"
|
||||||
|
#include "BlueprintErrors.h"
|
||||||
#include "IntegrationGameModeBase.generated.h"
|
#include "IntegrationGameModeBase.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -118,6 +119,10 @@ public:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
AActor *CurrentLookAt;
|
AActor *CurrentLookAt;
|
||||||
|
|
||||||
|
// The sensitivity level at which a log message triggers a debugger breakpoint.
|
||||||
|
UPROPERTY(EditAnywhere, Category="Debugging Tools")
|
||||||
|
ElxLogVerbosity BreakToDebuggerLogVerbosity;
|
||||||
|
|
||||||
// This stores the entire text currently visible in the console.
|
// This stores the entire text currently visible in the console.
|
||||||
FlxConsoleOutput ConsoleOutput;
|
FlxConsoleOutput ConsoleOutput;
|
||||||
|
|
||||||
@@ -150,4 +155,7 @@ public:
|
|||||||
// These allow us to pre-tick and post-tick.
|
// These allow us to pre-tick and post-tick.
|
||||||
FDelegateHandle OnWorldPreActorTickHandle;
|
FDelegateHandle OnWorldPreActorTickHandle;
|
||||||
FDelegateHandle OnWorldPostActorTickHandle;
|
FDelegateHandle OnWorldPostActorTickHandle;
|
||||||
|
|
||||||
|
// The device that implements BreakToDebuggerLogVerbosity, above.
|
||||||
|
TUniquePtr<FlxDebugBlueprintErrorsOutputDevice> BreakToDebuggerLogVerbosityDevice;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,21 +7,11 @@
|
|||||||
|
|
||||||
#define LOCTEXT_NAMESPACE "Luprex Utility"
|
#define LOCTEXT_NAMESPACE "Luprex Utility"
|
||||||
|
|
||||||
void UlxUtilityLibrary::Assert(bool condition, const FString &message) {
|
|
||||||
if (!condition) {
|
|
||||||
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::FatalError, FText::FromString(message));
|
|
||||||
FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namepart1, const FString &namepart2, const FString &fallback, bool bFailIfNotFound) {
|
void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namepart1, const FString &namepart2, const FString &fallback, bool bFailIfNotFound) {
|
||||||
FString fullname = namepart1 + namepart2;
|
FString fullname = namepart1 + namepart2;
|
||||||
if (!IsValid(object)) {
|
if (!IsValid(object)) {
|
||||||
const FBlueprintExceptionInfo ExceptionInfo(
|
UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, object passed in is not valid."));
|
||||||
EBlueprintExceptionType::FatalError,
|
|
||||||
LOCTEXT("CallFunctionByName_ObjectIsNotValid", "In CallFunctionByName, object passed in is not valid.")
|
|
||||||
);
|
|
||||||
FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UFunction* function = object->FindFunction(FName(*fullname));
|
UFunction* function = object->FindFunction(FName(*fullname));
|
||||||
@@ -31,20 +21,12 @@ void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namep
|
|||||||
if (!bFailIfNotFound) {
|
if (!bFailIfNotFound) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const FBlueprintExceptionInfo ExceptionInfo(
|
UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, cannot find the named function or the fallback function"));
|
||||||
EBlueprintExceptionType::FatalError,
|
|
||||||
LOCTEXT("CallFunctionByName_NoSuchFunction", "In CallFunctionByName, cannot find the named function or the fallback function.")
|
|
||||||
);
|
|
||||||
FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (function->ParmsSize != 0) {
|
if (function->ParmsSize != 0) {
|
||||||
const FBlueprintExceptionInfo ExceptionInfo(
|
UE_LOG(LogBlueprint, Error, TEXT("CallFunctionByName can only call functions that have no parameters and no return values"));
|
||||||
EBlueprintExceptionType::FatalError,
|
|
||||||
LOCTEXT("CallFunctionByName_FunctionHasParameters", "CallFunctionByName can only call functions that have no parameters and no return values.")
|
|
||||||
);
|
|
||||||
FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
object->ProcessEvent(function, nullptr);
|
object->ProcessEvent(function, nullptr);
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ class INTEGRATION_API UlxUtilityLibrary : public UObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// If condition is false, quit the game, reporting an error message to the log.
|
|
||||||
//
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Utility")
|
|
||||||
static void Assert(bool condition, const FString &ErrorMessage);
|
|
||||||
|
|
||||||
// Call a function by name, on any UObject. If the function doesn't exist, calls
|
// Call a function by name, on any UObject. If the function doesn't exist, calls
|
||||||
// the fallback function instead. If that isn't found either, returns false.
|
// the fallback function instead. If that isn't found either, returns false.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user