diff --git a/Source/Integration/FormatError.cpp b/Source/Integration/FormatError.cpp index d8baac87..12d04150 100644 --- a/Source/Integration/FormatError.cpp +++ b/Source/Integration/FormatError.cpp @@ -76,7 +76,15 @@ static bool IsFormatPin(const UEdGraphPin *Pin) { UK2Node_FormatError::UK2Node_FormatError(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - NodeTooltip = LOCTEXT("NodeTooltip", "Builds a formatted string using available format argument values.\n \u2022 Use {} to denote format arguments.\n \u2022 Argument types may be Byte, Integer, Float, Text, String, Name, Boolean, Object or ETextGender."); + NodeTooltip = LOCTEXT("NodeTooltip", + "Output an error, warning, or informational message to the log file.\n" + "\n" + " \u2022 Use {ArgName} to denote format arguments, giving each argument a different ArgName.\n" + " \u2022 Arguments may be Byte, Integer, Float, Text, String, Name, Boolean, Object or ETextGender.\n" + "\n" + "It is often desirable to use this in conjunction with a separate utility that\n" + "pauses the execution of the blueprint whenever an error is logged." + ); } void UK2Node_FormatError::AllocateDefaultPins() @@ -198,14 +206,18 @@ FText UK2Node_FormatError::GetNodeTitle(ENodeTitleType::Type TitleType) const FText UK2Node_FormatError::GetPinDisplayName(const UEdGraphPin* Pin) const { - // Many pins can go unlabeled if they have default values. - if ((Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) || - IsFormatPin(Pin) || IsVerbosityPin(Pin) || IsDisplayDurationPin(Pin)) + // The exec pins don't need labels. + if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) { - if (Pin->LinkedTo.Num() == 0) { + return FText::GetEmpty(); + } + + // Many pins can go unlabeled if they have default values. + if (IsFormatPin(Pin) || IsVerbosityPin(Pin) || IsDisplayDurationPin(Pin)) + { + if (Pin->LinkedTo.Num() == 0) + { return FText::GetEmpty(); - } else { - return FText::FromName(Pin->PinName); } } @@ -214,7 +226,7 @@ FText UK2Node_FormatError::GetPinDisplayName(const UEdGraphPin* Pin) const return FText::FromString(ArgumentNameRemovePrefix(Pin->PinName)); } - // Probably should never get here. + // Otherwise, just return the Pin Name the normal way. return FText::FromName(Pin->PinName); } @@ -596,17 +608,20 @@ FText UK2Node_FormatError::GetMenuCategory() const return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text); } -void UlxFormatErrorLibrary::FormatErrorInternal(ElxLogVerbosity Verbosity, ElxDisplayDuration DisplayDuration, FText InPattern, TArray InArgs) +void UlxFormatErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxDisplayDuration DisplayDuration, FText InPattern, TArray InArgs) { // Generate the formatted string. + // FText Message = FTextFormatter::Format(MoveTemp(InPattern), 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; @@ -615,21 +630,48 @@ void UlxFormatErrorLibrary::FormatErrorInternal(ElxLogVerbosity Verbosity, ElxDi default: Color = FLinearColor(0.8, 0.8, 0.8); break; } - // Output to Log + // Convert verbosity to an internal value. + // + ELogVerbosity::Type VerbosityValue; switch (Verbosity) { - case ElxLogVerbosity::Fatal: UE_LOG(LogBlueprint, Fatal, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::Error: UE_LOG(LogBlueprint, Error, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::Warning: UE_LOG(LogBlueprint, Warning, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::Display: UE_LOG(LogBlueprint, Display, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::Log: UE_LOG(LogBlueprint, Log, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::Verbose: UE_LOG(LogBlueprint, Verbose, TEXT("%s"), *MessageString); break; - case ElxLogVerbosity::VeryVerbose: UE_LOG(LogBlueprint, VeryVerbose, TEXT("%s"), *MessageString); break; + 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; } - // Output to Screen (Maybe) - if (Seconds != 0) { + // 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 + // + FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString); } diff --git a/Source/Integration/FormatError.h b/Source/Integration/FormatError.h index 8cfa1647..0a9bcd6a 100644 --- a/Source/Integration/FormatError.h +++ b/Source/Integration/FormatError.h @@ -21,44 +21,106 @@ class UEdGraph; class UObject; -// -// Export log verbosity to blueprints. -// -UENUM(BlueprintType) -enum class ElxLogVerbosity : uint8 { - Fatal, - Error, - Warning, - Display, - Log, - Verbose, - VeryVerbose, -}; + // -// Display Duration - how long to show a message on-screen. +// 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 { - No_Display = 0, + + /* Do not display the message in the viewport */ + No_Display_in_Viewport = 0, + + /* Display the message in the viewport for 1 second */ Display_1_Seconds = 1, + + /* Display the message in the viewport for 2 second */ Display_2_Seconds = 2, + + /* Display the message in the viewport for 3 second */ Display_3_Seconds = 3, + + /* Display the message in the viewport for 4 second */ Display_4_Seconds = 4, + + /* Display the message in the viewport for 5 second */ Display_5_Seconds = 5, + + /* Display the message in the viewport for 10 second */ Display_10_Seconds = 10, + + /* Display the message in the viewport for 20 second */ Display_20_Seconds = 20, + + /* Display the message in the viewport for 30 second */ Display_30_Seconds = 30, + + /* Display the message in the viewport for 1 second */ Display_40_Seconds = 40, + + /* Display the message in the viewport for 1 second */ Display_50_Seconds = 50, + + /* Display the message in the viewport for 1 second */ Display_60_Seconds = 60, + + /* Display the message in the viewport for 1 second */ Display_70_Seconds = 70, + + /* Display the message in the viewport for 1 second */ Display_80_Seconds = 80, + + /* Display the message in the viewport for 1 second */ Display_90_Seconds = 90, + + /* Display the message in the viewport for 1 second */ Display_1_Minute = 101, + + /* Display the message in the viewport for 1 second */ Display_2_Minutes = 102, + + /* Display the message in the viewport for 1 second */ Display_3_Minutes = 103, + + /* Display the message in the viewport for 1 second */ Display_4_Minutes = 104, + + /* Display the message in the viewport for 1 second */ Display_5_Minutes = 105, }; @@ -71,13 +133,13 @@ class UlxFormatErrorLibrary : public UBlueprintFunctionLibrary GENERATED_BODY() public: - UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true")) - static void FormatErrorInternal(ElxLogVerbosity Verbosity, ElxDisplayDuration DisplayDuration, FText InPattern, TArray InArgs); + UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true")) + static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxDisplayDuration DisplayDuration, FText InPattern, TArray InArgs); }; + // -// Library functions used by Format Error Message, and also -// by scripters writing their own Error Message Handler classes. +// The Format Error Message K2Node. // UCLASS(MinimalAPI) class UK2Node_FormatError : public UK2Node