diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset old mode 100755 new mode 100644 index 02b9c9f9..6044bebd --- a/Content/Luprex/lxGameMode.uasset +++ b/Content/Luprex/lxGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f98cfcb8de9d911f6578aec8989768fbb0d0147a5c1cfaa96dca50866125d3e -size 91884 +oid sha256:ff3b179343b33112026efc2151f88cad4ca374a204d858e500e5a90c09703fda +size 93870 diff --git a/Content/Tangibles/tangiblecharacter.uasset b/Content/Tangibles/tangiblecharacter.uasset index a483da5e..4acbc143 100644 --- a/Content/Tangibles/tangiblecharacter.uasset +++ b/Content/Tangibles/tangiblecharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87e7eeaef41ab4b963325a54691493c65b17a1690b7c2d2e27e36cac4fde062b -size 377351 +oid sha256:0150775dc3b38dc7ba59636a56e0f15625099e146127fbc5fbac2676cf8d726b +size 365847 diff --git a/Source/Integration/AnimQueue.cpp b/Source/Integration/AnimQueue.cpp index 1309b659..7cc130eb 100644 --- a/Source/Integration/AnimQueue.cpp +++ b/Source/Integration/AnimQueue.cpp @@ -81,8 +81,9 @@ void UlxAnimationStepLibrary::UnpackAnimationStep(bool &bChanged, FString &Actio bChanged = false; Action = TEXT(""); - if (prefix.IsEmpty()) { - UlxUtilityLibrary::Assert(false, TEXT("You may not pass an empty string for prefix")); + if (prefix.IsEmpty()) + { + UE_LOG(LogBlueprint, Error, TEXT("UnpackAnimationStep: You may not pass an empty string for prefix")); return; } @@ -92,8 +93,7 @@ void UlxAnimationStepLibrary::UnpackAnimationStep(bool &bChanged, FString &Actio FStructProperty* stepproperty = FindAnimationStepProperty(uclass, prefix); if (stepproperty == nullptr) { - UE_LOG(LogBlueprint, Error, TEXT("Target object: %s Prefix: %s"), *(target->GetName()), *prefix); - UlxUtilityLibrary::Assert(false, TEXT("Target object does not have an variable named ' Animation Step'")); + UE_LOG(LogBlueprint, Error, TEXT("UnpackAnimationStep: Target object does not have a variable named: '%s Animation Step'"), *prefix); return; } diff --git a/Source/Integration/BlueprintErrors.cpp b/Source/Integration/BlueprintErrors.cpp new file mode 100644 index 00000000..6271ec76 --- /dev/null +++ b/Source/Integration/BlueprintErrors.cpp @@ -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 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); + } +} + + +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); +} + + diff --git a/Source/Integration/BlueprintErrors.h b/Source/Integration/BlueprintErrors.h new file mode 100644 index 00000000..5bab638d --- /dev/null +++ b/Source/Integration/BlueprintErrors.h @@ -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 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; +}; diff --git a/Source/Integration/FormatError.cpp b/Source/Integration/FormatError.cpp index cc23791e..f1de0e29 100644 --- a/Source/Integration/FormatError.cpp +++ b/Source/Integration/FormatError.cpp @@ -15,12 +15,10 @@ #include "Engine/Blueprint.h" #include "HAL/PlatformCrt.h" #include "Internationalization/Internationalization.h" -#include "Internationalization/TextFormatter.h" #include "K2Node_CallFunction.h" #include "K2Node_MakeArray.h" #include "K2Node_MakeStruct.h" #include "Kismet/KismetMathLibrary.h" -#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetTextLibrary.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/CompilerResultsLog.h" @@ -115,7 +113,7 @@ void UK2Node_FormatError::CreateCorrectPins() } if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) { - UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), DisplayDurationPinName); + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), DisplayDurationPinName); P->DefaultValue = TEXT("No_Display"); 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. UK2Node_CallFunction* CallFormatFunction = CompilerContext.SpawnIntermediateNode(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->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFormatFunction, this); @@ -608,75 +606,5 @@ FText UK2Node_FormatError::GetMenuCategory() const return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text); } -void UlxFormatErrorLibrary::FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxDisplayDuration 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; - } - - // 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 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 - // - if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) - { - FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString); - } -} - #undef LOCTEXT_NAMESPACE diff --git a/Source/Integration/FormatError.h b/Source/Integration/FormatError.h index ea96cf6b..6735f652 100644 --- a/Source/Integration/FormatError.h +++ b/Source/Integration/FormatError.h @@ -2,6 +2,7 @@ #pragma once +#include "BlueprintErrors.h" #include "Containers/Array.h" #include "CoreMinimal.h" #include "EdGraph/EdGraphNode.h" @@ -12,6 +13,7 @@ #include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" #include "UObject/UObjectGlobals.h" +#include "BlueprintErrors.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 InArgs); -}; - - // // The Format Error Message K2Node. // diff --git a/Source/Integration/IntegrationGameModeBase.cpp b/Source/Integration/IntegrationGameModeBase.cpp index ce254d82..a4b211c7 100644 --- a/Source/Integration/IntegrationGameModeBase.cpp +++ b/Source/Integration/IntegrationGameModeBase.cpp @@ -77,6 +77,9 @@ void AIntegrationGameModeBase::ResetToInitialState() w->release(w.Get()); } + // Stop trapping log errors to the debugger. + BreakToDebuggerLogVerbosityDevice.Reset(); + // Clear the lua call assembly buffer. LuaCallBuffer.clear(); @@ -316,6 +319,10 @@ void AIntegrationGameModeBase::BeginPlay() // Initialize the tangible manager. TangibleManager = NewObject(); 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) diff --git a/Source/Integration/IntegrationGameModeBase.h b/Source/Integration/IntegrationGameModeBase.h index 05f2bda3..98f57409 100644 --- a/Source/Integration/IntegrationGameModeBase.h +++ b/Source/Integration/IntegrationGameModeBase.h @@ -10,6 +10,7 @@ #include "TangibleManager.h" #include "LuprexSockets.h" #include "TriggeredTask.h" +#include "BlueprintErrors.h" #include "IntegrationGameModeBase.generated.h" @@ -118,6 +119,10 @@ public: UPROPERTY() 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. FlxConsoleOutput ConsoleOutput; @@ -150,4 +155,7 @@ public: // These allow us to pre-tick and post-tick. FDelegateHandle OnWorldPreActorTickHandle; FDelegateHandle OnWorldPostActorTickHandle; + + // The device that implements BreakToDebuggerLogVerbosity, above. + TUniquePtr BreakToDebuggerLogVerbosityDevice; }; diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index 05a3733f..93d0cdbd 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -7,21 +7,11 @@ #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) { FString fullname = namepart1 + namepart2; if (!IsValid(object)) { - const FBlueprintExceptionInfo ExceptionInfo( - EBlueprintExceptionType::FatalError, - LOCTEXT("CallFunctionByName_ObjectIsNotValid", "In CallFunctionByName, object passed in is not valid.") - ); - FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); + UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, object passed in is not valid.")); return; } UFunction* function = object->FindFunction(FName(*fullname)); @@ -31,20 +21,12 @@ void UlxUtilityLibrary::CallFunctionByName(UObject *object, const FString &namep if (!bFailIfNotFound) { return; } - const FBlueprintExceptionInfo ExceptionInfo( - EBlueprintExceptionType::FatalError, - LOCTEXT("CallFunctionByName_NoSuchFunction", "In CallFunctionByName, cannot find the named function or the fallback function.") - ); - FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); + UE_LOG(LogBlueprint, Error, TEXT("In CallFunctionByName, cannot find the named function or the fallback function")); return; } } if (function->ParmsSize != 0) { - const FBlueprintExceptionInfo ExceptionInfo( - 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); + UE_LOG(LogBlueprint, Error, TEXT("CallFunctionByName can only call functions that have no parameters and no return values")); return; } object->ProcessEvent(function, nullptr); diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index f183adc7..4dbffa13 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -20,11 +20,6 @@ class INTEGRATION_API UlxUtilityLibrary : public UObject GENERATED_BODY() 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 // the fallback function instead. If that isn't found either, returns false.