#include "LPFixFindCenterCommandlet.h" #include "AssetRegistry/AssetRegistryModule.h" #include "EdGraph/EdGraph.h" #include "EdGraphSchema_K2.h" #include "Engine/Blueprint.h" #include "K2Node_CallFunction.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/KismetEditorUtilities.h" #include "LPDataCompat.h" #include "Misc/PackageName.h" #include "UObject/SavePackage.h" DEFINE_LOG_CATEGORY_STATIC(LogLPFixFindCenter, Log, All); namespace { // EventGraph : ville, extension, troisieme boucle de batiments. MoveTo : meme fonction hors chargement. constexpr int32 ExpectedCalls = 4; TArray Links(const UEdGraphPin* Pin) { TArray Out; if (Pin) { for (const UEdGraphPin* L : Pin->LinkedTo) { Out.Add(FString::Printf(TEXT("%s.%s"), L && L->GetOwningNode() ? *L->GetOwningNode()->GetName() : TEXT("?"), L ? *L->PinName.ToString() : TEXT("?"))); } } Out.Sort(); return Out; } FString PinState(const UEdGraphPin* Pin) { return FString::Printf(TEXT("[%s] defaut='%s'"), *FString::Join(Links(Pin), TEXT(", ")), *Pin->DefaultValue); } bool IsTargetPin(const UEdGraphPin* Pin) { return Pin && (Pin->PinName == UEdGraphSchema_K2::PN_Self || Pin->PinName == FName(TEXT("Target"))); } UFunction* ReplacementFor(const UK2Node_CallFunction* Node) { if (!Node || Node->FunctionReference.GetMemberName() != FName(TEXT("FindCenterOfPolygon"))) { return nullptr; } const UClass* Owner = Node->FunctionReference.GetMemberParentClass(Node->GetBlueprintClassFromNode()); if (!Owner || (!Owner->GetName().StartsWith(TEXT("GI_C")) && !Owner->GetName().StartsWith(TEXT("SKEL_GI_C")))) { return nullptr; } return ULPDataCompat::StaticClass()->FindFunctionByName(TEXT("FindCenterOfPolygon")); } } ULPFixFindCenterCommandlet::ULPFixFindCenterCommandlet() { IsClient = false; IsServer = false; IsEditor = true; LogToConsole = true; } int32 ULPFixFindCenterCommandlet::Main(const FString& Params) { const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase); if (!ULPDataCompat::StaticClass()->FindFunctionByName(TEXT("FindCenterOfPolygon"))) { UE_LOG(LogLPFixFindCenter, Error, TEXT("ULPDataCompat::FindCenterOfPolygon introuvable.")); return 1; } UE_LOG(LogLPFixFindCenter, Warning, TEXT("=== GI::FindCenterOfPolygon -> LPDataCompat%s ==="), bDryRun ? TEXT(" [DRY RUN]") : TEXT("")); FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); AssetRegistryModule.Get().SearchAllAssets(true); TArray BlueprintAssets; AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true); const UEdGraphSchema_K2* Schema = GetDefault(); int32 NodesFound = 0; int32 NodesFixed = 0; UBlueprint* GI = nullptr; for (const FAssetData& AssetData : BlueprintAssets) { if (AssetData.PackageName != FName(TEXT("/Game/BPs/GI"))) { continue; } GI = Cast(AssetData.GetAsset()); break; } if (!GI) { UE_LOG(LogLPFixFindCenter, Error, TEXT("Blueprint /Game/BPs/GI introuvable.")); return 1; } TArray Graphs; GI->GetAllGraphs(Graphs); for (UEdGraph* Graph : Graphs) { if (!Graph) { continue; } TArray Snapshot = Graph->Nodes; for (UEdGraphNode* GraphNode : Snapshot) { UK2Node_CallFunction* OldNode = Cast(GraphNode); UFunction* NewFunction = ReplacementFor(OldNode); if (!NewFunction) { continue; } ++NodesFound; UE_LOG(LogLPFixFindCenter, Warning, TEXT(" %s %s (%d, %d)"), *Graph->GetName(), *OldNode->GetName(), OldNode->NodePosX, OldNode->NodePosY); const UEdGraphPin* OldSelf = OldNode->FindPin(UEdGraphSchema_K2::PN_Self, EGPD_Input); if (OldSelf && OldSelf->LinkedTo.Num() > 0) { UE_LOG(LogLPFixFindCenter, Error, TEXT(" cible self reliee, abandon.")); return 1; } if (bDryRun) { continue; } TMap Before; for (const UEdGraphPin* Pin : OldNode->Pins) { if (Pin && !IsTargetPin(Pin)) { Before.Add(Pin->PinName, PinState(Pin)); } } UK2Node_CallFunction* NewNode = NewObject(Graph); NewNode->SetFromFunction(NewFunction); NewNode->NodePosX = OldNode->NodePosX; NewNode->NodePosY = OldNode->NodePosY; NewNode->CreateNewGuid(); NewNode->PostPlacedNewNode(); NewNode->AllocateDefaultPins(); Graph->Modify(); Graph->AddNode(NewNode, false, false); bool bOk = true; for (UEdGraphPin* OldPin : OldNode->Pins) { if (!OldPin || IsTargetPin(OldPin)) { continue; } UEdGraphPin* NewPin = NewNode->FindPin(OldPin->PinName, OldPin->Direction); if (!NewPin) { bOk = false; UE_LOG(LogLPFixFindCenter, Error, TEXT(" broche %s sans equivalent."), *OldPin->PinName.ToString()); break; } NewPin->DefaultValue = OldPin->DefaultValue; NewPin->DefaultObject = OldPin->DefaultObject; NewPin->DefaultTextValue = OldPin->DefaultTextValue; if (OldPin->LinkedTo.Num() > 0 && Schema->MovePinLinks(*OldPin, *NewPin).Response == CONNECT_RESPONSE_DISALLOW) { bOk = false; UE_LOG(LogLPFixFindCenter, Error, TEXT(" liaison refusee sur %s."), *OldPin->PinName.ToString()); break; } } if (bOk) { int32 Compared = 0; for (const UEdGraphPin* NewPin : NewNode->Pins) { if (!NewPin || IsTargetPin(NewPin)) { continue; } const FString* Expected = Before.Find(NewPin->PinName); if (!Expected || *Expected != PinState(NewPin)) { bOk = false; UE_LOG(LogLPFixFindCenter, Error, TEXT(" apres : %s %s (attendu %s)"), *NewPin->PinName.ToString(), *PinState(NewPin), Expected ? **Expected : TEXT("")); } ++Compared; } if (Compared != Before.Num()) { bOk = false; UE_LOG(LogLPFixFindCenter, Error, TEXT(" %d broche(s) apres, %d avant."), Compared, Before.Num()); } } if (!bOk) { UE_LOG(LogLPFixFindCenter, Error, TEXT(" ecart : Blueprint non sauvegarde.")); return 1; } OldNode->DestroyNode(); ++NodesFixed; } } UE_LOG(LogLPFixFindCenter, Warning, TEXT("=== %d appel(s) trouve(s), %d remplace(s), attendu %d ==="), NodesFound, NodesFixed, ExpectedCalls); if (NodesFound != ExpectedCalls || (!bDryRun && NodesFixed != ExpectedCalls)) { UE_LOG(LogLPFixFindCenter, Error, TEXT("Compte inattendu : Blueprint non sauvegarde.")); return 1; } if (bDryRun) { return 0; } FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GI); FKismetEditorUtilities::CompileBlueprint(GI); UPackage* Package = GI->GetOutermost(); FString Filename; if (!FPackageName::TryConvertLongPackageNameToFilename( Package->GetName(), Filename, FPackageName::GetAssetPackageExtension())) { UE_LOG(LogLPFixFindCenter, Error, TEXT("chemin introuvable")); return 1; } ResetLoaders(Package); FSavePackageArgs SaveArgs; SaveArgs.TopLevelFlags = RF_Public | RF_Standalone; SaveArgs.SaveFlags = SAVE_None; SaveArgs.bWarnOfLongFilename = true; SaveArgs.Error = GWarn; if (!UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs)) { UE_LOG(LogLPFixFindCenter, Error, TEXT("ECHEC de sauvegarde")); return 1; } UE_LOG(LogLPFixFindCenter, Warning, TEXT("GI sauvegarde.")); return 0; }