More work on Blueprint_Dump
This commit is contained in:
258
Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h
Normal file
258
Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h
Normal file
@@ -0,0 +1,258 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Animation/AnimBlueprint.h"
|
||||
#include "Animation/Skeleton.h"
|
||||
#include "Engine/SimpleConstructionScript.h"
|
||||
#include "Engine/SCS_Node.h"
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "WingFunctionArgs.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "AnimationGraph.h"
|
||||
#include "AnimationGraphSchema.h"
|
||||
#include "AnimationStateMachineSchema.h"
|
||||
#include "Blueprint_Dump.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Blueprint_Dump : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint path"))
|
||||
FString Blueprint;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Dump a Blueprint's structure: variables, interfaces, components, "
|
||||
"and graph names. Does not include graph contents (use Graph_Dump for that).");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
// Header
|
||||
UWingServer::Printf(TEXT("Blueprint: %s\n"), *WingUtils::FormatName(BP));
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *WingUtils::FormatName(BP->ParentClass) : TEXT("None"));
|
||||
UWingServer::Printf(TEXT("Type: %s\n"), *WingUtils::EnumToString(BP->BlueprintType));
|
||||
|
||||
// Animation Blueprint
|
||||
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
|
||||
{
|
||||
if (AnimBP->TargetSkeleton)
|
||||
UWingServer::Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName());
|
||||
}
|
||||
|
||||
// Interfaces
|
||||
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
|
||||
{
|
||||
if (I.Interface)
|
||||
UWingServer::Printf(TEXT("Interface: %s\n"), *WingUtils::FormatName(I.Interface));
|
||||
}
|
||||
|
||||
// Variables
|
||||
if (!BP->NewVariables.IsEmpty())
|
||||
{
|
||||
UWingServer::Print(TEXT("\nVariables:\n"));
|
||||
for (const FBPVariableDescription& V : BP->NewVariables)
|
||||
{
|
||||
UWingServer::Printf(TEXT(" %s %s"),
|
||||
*UWingTypes::TypeToText(V.VarType),
|
||||
*WingUtils::FormatName(V));
|
||||
if (!V.DefaultValue.IsEmpty())
|
||||
UWingServer::Printf(TEXT(" = %s"), *V.DefaultValue);
|
||||
if (!V.Category.IsEmpty() && V.Category.ToString() != TEXT("Default"))
|
||||
UWingServer::Printf(TEXT(" [%s]"), *V.Category.ToString());
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Components
|
||||
bool bHasAny = false;
|
||||
if (UClass* GenClass = BP->GeneratedClass)
|
||||
{
|
||||
// Native components first.
|
||||
if (AActor* CDO = Cast<AActor>(GenClass->GetDefaultObject()))
|
||||
{
|
||||
TArray<UActorComponent*> NativeComponents;
|
||||
CDO->GetComponents(NativeComponents);
|
||||
for (UActorComponent* Comp : NativeComponents)
|
||||
{
|
||||
if (!bHasAny) { UWingServer::Print(TEXT("\nComponents:\n")); bHasAny = true; }
|
||||
PrintComponent(Comp, TEXT("native"));
|
||||
}
|
||||
}
|
||||
|
||||
// Blueprint SCS components (this blueprint and parents)
|
||||
TArray<UBlueprint*> Hierarchy;
|
||||
UBlueprint::GetBlueprintHierarchyFromClass(GenClass, Hierarchy);
|
||||
for (int32 i = Hierarchy.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
UBlueprint* WalkBP = Hierarchy[i];
|
||||
if (!WalkBP->SimpleConstructionScript) continue;
|
||||
const TCHAR* Annotation = (WalkBP == BP) ? nullptr : TEXT("inherited");
|
||||
for (USCS_Node* Node : WalkBP->SimpleConstructionScript->GetAllNodes())
|
||||
{
|
||||
if (!bHasAny) { UWingServer::Print(TEXT("\nComponents:\n")); bHasAny = true; }
|
||||
PrintComponent(Node, Annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event Dispatchers
|
||||
if (!BP->DelegateSignatureGraphs.IsEmpty())
|
||||
{
|
||||
UWingServer::Print(TEXT("\nEvent Dispatchers:\n"));
|
||||
for (UEdGraph* Graph : BP->DelegateSignatureGraphs)
|
||||
PrintEventDispatcher(Graph);
|
||||
}
|
||||
|
||||
// Graphs
|
||||
TSet<UEdGraph*> Printed;
|
||||
|
||||
UWingServer::Print(TEXT("\nGraphs:\n"));
|
||||
for (UEdGraph* Graph : BP->UbergraphPages)
|
||||
{
|
||||
UWingServer::Printf(TEXT(" EventGraph %s\n"), *WingUtils::FormatName(Graph));
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
for (UEdGraph* Graph : BP->FunctionGraphs)
|
||||
{
|
||||
if (Graph->IsA<UAnimationGraph>()) continue;
|
||||
PrintGraph(Graph, TEXT("Function"));
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
for (UEdGraph* Graph : BP->MacroGraphs)
|
||||
{
|
||||
PrintGraph(Graph, TEXT("Macro"));
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
|
||||
for (UEdGraph* Graph : I.Graphs)
|
||||
{
|
||||
PrintGraph(Graph, TEXT("Interface"), I.Interface);
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
|
||||
// Delegate signature graphs are not real graphs, but mark them as printed.
|
||||
for (UEdGraph* Graph : BP->DelegateSignatureGraphs)
|
||||
Printed.Add(Graph);
|
||||
|
||||
// Animation graphs and state machines.
|
||||
TArray<UEdGraph*> AllGraphs = WingUtils::AllGraphs(BP);
|
||||
|
||||
FString AnimGraphNames;
|
||||
for (UEdGraph* Graph : AllGraphs)
|
||||
{
|
||||
if (Printed.Contains(Graph)) continue;
|
||||
if (!Graph->Schema || !Graph->Schema->IsChildOf(UAnimationGraphSchema::StaticClass())) continue;
|
||||
if (!AnimGraphNames.IsEmpty()) AnimGraphNames += TEXT(", ");
|
||||
AnimGraphNames += WingUtils::FormatName(Graph);
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
if (!AnimGraphNames.IsEmpty())
|
||||
UWingServer::Printf(TEXT("\nAnimation Graphs: %s\n"), *AnimGraphNames);
|
||||
|
||||
FString StateMachineNames;
|
||||
for (UEdGraph* Graph : AllGraphs)
|
||||
{
|
||||
if (Printed.Contains(Graph)) continue;
|
||||
if (!Graph->Schema || !Graph->Schema->IsChildOf(UAnimationStateMachineSchema::StaticClass())) continue;
|
||||
if (!StateMachineNames.IsEmpty()) StateMachineNames += TEXT(", ");
|
||||
StateMachineNames += WingUtils::FormatName(Graph);
|
||||
Printed.Add(Graph);
|
||||
}
|
||||
if (!StateMachineNames.IsEmpty())
|
||||
UWingServer::Printf(TEXT("\nAnimation State Machines: %s\n"), *StateMachineNames);
|
||||
|
||||
// Catch any graphs we missed.
|
||||
for (UEdGraph* Graph : AllGraphs)
|
||||
{
|
||||
if (Printed.Contains(Graph)) continue;
|
||||
UWingServer::Printf(TEXT("WARNING: unlisted graph: %s (%s)\n"),
|
||||
*WingUtils::FormatName(Graph),
|
||||
*WingUtils::FormatName(Graph->GetSchema()->GetClass()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void PrintEventDispatcher(UEdGraph* Graph)
|
||||
{
|
||||
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
|
||||
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
|
||||
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
|
||||
|
||||
FString Args;
|
||||
if (EntryNode.IsValid() && WingFunctionArgs::HasArgs(EntryNode.Get()))
|
||||
Args = WingFunctionArgs::GetArgs(EntryNode.Get());
|
||||
|
||||
UWingServer::Printf(TEXT(" %s(%s)\n"), *WingUtils::FormatName(Graph), *Args);
|
||||
}
|
||||
|
||||
void PrintComponent(USCS_Node* Node, const TCHAR* Annotation = nullptr)
|
||||
{
|
||||
FString ClassName = Node->ComponentClass
|
||||
? WingUtils::FormatName(Node->ComponentClass)
|
||||
: TEXT("None");
|
||||
UWingServer::Printf(TEXT(" %s %s"),
|
||||
*ClassName,
|
||||
*WingUtils::FormatName(Node));
|
||||
if (Node->ParentComponentOrVariableName != NAME_None)
|
||||
UWingServer::Printf(TEXT(" [parent: %s]"), *Node->ParentComponentOrVariableName.ToString());
|
||||
if (Annotation)
|
||||
UWingServer::Printf(TEXT(" [%s]"), Annotation);
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
void PrintComponent(UActorComponent* Comp, const TCHAR* Annotation = nullptr)
|
||||
{
|
||||
UWingServer::Printf(TEXT(" %s %s"),
|
||||
*WingUtils::FormatName(Comp->GetClass()),
|
||||
*Comp->GetName());
|
||||
if (USceneComponent* Scene = Cast<USceneComponent>(Comp))
|
||||
if (USceneComponent* Parent = Scene->GetAttachParent())
|
||||
UWingServer::Printf(TEXT(" [parent: %s]"), *Parent->GetName());
|
||||
if (Annotation)
|
||||
UWingServer::Printf(TEXT(" [%s]"), Annotation);
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr)
|
||||
{
|
||||
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
|
||||
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
|
||||
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
|
||||
|
||||
FString InputArgs;
|
||||
FString OutputArgs;
|
||||
if (EntryNode.IsValid() && WingFunctionArgs::HasArgs(EntryNode.Get()))
|
||||
InputArgs = WingFunctionArgs::GetArgs(EntryNode.Get());
|
||||
if (ResultNode.IsValid() && WingFunctionArgs::HasArgs(ResultNode.Get()))
|
||||
OutputArgs = WingFunctionArgs::GetArgs(ResultNode.Get());
|
||||
|
||||
UWingServer::Printf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph));
|
||||
if (!InputArgs.IsEmpty())
|
||||
UWingServer::Printf(TEXT("(%s)"), *InputArgs);
|
||||
if (!OutputArgs.IsEmpty())
|
||||
UWingServer::Printf(TEXT(" -> (%s)"), *OutputArgs);
|
||||
if (Interface)
|
||||
UWingServer::Printf(TEXT(" [%s]"), *WingUtils::FormatName(Interface));
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
}
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user