LifePlay-Revival/Source/LifePlayEditorTools/LPDumpFunctionCommandlet.cpp
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

198 lines
5.7 KiB
C++

// Voir LPDumpFunctionCommandlet.h
#include "LPDumpFunctionCommandlet.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "Engine/Blueprint.h"
#include "K2Node_CallFunction.h"
#include "K2Node_CustomEvent.h"
#include "K2Node_Event.h"
#include "K2Node_MacroInstance.h"
#include "K2Node_Variable.h"
#include "UObject/UObjectGlobals.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPDump, Log, All);
namespace
{
/** Ce que fait le noeud, en une ligne : appel de fonction, acces variable, ou sa classe. */
FString DescribeNode(const UEdGraphNode* Node)
{
if (const UK2Node_CallFunction* Call = Cast<UK2Node_CallFunction>(Node))
{
const UClass* Owner = Call->FunctionReference.GetMemberParentClass();
return FString::Printf(TEXT("appel %s::%s()"),
Owner ? *Owner->GetName() : TEXT("?"),
*Call->FunctionReference.GetMemberName().ToString());
}
if (const UK2Node_Variable* Var = Cast<UK2Node_Variable>(Node))
{
return FString::Printf(TEXT("variable '%s'"), *Var->VariableReference.GetMemberName().ToString());
}
// STB-T0 : nommer les evenements et les macros, sans quoi un noeud d'un EventGraph ne se rattache
// pas a l'evenement qui l'execute (UK2Node_CustomEvent derive de UK2Node_Event : tester d'abord).
if (const UK2Node_CustomEvent* Custom = Cast<UK2Node_CustomEvent>(Node))
{
return FString::Printf(TEXT("evenement personnalise '%s'"), *Custom->CustomFunctionName.ToString());
}
if (const UK2Node_Event* Event = Cast<UK2Node_Event>(Node))
{
return FString::Printf(TEXT("evenement '%s'"), *Event->GetFunctionName().ToString());
}
if (const UK2Node_MacroInstance* Macro = Cast<UK2Node_MacroInstance>(Node))
{
const UEdGraph* MacroGraph = Macro->GetMacroGraph();
return FString::Printf(TEXT("macro '%s'"), MacroGraph ? *MacroGraph->GetName() : TEXT("?"));
}
return Node->GetClass()->GetName();
}
FString DescribePin(const UEdGraphPin* Pin)
{
if (Pin->LinkedTo.Num() == 0)
{
return Pin->DefaultValue.IsEmpty()
? FString(TEXT("(vide)"))
: FString::Printf(TEXT("= '%s'"), *Pin->DefaultValue);
}
TArray<FString> Ends;
for (const UEdGraphPin* L : Pin->LinkedTo)
{
Ends.Add(FString::Printf(TEXT("%s.%s"),
L->GetOwningNode() ? *L->GetOwningNode()->GetName() : TEXT("?"),
*L->PinName.ToString()));
}
return FString::Printf(TEXT("-> %s"), *FString::Join(Ends, TEXT(", ")));
}
}
ULPDumpFunctionCommandlet::ULPDumpFunctionCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPDumpFunctionCommandlet::Main(const FString& Params)
{
TArray<FString> Tokens, Switches;
TMap<FString, FString> ParamsMap;
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
const FString WantedBP = ParamsMap.FindRef(TEXT("BP"));
const FString WantedFn = ParamsMap.FindRef(TEXT("Fn"));
const bool bShowPins = Switches.Contains(TEXT("Pins"));
// -Exec : inclut les broches d'execution (flux de controle). Absent par defaut pour que
// les photographies anterieures restent comparables ligne a ligne.
const bool bShowExec = Switches.Contains(TEXT("Exec"));
if (WantedBP.IsEmpty())
{
UE_LOG(LogLPDump, Error, TEXT("Usage : -BP=<Blueprint> [-Fn=<Fonction>] [-Pins]"));
return 1;
}
FAssetRegistryModule& AssetRegistryModule =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
TArray<FAssetData> BlueprintAssets;
#if ENGINE_MAJOR_VERSION >= 5
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true);
#else
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetFName(), BlueprintAssets, true);
#endif
UBlueprint* Target = nullptr;
for (const FAssetData& AssetData : BlueprintAssets)
{
if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/")))
{
continue;
}
if (AssetData.AssetName.ToString() == WantedBP)
{
Target = Cast<UBlueprint>(AssetData.GetAsset());
break;
}
}
if (!Target)
{
UE_LOG(LogLPDump, Error, TEXT("Blueprint '%s' introuvable sous /Game."), *WantedBP);
return 1;
}
TArray<UEdGraph*> Graphs;
Target->GetAllGraphs(Graphs);
// Sans -Fn : on se contente de lister les graphes disponibles.
if (WantedFn.IsEmpty())
{
UE_LOG(LogLPDump, Warning, TEXT("=== %s : %d graphe(s) ==="), *WantedBP, Graphs.Num());
for (const UEdGraph* Graph : Graphs)
{
if (Graph)
{
UE_LOG(LogLPDump, Warning, TEXT(" %s (%d noeuds)"), *Graph->GetName(), Graph->Nodes.Num());
}
}
return 0;
}
// -Fn=* : balayage complet, pour diffuser deux versions du meme Blueprint et
// laisser un diff textuel pointer les ecarts.
const bool bAll = (WantedFn == TEXT("*"));
int32 Dumped = 0;
for (UEdGraph* Graph : Graphs)
{
if (!Graph || (!bAll && Graph->GetName() != WantedFn))
{
continue;
}
++Dumped;
UE_LOG(LogLPDump, Warning, TEXT("=== %s :: %s (%d noeuds) ==="),
*WantedBP, *Graph->GetName(), Graph->Nodes.Num());
for (UEdGraphNode* Node : Graph->Nodes)
{
if (!Node)
{
continue;
}
UE_LOG(LogLPDump, Warning, TEXT(" %-34s %s"), *Node->GetName(), *DescribeNode(Node));
if (!bShowPins)
{
continue;
}
for (const UEdGraphPin* Pin : Node->Pins)
{
if (!Pin || (!bShowExec && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec))
{
continue;
}
UE_LOG(LogLPDump, Warning, TEXT(" %s%-22s %s"),
Pin->bOrphanedPin ? TEXT("[X] ") : TEXT(" "),
*Pin->PinName.ToString(), *DescribePin(Pin));
}
}
}
if (Dumped == 0)
{
UE_LOG(LogLPDump, Error, TEXT("Fonction '%s' introuvable dans %s. Relancer sans -Fn pour lister."),
*WantedFn, *WantedBP);
return 1;
}
return 0;
}