Lots of work on FormatMessage and FormatErrorMessage. These can now print enums.

This commit is contained in:
2025-03-24 21:49:09 -04:00
parent 5ef3adc527
commit 408c73521d
5 changed files with 200 additions and 60 deletions

Binary file not shown.

View File

@@ -83,6 +83,15 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool Value, con
return Result; return Result;
} }
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 Value, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int Value, const FString &Name) FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int Value, const FString &Name)
{ {
FFormatArgumentData Result; FFormatArgumentData Result;
@@ -200,7 +209,7 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTra
return Result; return Result;
} }
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString &Name) FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataBlank(const FString &Name)
{ {
FFormatArgumentData Result; FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentValueType = EFormatArgumentType::Text;
@@ -209,6 +218,25 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString
return Result; return Result;
} }
FFormatArgumentData UlxBlueprintErrorLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject)
{
const UEnum *Enum = Cast<const UEnum>(PinSubCategoryObject);
FFormatArgumentData Result;
if (Enum == nullptr)
{
Result.ArgumentValueType = EFormatArgumentType::Int;
Result.ArgumentName = Name;
Result.ArgumentValueInt = Value;
}
else
{
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value));
}
return Result;
}
FlxDebugBlueprintErrorsOutputDevice::FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef) FlxDebugBlueprintErrorsOutputDevice::FlxDebugBlueprintErrorsOutputDevice(const ElxLogVerbosity &SensitivityRef)
: Sensitivity(SensitivityRef) : Sensitivity(SensitivityRef)
{ {

View File

@@ -144,6 +144,16 @@ public:
UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true")) UFUNCTION(BlueprintCallable, meta=(WorldContext = "Context", BlueprintInternalUseOnly = "true"))
static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxErrorDisplayDuration DisplayDuration, const FString &InPattern, TArray<FFormatArgumentData> InArgs); static void FormatErrorInternal(UObject *Context, ElxLogVerbosity Verbosity, ElxErrorDisplayDuration DisplayDuration, const FString &InPattern, TArray<FFormatArgumentData> 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 // Convert an ElxLogVerbosity to an ELogVerbosity::Type
// //
static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity); static ELogVerbosity::Type ConvertElxLogVerbosity(ElxLogVerbosity Verbosity);
@@ -162,6 +172,9 @@ public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBool(bool Value, const FString &Name); 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") UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataInt(int Value, const FString &Name); static FFormatArgumentData FormatArgumentDataInt(int Value, const FString &Name);
@@ -200,9 +213,6 @@ public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility") UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name); static FFormatArgumentData FormatArgumentDataTransform(const FTransform &Value, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBlank(const FString &Name);
}; };
/* Debug Blueprint Errors output device. /* Debug Blueprint Errors output device.

View File

@@ -37,7 +37,7 @@
#include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h" #include "UObject/WeakObjectPtrTemplates.h"
#define LOCTEXT_NAMESPACE "FormatError" #define LOCTEXT_NAMESPACE "FormatMessage"
// All argument pins will have Names that start with "A:" // All argument pins will have Names that start with "A:"
@@ -71,26 +71,19 @@ static bool IsFormatPin(const UEdGraphPin *Pin) {
return (Pin->PinName == FormatPinName); return (Pin->PinName == FormatPinName);
} }
UK2Node_FormatError::UK2Node_FormatError(const FObjectInitializer& ObjectInitializer) static const FName ResultPinName(TEXT("Result"));
: Super(ObjectInitializer) static bool IsResultPin(const UEdGraphPin *Pin) {
{ return (Pin->PinName == ResultPinName);
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"
"\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()
void UK2Node_FormatMessage::AllocateDefaultPins()
{ {
Super::AllocateDefaultPins(); Super::AllocateDefaultPins();
CreateCorrectPins(); CreateCorrectPins();
} }
void UK2Node_FormatError::CreateCorrectPins() void UK2Node_FormatMessage::CreateCorrectPins()
{ {
if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) { if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) {
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
@@ -105,19 +98,34 @@ void UK2Node_FormatError::CreateCorrectPins()
P->DefaultValue = TEXT("Error Message"); P->DefaultValue = TEXT("Error Message");
} }
if (FindPin(VerbosityPinName, EGPD_Input) == nullptr) { // If this is a FormatMessage node, create a pin to output the result as text.
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxLogVerbosity>(), VerbosityPinName); //
P->DefaultValue = TEXT("Error"); if (!IsFormatErrorMessage())
P->AutogeneratedDefaultValue = P->DefaultValue; {
if (FindPin(ResultPinName, EGPD_Output) == nullptr) {
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Text, ResultPinName);
}
} }
if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) { // If this is a FormatErrorMessage node, create pins that control the log verbosity
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxErrorDisplayDuration>(), DisplayDurationPinName); //
P->DefaultValue = TEXT("No_Show"); if (IsFormatErrorMessage())
P->AutogeneratedDefaultValue = P->DefaultValue; {
if (FindPin(VerbosityPinName, EGPD_Input) == nullptr) {
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxLogVerbosity>(), VerbosityPinName);
P->DefaultValue = TEXT("Error");
P->AutogeneratedDefaultValue = P->DefaultValue;
}
if (FindPin(DisplayDurationPinName, EGPD_Input) == nullptr) {
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum<ElxErrorDisplayDuration>(), DisplayDurationPinName);
P->DefaultValue = TEXT("No_Show");
P->AutogeneratedDefaultValue = P->DefaultValue;
}
} }
// Transfer all Existing Argument pins to the Old Pins Map. // Transfer all Existing Argument pins to the Old Pins Map.
//
TMap<FString, UEdGraphPin *> OldPins; TMap<FString, UEdGraphPin *> OldPins;
for (auto It = Pins.CreateIterator(); It; ++It) for (auto It = Pins.CreateIterator(); It; ++It)
{ {
@@ -130,6 +138,7 @@ void UK2Node_FormatError::CreateCorrectPins()
} }
// Create Argument pins in the correct order, reusing old pins where possible. // Create Argument pins in the correct order, reusing old pins where possible.
//
for (const FString& Name : PinNames) for (const FString& Name : PinNames)
{ {
UEdGraphPin **OldPin = OldPins.Find(Name); UEdGraphPin **OldPin = OldPins.Find(Name);
@@ -143,6 +152,7 @@ void UK2Node_FormatError::CreateCorrectPins()
} }
// Delete any unused pins. // Delete any unused pins.
//
for (auto &iter : OldPins) for (auto &iter : OldPins)
{ {
iter.Value->Modify(); iter.Value->Modify();
@@ -152,7 +162,7 @@ void UK2Node_FormatError::CreateCorrectPins()
} }
void UK2Node_FormatError::SynchronizeArgumentPinType(UEdGraphPin* Pin) void UK2Node_FormatMessage::SynchronizeArgumentPinType(UEdGraphPin* Pin)
{ {
if (IsArgumentPin(Pin)) if (IsArgumentPin(Pin))
{ {
@@ -196,12 +206,19 @@ void UK2Node_FormatError::SynchronizeArgumentPinType(UEdGraphPin* Pin)
} }
} }
FText UK2Node_FormatError::GetNodeTitle(ENodeTitleType::Type TitleType) const FText UK2Node_FormatMessage::GetNodeTitle(ENodeTitleType::Type TitleType) const
{ {
return LOCTEXT("FormatError_Title", "Format Error Message"); if (IsFormatErrorMessage())
{
return LOCTEXT("FormatErrorMessage_Title", "Format Error Message");
}
else
{
return LOCTEXT("FormatMessage_Title", "Format Message");
}
} }
FText UK2Node_FormatError::GetPinDisplayName(const UEdGraphPin* Pin) const FText UK2Node_FormatMessage::GetPinDisplayName(const UEdGraphPin* Pin) const
{ {
// The exec pins don't need labels. // The exec pins don't need labels.
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
@@ -227,10 +244,10 @@ FText UK2Node_FormatError::GetPinDisplayName(const UEdGraphPin* Pin) const
return FText::FromName(Pin->PinName); return FText::FromName(Pin->PinName);
} }
void UK2Node_FormatError::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) void UK2Node_FormatMessage::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{ {
const FName PropertyName = (PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None); const FName PropertyName = (PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None);
if (PropertyName == GET_MEMBER_NAME_CHECKED(UK2Node_FormatError, PinNames)) if (PropertyName == GET_MEMBER_NAME_CHECKED(UK2Node_FormatMessage, PinNames))
{ {
ReconstructNode(); ReconstructNode();
} }
@@ -238,13 +255,13 @@ void UK2Node_FormatError::PostEditChangeProperty(struct FPropertyChangedEvent& P
GetGraph()->NotifyNodeChanged(this); GetGraph()->NotifyNodeChanged(this);
} }
void UK2Node_FormatError::PinConnectionListChanged(UEdGraphPin* Pin) void UK2Node_FormatMessage::PinConnectionListChanged(UEdGraphPin* Pin)
{ {
Modify(); Modify();
SynchronizeArgumentPinType(Pin); SynchronizeArgumentPinType(Pin);
} }
void UK2Node_FormatError::PinDefaultValueChanged(UEdGraphPin* Pin) void UK2Node_FormatMessage::PinDefaultValueChanged(UEdGraphPin* Pin)
{ {
if(IsFormatPin(Pin)) if(IsFormatPin(Pin))
{ {
@@ -255,19 +272,19 @@ void UK2Node_FormatError::PinDefaultValueChanged(UEdGraphPin* Pin)
} }
} }
void UK2Node_FormatError::PinTypeChanged(UEdGraphPin* Pin) void UK2Node_FormatMessage::PinTypeChanged(UEdGraphPin* Pin)
{ {
SynchronizeArgumentPinType(Pin); SynchronizeArgumentPinType(Pin);
Super::PinTypeChanged(Pin); Super::PinTypeChanged(Pin);
} }
FText UK2Node_FormatError::GetTooltipText() const FText UK2Node_FormatMessage::GetTooltipText() const
{ {
return NodeTooltip; return NodeTooltip;
} }
void UK2Node_FormatError::PostReconstructNode() void UK2Node_FormatMessage::PostReconstructNode()
{ {
Super::PostReconstructNode(); Super::PostReconstructNode();
@@ -284,16 +301,21 @@ void UK2Node_FormatError::PostReconstructNode()
// Get a function that can convert the specified type into a FFormatArgumentData. // Get a function that can convert the specified type into a FFormatArgumentData.
// //
// For example:
// * if you pass in a pin of type 'String', it will return UlxFormatDataLibrary::FormatArgumentDataString
// * if you pass in a pin of type 'Vector', it will return UlxFormatDataLibrary::FormatArgumentDataVector
// and so forth.
//
UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphPinType& PinType, bool AllowWild) UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphPinType& PinType, bool AllowWild)
{ {
// Special case. Wildcard Pins are unconnected pins. // Special case. Wildcard Pins are unconnected pins.
// //
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && AllowWild) if (PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && AllowWild)
{ {
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank)); return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataBlank));
} }
// Try to find an exact match in the UlxFormatDataLibrary. // Try to find a match in the UlxFormatDataLibrary.
// //
for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It) for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It)
{ {
@@ -306,6 +328,14 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
return Function; return Function;
} }
// A general handler for Enums. You can override this for specific enums by
// putting that particular enum into the UlxFormatDataLibrary.
//
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast<const UEnum>(PinType.PinSubCategoryObject)))
{
return UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatArgumentDataEnum));
}
// A case for subclasses of 'Object' which are not exactly 'Object' // A case for subclasses of 'Object' which are not exactly 'Object'
// //
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object) if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
@@ -320,12 +350,12 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) void UK2Node_FormatMessage::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{ {
Super::ExpandNode(CompilerContext, SourceGraph); Super::ExpandNode(CompilerContext, SourceGraph);
/** /**
At the end of this, the UK2Node_FormatError will not be a part of the Blueprint, it merely handles connecting At the end of this, the UK2Node_FormatMessage will not be a part of the Blueprint, it merely handles connecting
the other nodes into the Blueprint. the other nodes into the Blueprint.
*/ */
@@ -336,9 +366,19 @@ void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerConte
MakeArrayNode->AllocateDefaultPins(); MakeArrayNode->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeArrayNode, this); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(MakeArrayNode, this);
// Decide which formatting function we're going to call.
UFunction *FormatFunction;
if (IsFormatErrorMessage())
{
FormatFunction = UlxBlueprintErrorLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxBlueprintErrorLibrary, FormatErrorInternal));
}
else
{
FormatFunction = UKismetTextLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetTextLibrary, Format));
}
// 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 = 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);
@@ -372,16 +412,23 @@ void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerConte
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(ConvertNode, this); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(ConvertNode, this);
UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("Value")); UEdGraphPin *ValuePin = ConvertNode->FindPin(TEXT("Value"));
UEdGraphPin *NamePin = ConvertNode->FindPinChecked(TEXT("Name")); UEdGraphPin *NamePin = ConvertNode->FindPinChecked(TEXT("Name"));
UEdGraphPin *SubCategoryObjectPin = ConvertNode->FindPin(TEXT("PinSubCategoryObject"));
// Set a value for the 'Name' pin of the converter. // Set a value for the 'Name' pin of the converter.
ConvertNode->GetSchema()->TrySetDefaultValue(*NamePin, OriginalName); ConvertNode->GetSchema()->TrySetDefaultValue(*NamePin, OriginalName);
// Connect the Value pin of the converter. // Connect the Value pin of the converter, if any.
if (ValuePin != nullptr) if (ValuePin != nullptr)
{ {
CompilerContext.MovePinLinksToIntermediate(*ArgumentPin, *ValuePin); CompilerContext.MovePinLinksToIntermediate(*ArgumentPin, *ValuePin);
} }
// If the converter wants to know the PinSubCategoryObject, pass it in.
if (SubCategoryObjectPin != nullptr)
{
SubCategoryObjectPin->DefaultObject = Cast<UObject>(ArgumentPin->PinType.PinSubCategoryObject);
}
// The "Make Array" node already has one pin available, so don't create one for ArgIdx == 0 // The "Make Array" node already has one pin available, so don't create one for ArgIdx == 0
if(ArgIdx > 0) if(ArgIdx > 0)
{ {
@@ -396,10 +443,17 @@ void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerConte
ConvertNode->GetReturnValuePin()->MakeLinkTo(InputPin); ConvertNode->GetReturnValuePin()->MakeLinkTo(InputPin);
} }
// Move connection of "Format" pin to the call function's "InPattern" pin // Connect up other pins to the Formatting node.
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(FormatPinName), *CallFormatFunction->FindPinChecked(TEXT("InPattern"))); CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(FormatPinName), *CallFormatFunction->FindPinChecked(TEXT("InPattern")));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(VerbosityPinName), *CallFormatFunction->FindPinChecked(TEXT("Verbosity"))); if (IsFormatErrorMessage())
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(DisplayDurationPinName), *CallFormatFunction->FindPinChecked(TEXT("DisplayDuration"))); {
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(VerbosityPinName), *CallFormatFunction->FindPinChecked(TEXT("Verbosity")));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(DisplayDurationPinName), *CallFormatFunction->FindPinChecked(TEXT("DisplayDuration")));
}
else
{
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(ResultPinName), *CallFormatFunction->GetReturnValuePin());
}
// Link up the Exec pins. // Link up the Exec pins.
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFormatFunction->GetExecPin()); CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *CallFormatFunction->GetExecPin());
@@ -409,7 +463,7 @@ void UK2Node_FormatError::ExpandNode(class FKismetCompilerContext& CompilerConte
} }
UK2Node::ERedirectType UK2Node_FormatError::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const UK2Node::ERedirectType UK2Node_FormatMessage::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
{ {
ERedirectType RedirectType = ERedirectType_None; ERedirectType RedirectType = ERedirectType_None;
@@ -454,7 +508,7 @@ UK2Node::ERedirectType UK2Node_FormatError::DoPinsMatchForReconstruction(const U
return RedirectType; return RedirectType;
} }
bool UK2Node_FormatError::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const bool UK2Node_FormatMessage::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{ {
// The following pins cannot be connected. They are meant to be hardwired constants. // The following pins cannot be connected. They are meant to be hardwired constants.
if (IsFormatPin(MyPin)) if (IsFormatPin(MyPin))
@@ -481,7 +535,7 @@ bool UK2Node_FormatError::IsConnectionDisallowed(const UEdGraphPin* MyPin, const
} }
void UK2Node_FormatError::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const void UK2Node_FormatMessage::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{ {
// actions get registered under specific object-keys; the idea is that // actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is // actions might have to be updated (or deleted) if their object-key is
@@ -501,10 +555,33 @@ void UK2Node_FormatError::GetMenuActions(FBlueprintActionDatabaseRegistrar& Acti
} }
} }
FText UK2Node_FormatError::GetMenuCategory() const FText UK2Node_FormatMessage::GetMenuCategory() const
{ {
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text); return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text);
} }
UK2Node_FormatMessage::UK2Node_FormatMessage(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NodeTooltip = LOCTEXT("NodeTooltip",
"Format a message, and output it as Text.\n"
"\n"
" \u2022 Use {ArgName} to denote format arguments, giving each argument a different ArgName.\n"
"\n"
);
}
UK2Node_FormatErrorMessage::UK2Node_FormatErrorMessage(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
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"
"\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."
);
}
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE

View File

@@ -22,14 +22,21 @@ class FString;
class UEdGraph; class UEdGraph;
class UObject; class UObject;
// //
// The Format Error Message K2Node. // FormatMessage and FormatErrorMessage
//
// This file defines two K2Nodes: FormatMessage, and FormatErrorMessage. The
// only difference between them is that the former outputs the message as an
// output pin. The latter outputs the message to the log instead.
//
// To implement code reuse, we put all the code into FormatMessage, and made
// FormatErrorMessage a derived class of FormatMessage. The derived class
// doesn't override anything - all it does is set a flag, the flag changes
// the behavior of FormatMessage.
//
// //
UCLASS(MinimalAPI) UCLASS(MinimalAPI)
class UK2Node_FormatError : public UK2Node class UK2Node_FormatMessage : public UK2Node
{ {
GENERATED_UCLASS_BODY() GENERATED_UCLASS_BODY()
@@ -60,14 +67,17 @@ class UK2Node_FormatError : public UK2Node
virtual int32 GetNodeRefreshPriority() const override { return EBaseNodeRefreshPriority::Low_UsesDependentWildcard; } virtual int32 GetNodeRefreshPriority() const override { return EBaseNodeRefreshPriority::Low_UsesDependentWildcard; }
//~ End UK2Node Interface. //~ End UK2Node Interface.
private: protected:
/** Create all necessary pins */ /** Create all necessary pins */
void CreateCorrectPins(); void CreateCorrectPins();
/** Synchronize the type of the given argument pin with the type its connected to, or reset it to a wildcard pin if there's no connection */ /** Synchronize the type of the given argument pin with the type its connected to, or reset it to a wildcard pin if there's no connection */
void SynchronizeArgumentPinType(UEdGraphPin* Pin); void SynchronizeArgumentPinType(UEdGraphPin* Pin);
private: /** Our derived class will set this to true, altering the behavior of this K2Node. **/
virtual bool IsFormatErrorMessage() const { return false; }
protected:
/** When adding arguments to the node, their names are placed here and are generated as pins during construction */ /** When adding arguments to the node, their names are placed here and are generated as pins during construction */
UPROPERTY() UPROPERTY()
TArray<FString> PinNames; TArray<FString> PinNames;
@@ -76,3 +86,18 @@ private:
FText NodeTooltip; FText NodeTooltip;
}; };
//
// This derives from FormatMessage.
//
UCLASS(MinimalAPI)
class UK2Node_FormatErrorMessage : public UK2Node_FormatMessage
{
GENERATED_UCLASS_BODY()
// Setting this flag alters the behavior of FormatMessage, making it
// output to the log instead of to a pin.
//
virtual bool IsFormatErrorMessage() const override { return true; }
};