// // BlueprintErrors: Better error handling for blueprint errors. // #pragma once #include "Containers/Array.h" #include "CoreMinimal.h" #include "InputCoreTypes.h" #include "HAL/Platform.h" #include "Misc/OutputDeviceError.h" #include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" #include "UObject/UObjectGlobals.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "BlueprintErrors.generated.h" class UlxLuaValues; struct FlxAnimationStep; /* * 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, }; /* 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, const FString &InPattern, TArray InArgs); // A formatting routine for pins that were never connected. // UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataBlank(const FString &Name); // A specialized formatting routine for pins of enum types. // UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject); // Convert an ElxLogVerbosity to an ELogVerbosity::Type // static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity); }; /* * A library that contains functions that convert data into FFormatArgumentData * structs, so that the data can be passed to FText::Format. */ UCLASS(MinimalAPI) class UlxFormatDataLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataBool(bool Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataByte(uint8 Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataInt(int Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataInt64(int64 Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataFloat(float Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataDouble(double Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataText(FText Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataString(FString Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataName(FName Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataKey(FKey Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataGender(ETextGender Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataObject(UObject *Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataVector(const FVector &Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataVector2D(const FVector2D &Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataRotator(const FRotator &Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *Value, const FString &Name); UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &Value, const FString &Name); }; /* 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; };