LifePlay-Revival/Source/LifePlayEditorTools/LPFixSkeletalMeshGetCommandlet.cpp

254 lines
7.7 KiB
C++
Raw Permalink Normal View History

// Portage UE 5.5 - voir LPFixSkeletalMeshGetCommandlet.h
#include "LPFixSkeletalMeshGetCommandlet.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Components/SkinnedMeshComponent.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "Engine/Blueprint.h"
#include "K2Node_CallFunction.h"
#include "K2Node_VariableGet.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "LPMeshCompat.h"
#include "Misc/PackageName.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPFixSkelMesh, Log, All);
namespace
{
const FName SkeletalMeshPropertyName(TEXT("SkeletalMesh"));
const FName NewFunctionName(TEXT("GetSkeletalMesh"));
const FName ComponentPinName(TEXT("Component"));
/** Extremites reliees a une broche, triees : "Noeud.Broche". */
TArray<FString> Links(const UEdGraphPin* Pin)
{
TArray<FString> 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;
}
bool IsDeprecatedSkeletalMeshGet(const UK2Node_VariableGet* Node)
{
if (!Node || Node->VariableReference.GetMemberName() != SkeletalMeshPropertyName)
{
return false;
}
const UClass* Owner = Node->VariableReference.GetMemberParentClass();
return Owner && Owner->IsChildOf(USkinnedMeshComponent::StaticClass());
}
bool HasExecPins(const UEdGraphNode* Node)
{
for (const UEdGraphPin* Pin : Node->Pins)
{
if (Pin && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
{
return true;
}
}
return false;
}
}
ULPFixSkeletalMeshGetCommandlet::ULPFixSkeletalMeshGetCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPFixSkeletalMeshGetCommandlet::Main(const FString& Params)
{
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
UFunction* NewFunction = ULPMeshCompat::StaticClass()->FindFunctionByName(NewFunctionName);
if (!NewFunction)
{
UE_LOG(LogLPFixSkelMesh, Error, TEXT("ULPMeshCompat::GetSkeletalMesh introuvable."));
return 1;
}
UE_LOG(LogLPFixSkelMesh, Warning, TEXT("=== Get SkeletalMesh (deprecie) -> LPMeshCompat::GetSkeletalMesh%s ==="),
bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
FAssetRegistryModule& AssetRegistryModule =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
TArray<FAssetData> BlueprintAssets;
// Sous-classes incluses : couvre les UWidgetBlueprint (CharGen, Inventory, ...).
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true);
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
int32 NodesFound = 0;
int32 NodesFixed = 0;
int32 Unhandled = 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 FixedHere = 0;
int32 FailedHere = 0;
for (UEdGraph* Graph : Graphs)
{
if (!Graph)
{
continue;
}
// Copie : on ajoute et on detruit des noeuds pendant le parcours.
TArray<UEdGraphNode*> Snapshot = Graph->Nodes;
for (UEdGraphNode* GraphNode : Snapshot)
{
UK2Node_VariableGet* VarGet = Cast<UK2Node_VariableGet>(GraphNode);
if (!IsDeprecatedSkeletalMeshGet(VarGet))
{
continue;
}
++NodesFound;
UEdGraphPin* OldSelf = VarGet->FindPin(UEdGraphSchema_K2::PN_Self, EGPD_Input);
UEdGraphPin* OldValue = VarGet->GetValuePin();
const TArray<FString> SelfBefore = Links(OldSelf);
const TArray<FString> ValueBefore = Links(OldValue);
UE_LOG(LogLPFixSkelMesh, Warning, TEXT(" %s :: %s -> %s | cible [%s] | valeur [%s]"),
*Blueprint->GetName(), *Graph->GetName(), *VarGet->GetName(),
*FString::Join(SelfBefore, TEXT(", ")), *FString::Join(ValueBefore, TEXT(", ")));
if (!OldValue || SelfBefore.Num() == 0 || HasExecPins(VarGet))
{
++Unhandled;
++FailedHere;
UE_LOG(LogLPFixSkelMesh, Error, TEXT(" cas non pris en charge (%s) : noeud laisse en l'etat."),
!OldValue ? TEXT("pas de broche de valeur") : SelfBefore.Num() == 0 ? TEXT("cible = self") : TEXT("noeud impur"));
continue;
}
if (bDryRun)
{
continue;
}
UK2Node_CallFunction* NewNode = NewObject<UK2Node_CallFunction>(Graph);
NewNode->SetFromFunction(NewFunction);
NewNode->NodePosX = VarGet->NodePosX;
NewNode->NodePosY = VarGet->NodePosY;
NewNode->CreateNewGuid();
NewNode->PostPlacedNewNode();
NewNode->AllocateDefaultPins();
Graph->Modify();
Graph->AddNode(NewNode, false, false);
UEdGraphPin* NewComponent = NewNode->FindPin(ComponentPinName, EGPD_Input);
UEdGraphPin* NewValue = NewNode->GetReturnValuePin();
bool bOk = NewComponent && NewValue
&& Schema->MovePinLinks(*OldSelf, *NewComponent).Response != CONNECT_RESPONSE_DISALLOW
&& Schema->MovePinLinks(*OldValue, *NewValue).Response != CONNECT_RESPONSE_DISALLOW;
// Les noms des noeuds voisins ne changent pas : on doit retrouver exactement les memes extremites.
if (bOk && (Links(NewComponent) != SelfBefore || Links(NewValue) != ValueBefore))
{
bOk = false;
UE_LOG(LogLPFixSkelMesh, Error, TEXT(" apres : cible [%s] | valeur [%s]"),
*FString::Join(Links(NewComponent), TEXT(", ")), *FString::Join(Links(NewValue), TEXT(", ")));
}
if (!bOk)
{
// Le Blueprint ne sera pas sauvegarde : le paquet sur disque reste intact.
++Unhandled;
++FailedHere;
UE_LOG(LogLPFixSkelMesh, Error, TEXT(" ecart de liaisons : Blueprint non sauvegarde."));
continue;
}
VarGet->DestroyNode();
++NodesFixed;
++FixedHere;
}
}
if (FailedHere > 0)
{
UE_LOG(LogLPFixSkelMesh, Error, TEXT(" -> %s : %d noeud(s) en ecart, Blueprint NON sauvegarde."),
*Blueprint->GetName(), FailedHere);
continue;
}
if (FixedHere == 0 || bDryRun)
{
continue;
}
++BlueprintsModified;
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
FKismetEditorUtilities::CompileBlueprint(Blueprint);
UPackage* Package = Blueprint->GetOutermost();
FString Filename;
if (!FPackageName::TryConvertLongPackageNameToFilename(
Package->GetName(), Filename, FPackageName::GetAssetPackageExtension()))
{
++SaveFailures;
UE_LOG(LogLPFixSkelMesh, 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(LogLPFixSkelMesh, Warning, TEXT(" -> %s sauvegarde (%d noeud(s))."), *Package->GetName(), FixedHere);
}
else
{
++SaveFailures;
UE_LOG(LogLPFixSkelMesh, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
}
}
UE_LOG(LogLPFixSkelMesh, Warning,
TEXT("=== Termine : %d noeud(s) trouve(s), %d remplace(s), %d Blueprint(s) modifie(s), %d non traite(s), %d echec(s) de sauvegarde. ==="),
NodesFound, NodesFixed, BlueprintsModified, Unhandled, SaveFailures);
return (SaveFailures > 0 || Unhandled > 0) ? 1 : 0;
}