220 lines
6.3 KiB
C++
220 lines
6.3 KiB
C++
|
|
// Portage UE 4.27 (2026.9 beta) - voir LPFixAssetRegistryNodesCommandlet.h
|
||
|
|
|
||
|
|
#include "LPFixAssetRegistryNodesCommandlet.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 "Kismet2/BlueprintEditorUtils.h"
|
||
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
||
|
|
#include "LPAssetRegistryHelpers.h"
|
||
|
|
#include "Misc/FeedbackContext.h"
|
||
|
|
#include "Misc/PackageName.h"
|
||
|
|
#include "UObject/Package.h"
|
||
|
|
#include "UObject/SavePackage.h"
|
||
|
|
#include "UObject/UObjectGlobals.h"
|
||
|
|
|
||
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLPFixNodes, Log, All);
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
const TCHAR* TargetFunctionName = TEXT("GetAssetByObjectPath");
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Le noeud impur possede des broches Exec que la version pure n'aura plus.
|
||
|
|
* Si elles sont reliees, on raccorde l'amont a l'aval pour ne pas casser la chaine
|
||
|
|
* d'execution avant de reconstruire le noeud.
|
||
|
|
*/
|
||
|
|
void BridgeExecPins(UK2Node_CallFunction* Node)
|
||
|
|
{
|
||
|
|
UEdGraphPin* ExecPin = Node->FindPin(UEdGraphSchema_K2::PN_Execute, EGPD_Input);
|
||
|
|
UEdGraphPin* ThenPin = Node->FindPin(UEdGraphSchema_K2::PN_Then, EGPD_Output);
|
||
|
|
|
||
|
|
if (!ExecPin || !ThenPin || ExecPin->LinkedTo.Num() == 0 || ThenPin->LinkedTo.Num() == 0)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
TArray<UEdGraphPin*> Sources = ExecPin->LinkedTo;
|
||
|
|
TArray<UEdGraphPin*> Destinations = ThenPin->LinkedTo;
|
||
|
|
|
||
|
|
for (UEdGraphPin* Source : Sources)
|
||
|
|
{
|
||
|
|
for (UEdGraphPin* Destination : Destinations)
|
||
|
|
{
|
||
|
|
Source->MakeLinkTo(Destination);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning,
|
||
|
|
TEXT(" (broches Exec reliees : chaine d'execution pontee sur %s)"), *Node->GetName());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ULPFixAssetRegistryNodesCommandlet::ULPFixAssetRegistryNodesCommandlet()
|
||
|
|
{
|
||
|
|
IsClient = false;
|
||
|
|
IsServer = false;
|
||
|
|
IsEditor = true;
|
||
|
|
LogToConsole = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32 ULPFixAssetRegistryNodesCommandlet::Main(const FString& Params)
|
||
|
|
{
|
||
|
|
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
|
||
|
|
|
||
|
|
UFunction* NewFunction = ULPAssetRegistryHelpers::StaticClass()->FindFunctionByName(FName(TargetFunctionName));
|
||
|
|
if (!NewFunction)
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPFixNodes, Error, TEXT("ULPAssetRegistryHelpers::%s introuvable."), TargetFunctionName);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT("=== Correction des noeuds %s%s ==="),
|
||
|
|
TargetFunctionName, bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
|
||
|
|
|
||
|
|
FAssetRegistryModule& AssetRegistryModule =
|
||
|
|
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||
|
|
AssetRegistryModule.Get().SearchAllAssets(true);
|
||
|
|
|
||
|
|
TArray<FAssetData> BlueprintAssets;
|
||
|
|
// 5.1+ : GetAssetsByClass prend un FTopLevelAssetPath, plus un FName de classe.
|
||
|
|
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true);
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT("%d Blueprints a inspecter."), BlueprintAssets.Num());
|
||
|
|
|
||
|
|
int32 NodesFixed = 0;
|
||
|
|
int32 BlueprintsModified = 0;
|
||
|
|
int32 SaveFailures = 0;
|
||
|
|
|
||
|
|
for (const FAssetData& AssetData : BlueprintAssets)
|
||
|
|
{
|
||
|
|
if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/")))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
UBlueprint* Blueprint = Cast<UBlueprint>(AssetData.GetAsset());
|
||
|
|
if (!Blueprint)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
TArray<UEdGraph*> Graphs;
|
||
|
|
Blueprint->GetAllGraphs(Graphs);
|
||
|
|
|
||
|
|
int32 NodesFixedHere = 0;
|
||
|
|
|
||
|
|
for (UEdGraph* Graph : Graphs)
|
||
|
|
{
|
||
|
|
if (!Graph)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
TArray<UEdGraphNode*> NodesSnapshot = Graph->Nodes;
|
||
|
|
for (UEdGraphNode* GraphNode : NodesSnapshot)
|
||
|
|
{
|
||
|
|
UK2Node_CallFunction* CallNode = Cast<UK2Node_CallFunction>(GraphNode);
|
||
|
|
if (!CallNode)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (CallNode->FunctionReference.GetMemberName() != FName(TargetFunctionName))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Deja migre vers la version pure : rien a faire.
|
||
|
|
if (CallNode->FunctionReference.GetMemberParentClass() == ULPAssetRegistryHelpers::StaticClass())
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT(" %s :: %s -> noeud %s"),
|
||
|
|
*Blueprint->GetName(), *Graph->GetName(), *CallNode->GetName());
|
||
|
|
|
||
|
|
if (!bDryRun)
|
||
|
|
{
|
||
|
|
Blueprint->Modify();
|
||
|
|
Graph->Modify();
|
||
|
|
CallNode->Modify();
|
||
|
|
|
||
|
|
BridgeExecPins(CallNode);
|
||
|
|
|
||
|
|
// L'ancien noeud est un appel d'interface : sa broche Target pointe vers
|
||
|
|
// l'objet AssetRegistry. La version pure est une fonction statique de
|
||
|
|
// bibliotheque : sa broche Target est d'un autre type et le lien serait
|
||
|
|
// reapplique tel quel par la reconstruction -> incompatibilite. On le coupe.
|
||
|
|
if (UEdGraphPin* SelfPin = CallNode->FindPin(UEdGraphSchema_K2::PN_Self, EGPD_Input))
|
||
|
|
{
|
||
|
|
SelfPin->BreakAllPinLinks();
|
||
|
|
}
|
||
|
|
|
||
|
|
CallNode->SetFromFunction(NewFunction);
|
||
|
|
CallNode->ReconstructNode();
|
||
|
|
}
|
||
|
|
|
||
|
|
++NodesFixedHere;
|
||
|
|
++NodesFixed;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (NodesFixedHere == 0)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
++BlueprintsModified;
|
||
|
|
|
||
|
|
if (bDryRun)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
||
|
|
FKismetEditorUtilities::CompileBlueprint(Blueprint);
|
||
|
|
|
||
|
|
UPackage* Package = Blueprint->GetOutermost();
|
||
|
|
Package->FullyLoad();
|
||
|
|
Package->SetDirtyFlag(true);
|
||
|
|
|
||
|
|
const FString Filename = FPackageName::LongPackageNameToFilename(
|
||
|
|
Package->GetName(), FPackageName::GetAssetPackageExtension());
|
||
|
|
|
||
|
|
// Le linker du package garde le .uasset ouvert : sans cela SavePackage ne peut pas
|
||
|
|
// remplacer le fichier ("DeleteFile was unable to delete ...").
|
||
|
|
ResetLoaders(Package);
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT(" -> sauvegarde vers %s"), *Filename);
|
||
|
|
|
||
|
|
// 5.x : les arguments passent par FSavePackageArgs (Conform n'est plus implemente).
|
||
|
|
FSavePackageArgs SaveArgs;
|
||
|
|
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
|
||
|
|
SaveArgs.SaveFlags = SAVE_None;
|
||
|
|
SaveArgs.bForceByteSwapping = false;
|
||
|
|
SaveArgs.bWarnOfLongFilename = true;
|
||
|
|
// GWarn et non GError : en commandlet, GError rend toute erreur de sauvegarde fatale.
|
||
|
|
SaveArgs.Error = GWarn;
|
||
|
|
|
||
|
|
if (UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT(" -> %s sauvegarde (%d noeud(s))."), *Package->GetName(), NodesFixedHere);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
++SaveFailures;
|
||
|
|
UE_LOG(LogLPFixNodes, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixNodes, Warning, TEXT("=== Termine : %d noeud(s) dans %d Blueprint(s), %d echec(s) de sauvegarde. ==="),
|
||
|
|
NodesFixed, BlueprintsModified, SaveFailures);
|
||
|
|
|
||
|
|
return SaveFailures > 0 ? 1 : 0;
|
||
|
|
}
|