// 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_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(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(Node)) { return FString::Printf(TEXT("variable '%s'"), *Var->VariableReference.GetMemberName().ToString()); } 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 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 Tokens, Switches; TMap 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")); if (WantedBP.IsEmpty()) { UE_LOG(LogLPDump, Error, TEXT("Usage : -BP= [-Fn=] [-Pins]")); return 1; } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); AssetRegistryModule.Get().SearchAllAssets(true); TArray 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(AssetData.GetAsset()); break; } } if (!Target) { UE_LOG(LogLPDump, Error, TEXT("Blueprint '%s' introuvable sous /Game."), *WantedBP); return 1; } TArray 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 || 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; }