Better graph dump that shows exec pin names so that the AI knows how to connect them.

This commit is contained in:
2026-04-09 16:44:30 -04:00
parent 3b6207f7a1
commit 88b72712ca
2 changed files with 136 additions and 97 deletions

View File

@@ -38,6 +38,13 @@ WingGraphExport::WingGraphExport(UEdGraphNode* InNode, bool Locals, bool Details
// //
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
bool WingGraphExport::IsLinked(UEdGraphPin* Pin)
{
if (Pin == nullptr) return false;
if (Pin->LinkedTo.IsEmpty()) return false;
return true;
}
UEdGraphPin* WingGraphExport::GetLinkedTo(UEdGraphPin* Pin) UEdGraphPin* WingGraphExport::GetLinkedTo(UEdGraphPin* Pin)
{ {
if (Pin == nullptr) return nullptr; if (Pin == nullptr) return nullptr;
@@ -64,13 +71,16 @@ bool WingGraphExport::IsDefaultToSelf(UEdGraphPin* Pin)
return Pin->PinName.ToString() == DefaultToSelfPinName; return Pin->PinName.ToString() == DefaultToSelfPinName;
} }
TArray<UEdGraphPin*> WingGraphExport::FilterPins(UEdGraphNode* Node, EEdGraphPinDirection Direction, FName Category) TArray<UEdGraphPin*> WingGraphExport::GetPins(
UEdGraphNode* Node, EEdGraphPinDirection Direction, PinKind Kind)
{ {
TArray<UEdGraphPin*> Result; TArray<UEdGraphPin*> Result;
for (UEdGraphPin* Pin : Node->Pins) for (UEdGraphPin* Pin : Node->Pins)
{ {
if (Direction != EGPD_MAX && Pin->Direction != Direction) continue; if (Pin->bHidden) continue;
if (!Category.IsNone() && Pin->PinType.PinCategory != Category) continue; if (Pin->Direction != Direction) continue;
if ((Kind == PK_VALUE) && (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)) continue;
if ((Kind == PK_EXEC) && (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec)) continue;
Result.Add(Pin); Result.Add(Pin);
} }
return Result; return Result;
@@ -78,78 +88,23 @@ TArray<UEdGraphPin*> WingGraphExport::FilterPins(UEdGraphNode* Node, EEdGraphPin
bool WingGraphExport::HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction) bool WingGraphExport::HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
{ {
return FilterPins(Node, Direction, UEdGraphSchema_K2::PC_Exec).Num() > 0; return GetPins(Node, Direction, PK_EXEC).Num() > 0;
}
UEdGraphPin* WingGraphExport::FindFirstPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
{
for (UEdGraphPin* Pin : Node->Pins)
{
if (Pin->Direction == Direction) return Pin;
}
return nullptr;
} }
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// //
// Traverse and Emit the Nodes. // Traversal
// //
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
FString WingGraphExport::FormatPinSource(UEdGraphPin* Pin)
{
// If connected, show source node.pin
UEdGraphPin* LinkedTo = GetLinkedTo(Pin);
if (LinkedTo != nullptr)
{
UEdGraphNode* LinkedToNode = LinkedTo->GetOwningNode();
FString PinLabel = WingUtils::FormatName(LinkedTo);
return FString::Printf(TEXT("%s.%s"), *WingUtils::FormatName(LinkedToNode), *PinLabel);
}
// String pins: always show in quotes (even if empty).
FName Category = Pin->PinType.PinCategory;
if (Category == UEdGraphSchema_K2::PC_String ||
Category == UEdGraphSchema_K2::PC_Name ||
Category == UEdGraphSchema_K2::PC_Text)
{
return FString::Printf(TEXT("\"%s\""), *Pin->DefaultValue);
}
// If has a non-empty default, show it.
if (!Pin->DefaultValue.IsEmpty())
{
return Pin->DefaultValue;
}
if (Pin->DefaultObject)
{
return Pin->DefaultObject->GetName();
}
if (!Pin->DefaultTextValue.IsEmpty())
{
return FString::Printf(TEXT("\"%s\""), *Pin->DefaultTextValue.ToString());
}
if (IsDefaultToSelf(Pin))
{
return TEXT("<self>");
}
else
{
return TEXT("<default>");
}
}
void WingGraphExport::Traverse(UEdGraphNode* Node) void WingGraphExport::Traverse(UEdGraphNode* Node)
{ {
if (Visited.Contains(Node)) return; if (Visited.Contains(Node)) return;
Visited.Add(Node); Visited.Add(Node);
// First, traverse input nodes // First, traverse input nodes
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input)) for (UEdGraphPin* Pin : GetPins(Node, EGPD_Input, PK_BOTH))
for (UEdGraphPin* LinkedPin : Pin->LinkedTo) for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
Traverse(LinkedPin->GetOwningNode()); Traverse(LinkedPin->GetOwningNode());
@@ -159,7 +114,7 @@ void WingGraphExport::Traverse(UEdGraphNode* Node)
// Then, traverse exec output nodes only. // Then, traverse exec output nodes only.
// Data outputs are not followed — data nodes get pulled in // Data outputs are not followed — data nodes get pulled in
// through their consumers' input traversal. // through their consumers' input traversal.
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Output, UEdGraphSchema_K2::PC_Exec)) for (UEdGraphPin* Pin : GetPins(Node, EGPD_Output, PK_EXEC))
for (UEdGraphPin* LinkedPin : Pin->LinkedTo) for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
Traverse(LinkedPin->GetOwningNode()); Traverse(LinkedPin->GetOwningNode());
} }
@@ -195,6 +150,62 @@ void WingGraphExport::SortNodes()
} }
} }
////////////////////////////////////////////////////////
//
// Text Output
//
////////////////////////////////////////////////////////
void WingGraphExport::EmitLinks(UEdGraphPin* Pin)
{
bool comma = false;
for (const UEdGraphPin *Other : Pin->LinkedTo)
{
if (comma) Output.Append(TEXT(", "));
comma = true;
Output.Appendf(TEXT("node:%s,pin:%s"),
*WingUtils::FormatName(Other->GetOwningNode()), *WingUtils::FormatName(Other));
}
}
void WingGraphExport::EmitInputPinValue(UEdGraphPin* Pin)
{
// If connected, show source node and pin
if (IsLinked(Pin)) return EmitLinks(Pin);
// String pins: always show in quotes (even if empty).
FName Category = Pin->PinType.PinCategory;
if (Category == UEdGraphSchema_K2::PC_String ||
Category == UEdGraphSchema_K2::PC_Name ||
Category == UEdGraphSchema_K2::PC_Text)
{
Output.Appendf(TEXT("\"%s\""), *Pin->DefaultValue);
return;
}
// If has a non-empty default, show it.
if (!Pin->DefaultValue.IsEmpty())
{
Output.Append(Pin->DefaultValue);
return;
}
if (Pin->DefaultObject)
{
Output.Append(Pin->DefaultObject->GetName());
return;
}
if (!Pin->DefaultTextValue.IsEmpty())
{
Output.Appendf(TEXT("\"%s\""), *Pin->DefaultTextValue.ToString());
return;
}
Output.Appendf(TEXT("<default>"));
}
void WingGraphExport::EmitNode(UEdGraphNode* Node) void WingGraphExport::EmitNode(UEdGraphNode* Node)
{ {
if (Node->IsA<UEdGraphNode_Comment>()) return; if (Node->IsA<UEdGraphNode_Comment>()) return;
@@ -218,45 +229,65 @@ void WingGraphExport::EmitNode(UEdGraphNode* Node)
SuppressedDetails = true; SuppressedDetails = true;
} }
// Emit the input exec pin(s). Do not show what they're connected to.
TArray<UEdGraphPin*> ExecIns = GetPins(Node, EGPD_Input, PK_EXEC);
for (UEdGraphPin* Pin : ExecIns)
{
if (IsLinked(Pin))
{
Output.Appendf(TEXT(" exec-in %s from "), *WingUtils::FormatName(Pin));
EmitLinks(Pin);
Output.AppendChar('\n');
}
else
{
Output.Appendf(TEXT(" exec-in %s (unconnected)\n"), *WingUtils::FormatName(Pin));
}
}
// Emit input data pins. // Emit input data pins.
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input)) for (UEdGraphPin* Pin : GetPins(Node, EGPD_Input, PK_VALUE))
{ {
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue; if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue;
if (Pin->bHidden) continue;
Output.Appendf(TEXT(" input %s %s = %s\n"), Output.Appendf(TEXT(" input-pin %s %s = "),
*UWingTypes::TypeToText(Pin->PinType), *UWingTypes::TypeToText(Pin->PinType),
*WingUtils::FormatName(Pin), *WingUtils::FormatName(Pin));
*FormatPinSource(Pin)); EmitInputPinValue(Pin);
Output.AppendChar('\n');
} }
// Emit output data pins as a return line. // Emit output data pins as a single 'output-pins' line.
FString ReturnPins; TArray<UEdGraphPin*> OutputPins = GetPins(Node, EGPD_Output, PK_VALUE);
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Output)) if (!OutputPins.IsEmpty())
{ {
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue; Output.Append(TEXT(" output-pins "));
if (Pin->bHidden) continue; bool comma = false;
if (!ReturnPins.IsEmpty()) ReturnPins += TEXT(", "); for (UEdGraphPin *Pin : OutputPins)
ReturnPins += WingUtils::FormatName(Pin); {
if (comma) Output.Append(TEXT(", "));
comma = true;
Output.Append(UWingTypes::TypeToText(Pin->PinType));
Output.AppendChar(' ');
Output.Append(WingUtils::FormatName(Pin));
} }
if (!ReturnPins.IsEmpty()) Output.AppendChar('\n');
{
Output.Appendf(TEXT(" output-pins %s\n"), *ReturnPins);
} }
// Emit output exec pins as goto statements. // Emit output exec pins.
TArray<UEdGraphPin*> ExecOuts = FilterPins(Node, EGPD_Output, UEdGraphSchema_K2::PC_Exec); TArray<UEdGraphPin*> ExecOuts = GetPins(Node, EGPD_Output, PK_EXEC);
for (UEdGraphPin* Pin : ExecOuts) for (UEdGraphPin* Pin : ExecOuts)
{ {
UEdGraphPin* LinkedTo = GetLinkedTo(Pin); if (IsLinked(Pin))
if (!LinkedTo) continue; {
Output.Appendf(TEXT(" exec-out %s to "), *WingUtils::FormatName(Pin));
FString Target = WingUtils::FormatName(LinkedTo->GetOwningNode()); EmitLinks(Pin);
Output.AppendChar('\n');
if (ExecOuts.Num() == 1) }
Output.Appendf(TEXT(" goto %s\n"), *Target);
else else
Output.Appendf(TEXT(" goto-if %s %s\n"), *WingUtils::FormatName(Pin), *Target); {
Output.Appendf(TEXT(" exec-out %s (unconnected)\n"), *WingUtils::FormatName(Pin));
}
} }
Output.Append(TEXT("\n")); Output.Append(TEXT("\n"));

View File

@@ -22,6 +22,17 @@ private:
// //
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
enum PinKind
{
PK_EXEC,
PK_VALUE,
PK_BOTH
};
// Return true if this pin is connected.
//
static bool IsLinked(UEdGraphPin *Pin);
// Get the pin that this pin is linked to. // Get the pin that this pin is linked to.
// //
static UEdGraphPin* GetLinkedTo(UEdGraphPin *Pin); static UEdGraphPin* GetLinkedTo(UEdGraphPin *Pin);
@@ -32,27 +43,24 @@ private:
static bool IsDefaultToSelf(UEdGraphPin* Pin); static bool IsDefaultToSelf(UEdGraphPin* Pin);
// Get a subset of the pins in the node, filtered // Get a subset of the pins in the node, filtered
// by direction, category, or both. // by direction, Kind, or both.
// //
static TArray<UEdGraphPin*> FilterPins(UEdGraphNode* Node, static TArray<UEdGraphPin*> GetPins(
EEdGraphPinDirection Direction = EGPD_MAX, FName Category = FName()); UEdGraphNode* Node, EEdGraphPinDirection Direction, PinKind Kind);
// Return true if the node has an exec pin that points // Return true if the node has an exec pin that points
// in the specified direction. // in the specified direction.
// //
static bool HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction); static bool HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction);
// Find the first pin that points in the specified direction.
//
static UEdGraphPin* FindFirstPin(UEdGraphNode* Node, EEdGraphPinDirection Direction);
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// //
// Traverse and Emit the Nodes. // Traverse and Emit the Nodes.
// //
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
FString FormatPinSource(UEdGraphPin* Pin); void EmitLinks(UEdGraphPin* Pin);
void EmitInputPinValue(UEdGraphPin* Pin);
void Traverse(UEdGraphNode* Node); void Traverse(UEdGraphNode* Node);
void SortNodes(); void SortNodes();
void EmitNode(UEdGraphNode* Node); void EmitNode(UEdGraphNode* Node);