Instantane de reference de la reprise de migration (2026-09-29). Contient GI.uasset tel que trouve (copie UE4 identique a 4.27, MD5 F4550DA5DC05FC005400CDCA2FC123C9). Ajouts de ce commit : .gitattributes (aucune conversion de fin de ligne), regles .gitignore CHG-001, Tools/ (Snapshot, Restore, AssetHeaders), empreintes L0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
319 lines
9.8 KiB
C++
319 lines
9.8 KiB
C++
// Portage UE 5.5 - voir LPFixAssetRegistryPinsCommandlet.h
|
|
|
|
#include "LPFixAssetRegistryPinsCommandlet.h"
|
|
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "K2Node_BreakStruct.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "LPAssetRegistryHelpers.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/SavePackage.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLPFixARPins, Log, All);
|
|
|
|
namespace
|
|
{
|
|
const FName DeprecatedClassNamePin(TEXT("ClassName"));
|
|
const FName DeprecatedObjectPathPin(TEXT("ObjectPath"));
|
|
const FName AssetDataPin(TEXT("AssetData"));
|
|
const FName AssetDataStructName(TEXT("AssetData"));
|
|
|
|
const FName GetAssetsByClassFunction(TEXT("GetAssetsByClass"));
|
|
const FName ReplacementEnumFunction(TEXT("GetAssetsByClassName"));
|
|
const FName ReplacementPathFunction(TEXT("GetAssetObjectPath"));
|
|
|
|
/** Broche orpheline portant ce nom, s'il y en a une. */
|
|
UEdGraphPin* FindOrphan(UEdGraphNode* Node, FName PinName)
|
|
{
|
|
for (UEdGraphPin* Pin : Node->Pins)
|
|
{
|
|
if (Pin && Pin->bOrphanedPin && Pin->PinName == PinName)
|
|
{
|
|
return Pin;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ULPFixAssetRegistryPinsCommandlet::ULPFixAssetRegistryPinsCommandlet()
|
|
{
|
|
IsClient = false;
|
|
IsServer = false;
|
|
IsEditor = true;
|
|
LogToConsole = true;
|
|
}
|
|
|
|
int32 ULPFixAssetRegistryPinsCommandlet::Main(const FString& Params)
|
|
{
|
|
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
|
|
|
|
UFunction* EnumFunction = ULPAssetRegistryHelpers::StaticClass()->FindFunctionByName(ReplacementEnumFunction);
|
|
UFunction* PathFunction = ULPAssetRegistryHelpers::StaticClass()->FindFunctionByName(ReplacementPathFunction);
|
|
if (!EnumFunction || !PathFunction)
|
|
{
|
|
UE_LOG(LogLPFixARPins, Error, TEXT("Fonctions de remplacement introuvables (%s=%s, %s=%s)."),
|
|
*ReplacementEnumFunction.ToString(), EnumFunction ? TEXT("ok") : TEXT("NULL"),
|
|
*ReplacementPathFunction.ToString(), PathFunction ? TEXT("ok") : TEXT("NULL"));
|
|
return 1;
|
|
}
|
|
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT("=== Broches AssetRegistry perdues a la migration 5.1%s ==="),
|
|
bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
|
|
|
|
FAssetRegistryModule& AssetRegistryModule =
|
|
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
|
AssetRegistryModule.Get().SearchAllAssets(true);
|
|
|
|
TArray<FAssetData> BlueprintAssets;
|
|
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true);
|
|
|
|
int32 EnumFixed = 0;
|
|
int32 PathFixed = 0;
|
|
int32 Unhandled = 0;
|
|
int32 BlueprintsModified = 0;
|
|
int32 SaveFailures = 0;
|
|
|
|
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
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 FixedHere = 0;
|
|
|
|
for (UEdGraph* Graph : Graphs)
|
|
{
|
|
if (!Graph)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TArray<UEdGraphNode*> Snapshot = Graph->Nodes;
|
|
for (UEdGraphNode* GraphNode : Snapshot)
|
|
{
|
|
// -------------------------------------------------------------------
|
|
// 1. GetAssetsByClass : la valeur de classe est restee sur l'orpheline.
|
|
// On bascule sur l'enrobage FName, qui la reprend telle quelle.
|
|
// -------------------------------------------------------------------
|
|
if (UK2Node_CallFunction* CallNode = Cast<UK2Node_CallFunction>(GraphNode))
|
|
{
|
|
if (CallNode->FunctionReference.GetMemberName() != GetAssetsByClassFunction)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UEdGraphPin* Orphan = FindOrphan(CallNode, DeprecatedClassNamePin);
|
|
if (!Orphan)
|
|
{
|
|
continue; // deja corrige, ou noeud sain
|
|
}
|
|
|
|
const FString ClassValue = Orphan->DefaultValue;
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT(" [enum] %s :: %s -> %s | classe='%s'"),
|
|
*Blueprint->GetName(), *Graph->GetName(), *GraphNode->GetName(), *ClassValue);
|
|
|
|
if (ClassValue.IsEmpty())
|
|
{
|
|
++Unhandled;
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" orpheline vide : rien a reporter, reprise manuelle."));
|
|
continue;
|
|
}
|
|
if (bDryRun)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CallNode->Modify();
|
|
// La broche orpheline doit disparaitre AVANT la reconstruction, sinon
|
|
// ReconstructNode la conserve (garde-fou d'UE contre la perte de donnees).
|
|
Orphan->BreakAllPinLinks();
|
|
Orphan->DefaultValue.Empty();
|
|
|
|
CallNode->SetFromFunction(EnumFunction);
|
|
CallNode->ReconstructNode();
|
|
|
|
// La fonction de remplacement a une broche ClassName de meme nom : on y
|
|
// remet la valeur recuperee.
|
|
if (UEdGraphPin* NewPin = CallNode->FindPin(DeprecatedClassNamePin, EGPD_Input))
|
|
{
|
|
NewPin->DefaultValue = ClassValue;
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT(" -> GetAssetsByClassName(ClassName='%s')"), *ClassValue);
|
|
++EnumFixed;
|
|
++FixedHere;
|
|
}
|
|
else
|
|
{
|
|
++Unhandled;
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" broche ClassName absente apres reconstruction."));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// 2. Break AssetData : la sortie ObjectPath n'existe plus. On insere un
|
|
// appel a GetAssetObjectPath alimente par la meme structure, et on lui
|
|
// transfere les liaisons restees sur l'orpheline.
|
|
// -------------------------------------------------------------------
|
|
UK2Node_BreakStruct* BreakNode = Cast<UK2Node_BreakStruct>(GraphNode);
|
|
if (!BreakNode || !BreakNode->StructType ||
|
|
BreakNode->StructType->GetFName() != AssetDataStructName)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UEdGraphPin* Orphan = FindOrphan(BreakNode, DeprecatedObjectPathPin);
|
|
if (!Orphan)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UEdGraphPin* StructInput = BreakNode->FindPin(AssetDataPin, EGPD_Input);
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT(" [path] %s :: %s -> %s | %d consommateur(s)"),
|
|
*Blueprint->GetName(), *Graph->GetName(), *GraphNode->GetName(), Orphan->LinkedTo.Num());
|
|
|
|
if (Orphan->LinkedTo.Num() == 0)
|
|
{
|
|
// Personne ne la lit : la vider suffit a la faire disparaitre.
|
|
if (!bDryRun)
|
|
{
|
|
BreakNode->Modify();
|
|
Orphan->DefaultValue.Empty();
|
|
BreakNode->ReconstructNode();
|
|
++PathFixed;
|
|
++FixedHere;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!StructInput || StructInput->LinkedTo.Num() == 0)
|
|
{
|
|
++Unhandled;
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" entree AssetData non connectee : reprise manuelle."));
|
|
continue;
|
|
}
|
|
if (bDryRun)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Noeud de remplacement, place juste sous le Break.
|
|
UK2Node_CallFunction* PathNode = NewObject<UK2Node_CallFunction>(Graph);
|
|
PathNode->SetFromFunction(PathFunction);
|
|
PathNode->NodePosX = BreakNode->NodePosX;
|
|
PathNode->NodePosY = BreakNode->NodePosY + 120;
|
|
PathNode->CreateNewGuid();
|
|
PathNode->PostPlacedNewNode();
|
|
PathNode->AllocateDefaultPins();
|
|
Graph->Modify();
|
|
Graph->AddNode(PathNode, false, false);
|
|
|
|
UEdGraphPin* PathStructIn = PathNode->FindPin(AssetDataPin, EGPD_Input);
|
|
UEdGraphPin* PathReturn = PathNode->GetReturnValuePin();
|
|
|
|
bool bOk = PathStructIn && PathReturn;
|
|
|
|
// Meme source de structure que le Break.
|
|
if (bOk)
|
|
{
|
|
UEdGraphPin* Source = StructInput->LinkedTo[0];
|
|
bOk = Schema->TryCreateConnection(Source, PathStructIn);
|
|
if (!bOk)
|
|
{
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" source AssetData incompatible avec la nouvelle entree."));
|
|
}
|
|
}
|
|
|
|
// Transfert des consommateurs de l'orpheline vers la nouvelle sortie.
|
|
if (bOk)
|
|
{
|
|
const FPinConnectionResponse R = Schema->MovePinLinks(*Orphan, *PathReturn);
|
|
bOk = R.Response != CONNECT_RESPONSE_DISALLOW;
|
|
if (!bOk)
|
|
{
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" transfert impossible (%s)."), *R.Message.ToString());
|
|
}
|
|
}
|
|
|
|
if (!bOk)
|
|
{
|
|
++Unhandled;
|
|
PathNode->DestroyNode();
|
|
continue;
|
|
}
|
|
|
|
BreakNode->Modify();
|
|
Orphan->BreakAllPinLinks();
|
|
Orphan->DefaultValue.Empty();
|
|
BreakNode->ReconstructNode();
|
|
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT(" -> GetAssetObjectPath inserE"));
|
|
++PathFixed;
|
|
++FixedHere;
|
|
}
|
|
}
|
|
|
|
if (FixedHere > 0 && !bDryRun)
|
|
{
|
|
++BlueprintsModified;
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
|
FKismetEditorUtilities::CompileBlueprint(Blueprint);
|
|
|
|
UPackage* Package = Blueprint->GetOutermost();
|
|
FString Filename;
|
|
if (!FPackageName::TryConvertLongPackageNameToFilename(
|
|
Package->GetName(), Filename, FPackageName::GetAssetPackageExtension()))
|
|
{
|
|
++SaveFailures;
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" chemin introuvable pour %s"), *Package->GetName());
|
|
continue;
|
|
}
|
|
|
|
ResetLoaders(Package);
|
|
|
|
FSavePackageArgs SaveArgs;
|
|
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
|
|
SaveArgs.SaveFlags = SAVE_None;
|
|
SaveArgs.bWarnOfLongFilename = true;
|
|
// GWarn et non GError : en commandlet, GError rend toute erreur de save fatale.
|
|
SaveArgs.Error = GWarn;
|
|
|
|
if (UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
|
|
{
|
|
UE_LOG(LogLPFixARPins, Warning, TEXT(" -> %s sauvegarde (%d noeud(s))."), *Package->GetName(), FixedHere);
|
|
}
|
|
else
|
|
{
|
|
++SaveFailures;
|
|
UE_LOG(LogLPFixARPins, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
|
|
}
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogLPFixARPins, Warning,
|
|
TEXT("=== Termine : %d enumeration(s), %d chemin(s), %d Blueprint(s) modifie(s), %d cas non traite(s), %d echec(s). ==="),
|
|
EnumFixed, PathFixed, BlueprintsModified, Unhandled, SaveFailures);
|
|
|
|
return (SaveFailures > 0 || Unhandled > 0) ? 1 : 0;
|
|
}
|