// Portage UE 5.5 - voir LPCheckMeshAssignmentCommandlet.h #include "LPCheckMeshAssignmentCommandlet.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 "UObject/UObjectGlobals.h" DEFINE_LOG_CATEGORY_STATIC(LogLPCheckMesh, Log, All); namespace { /** Noeuds qui nous interessent : tout ce qui lit ou ecrit le maillage d'un composant. */ bool IsMeshFunction(FName Name) { const FString S = Name.ToString(); return S.Contains(TEXT("SkinnedAsset")) || S.Contains(TEXT("SkeletalMesh")) || S.Contains(TEXT("PoseComponent")); // Set(Master|Leader)PoseComponent : renomme en 5.1 } FString DescribePin(const UEdGraphPin* Pin) { if (!Pin) { return TEXT("(absente)"); } const FString Type = Pin->PinType.PinSubCategoryObject.IsValid() ? Pin->PinType.PinSubCategoryObject->GetName() : Pin->PinType.PinCategory.ToString(); if (Pin->LinkedTo.Num() == 0) { return FString::Printf(TEXT("[%s] NON CONNECTEE (defaut='%s')"), *Type, *Pin->DefaultValue); } TArray Sources; for (const UEdGraphPin* Linked : Pin->LinkedTo) { Sources.Add(FString::Printf(TEXT("%s.%s"), Linked->GetOwningNode() ? *Linked->GetOwningNode()->GetName() : TEXT("?"), *Linked->PinName.ToString())); } return FString::Printf(TEXT("[%s] <- %s"), *Type, *FString::Join(Sources, TEXT(", "))); } } ULPCheckMeshAssignmentCommandlet::ULPCheckMeshAssignmentCommandlet() { IsClient = false; IsServer = false; IsEditor = true; LogToConsole = true; } int32 ULPCheckMeshAssignmentCommandlet::Main(const FString& Params) { UE_LOG(LogLPCheckMesh, Warning, TEXT("=== Broches orphelines (supprimees par une migration) ===")); FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); AssetRegistryModule.Get().SearchAllAssets(true); TArray BlueprintAssets; AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true); int32 Total = 0; int32 Disconnected = 0; for (const FAssetData& AssetData : BlueprintAssets) { if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/"))) { continue; } UBlueprint* Blueprint = Cast(AssetData.GetAsset()); if (!Blueprint) { continue; } TArray Graphs; Blueprint->GetAllGraphs(Graphs); for (UEdGraph* Graph : Graphs) { if (!Graph) { continue; } for (UEdGraphNode* GraphNode : Graph->Nodes) { // Balayage general : toute broche que le moteur a marquee orpheline, quel que // soit le noeud. C'est la trace exacte d'une broche supprimee par une montee // de version alors qu'elle portait encore des liaisons ou une valeur. // Nom de la fonction appelee, quand il y en a une : c'est lui qui dit // quelle API a perdu sa broche. FString Callee; if (const UK2Node_CallFunction* Fn = Cast(GraphNode)) { Callee = FString::Printf(TEXT(" [%s]"), *Fn->FunctionReference.GetMemberName().ToString()); } // Un noeud ne nous interesse que s'il porte au moins une broche orpheline. bool bHasOrphan = false; for (const UEdGraphPin* Pin : GraphNode->Pins) { if (Pin && Pin->bOrphanedPin) { bHasOrphan = true; break; } } if (!bHasOrphan) { continue; } UE_LOG(LogLPCheckMesh, Warning, TEXT(" %s :: %s -> %s%s"), *Blueprint->GetName(), *Graph->GetName(), *GraphNode->GetName(), *Callee); // On dumpe TOUTES les broches : l'orpheline porte l'ancienne valeur, et la // broche de remplacement dit si la migration l'a reprise ou laissee vide. for (UEdGraphPin* Pin : GraphNode->Pins) { if (!Pin || Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) { continue; } const bool bOrphan = Pin->bOrphanedPin; if (bOrphan) { ++Total; if (Pin->LinkedTo.Num() > 0) { ++Disconnected; } } UE_LOG(LogLPCheckMesh, Warning, TEXT(" %-22s %-3s %s"), *Pin->PinName.ToString(), bOrphan ? TEXT("[X]") : TEXT(" "), *DescribePin(Pin)); } } } } UE_LOG(LogLPCheckMesh, Warning, TEXT("=== Termine : %d broche(s) orpheline(s), dont %d portant encore des liaisons. ==="), Total, Disconnected); return 0; }