lxGameMode can now trap UE_LOG Error into the debugger

This commit is contained in:
2024-11-22 23:01:05 -05:00
parent c1f2d74351
commit 75dcdf54cc
11 changed files with 348 additions and 223 deletions

BIN
Content/Luprex/lxGameMode.uasset LFS Executable file → Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -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 '<prefix> Animation Step'"));
UE_LOG(LogBlueprint, Error, TEXT("UnpackAnimationStep: Target object does not have a variable named: '%s Animation Step'"), *prefix);
return;
}

View 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);
}

View 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;
};

View File

@@ -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<ElxDisplayDuration>(), DisplayDurationPinName);
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxErrorDisplayDuration>(), 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<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->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<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

View File

@@ -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<FFormatArgumentData> InArgs);
};
//
// The Format Error Message K2Node.
//

View File

@@ -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<UlxTangibleManager>();
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)

View File

@@ -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<FlxDebugBlueprintErrorsOutputDevice> BreakToDebuggerLogVerbosityDevice;
};

View File

@@ -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);

View File

@@ -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.