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)
{
if (Pin == nullptr) return nullptr;
@@ -64,13 +71,16 @@ bool WingGraphExport::IsDefaultToSelf(UEdGraphPin* Pin)
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;
for (UEdGraphPin* Pin : Node->Pins)
{
if (Direction != EGPD_MAX && Pin->Direction != Direction) continue;
if (!Category.IsNone() && Pin->PinType.PinCategory != Category) continue;
if (Pin->bHidden) 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);
}
return Result;
@@ -78,78 +88,23 @@ TArray<UEdGraphPin*> WingGraphExport::FilterPins(UEdGraphNode* Node, EEdGraphPin
bool WingGraphExport::HasExecPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
{
return FilterPins(Node, Direction, UEdGraphSchema_K2::PC_Exec).Num() > 0;
}
UEdGraphPin* WingGraphExport::FindFirstPin(UEdGraphNode* Node, EEdGraphPinDirection Direction)
{
for (UEdGraphPin* Pin : Node->Pins)
{
if (Pin->Direction == Direction) return Pin;
}
return nullptr;
return GetPins(Node, Direction, PK_EXEC).Num() > 0;
}
////////////////////////////////////////////////////////
//
// 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)
{
if (Visited.Contains(Node)) return;
Visited.Add(Node);
// 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)
Traverse(LinkedPin->GetOwningNode());
@@ -159,7 +114,7 @@ void WingGraphExport::Traverse(UEdGraphNode* Node)
// Then, traverse exec output nodes only.
// Data outputs are not followed — data nodes get pulled in
// 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)
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)
{
if (Node->IsA<UEdGraphNode_Comment>()) return;
@@ -218,45 +229,65 @@ void WingGraphExport::EmitNode(UEdGraphNode* Node)
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.
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->bHidden) continue;
Output.Appendf(TEXT(" input %s %s = %s\n"),
Output.Appendf(TEXT(" input-pin %s %s = "),
*UWingTypes::TypeToText(Pin->PinType),
*WingUtils::FormatName(Pin),
*FormatPinSource(Pin));
*WingUtils::FormatName(Pin));
EmitInputPinValue(Pin);
Output.AppendChar('\n');
}
// Emit output data pins as a return line.
FString ReturnPins;
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Output))
// Emit output data pins as a single 'output-pins' line.
TArray<UEdGraphPin*> OutputPins = GetPins(Node, EGPD_Output, PK_VALUE);
if (!OutputPins.IsEmpty())
{
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) continue;
if (Pin->bHidden) continue;
if (!ReturnPins.IsEmpty()) ReturnPins += TEXT(", ");
ReturnPins += WingUtils::FormatName(Pin);
Output.Append(TEXT(" output-pins "));
bool comma = false;
for (UEdGraphPin *Pin : OutputPins)
{
if (comma) Output.Append(TEXT(", "));
comma = true;
Output.Append(UWingTypes::TypeToText(Pin->PinType));
Output.AppendChar(' ');
Output.Append(WingUtils::FormatName(Pin));
}
if (!ReturnPins.IsEmpty())
{
Output.Appendf(TEXT(" output-pins %s\n"), *ReturnPins);
Output.AppendChar('\n');
}
// Emit output exec pins as goto statements.
TArray<UEdGraphPin*> ExecOuts = FilterPins(Node, EGPD_Output, UEdGraphSchema_K2::PC_Exec);
// Emit output exec pins.
TArray<UEdGraphPin*> ExecOuts = GetPins(Node, EGPD_Output, PK_EXEC);
for (UEdGraphPin* Pin : ExecOuts)
{
UEdGraphPin* LinkedTo = GetLinkedTo(Pin);
if (!LinkedTo) continue;
FString Target = WingUtils::FormatName(LinkedTo->GetOwningNode());
if (ExecOuts.Num() == 1)
Output.Appendf(TEXT(" goto %s\n"), *Target);
if (IsLinked(Pin))
{
Output.Appendf(TEXT(" exec-out %s to "), *WingUtils::FormatName(Pin));
EmitLinks(Pin);
Output.AppendChar('\n');
}
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"));

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.
//
static UEdGraphPin* GetLinkedTo(UEdGraphPin *Pin);
@@ -32,27 +43,24 @@ private:
static bool IsDefaultToSelf(UEdGraphPin* Pin);
// 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,
EEdGraphPinDirection Direction = EGPD_MAX, FName Category = FName());
static TArray<UEdGraphPin*> GetPins(
UEdGraphNode* Node, EEdGraphPinDirection Direction, PinKind Kind);
// Return true if the node has an exec pin that points
// in the specified 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.
//
////////////////////////////////////////////////////////
FString FormatPinSource(UEdGraphPin* Pin);
void EmitLinks(UEdGraphPin* Pin);
void EmitInputPinValue(UEdGraphPin* Pin);
void Traverse(UEdGraphNode* Node);
void SortNodes();
void EmitNode(UEdGraphNode* Node);