diff --git a/Content/BPs/GI.uasset b/Content/BPs/GI.uasset index 9b2266d..0498362 100644 Binary files a/Content/BPs/GI.uasset and b/Content/BPs/GI.uasset differ diff --git a/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.cpp b/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.cpp new file mode 100644 index 0000000..ca384a5 --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.cpp @@ -0,0 +1,255 @@ +#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; +} diff --git a/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.h b/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.h new file mode 100644 index 0000000..92ea494 --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixFindCenterCommandlet.h @@ -0,0 +1,20 @@ +// Rebranche les appels GI::FindCenterOfPolygon vers ULPDataCompat::FindCenterOfPolygon. +// La fonction Blueprint d'origine reste dans GI. Tout ecart de broche : Blueprint non sauvegarde. +// +// Usage : +// UnrealEditor-Cmd.exe -run=LPFixFindCenter [-DryRun] +#pragma once + +#include "CoreMinimal.h" +#include "Commandlets/Commandlet.h" +#include "LPFixFindCenterCommandlet.generated.h" + +UCLASS() +class ULPFixFindCenterCommandlet : public UCommandlet +{ + GENERATED_BODY() + +public: + ULPFixFindCenterCommandlet(); + virtual int32 Main(const FString& Params) override; +};