201 lines
5.6 KiB
C++
201 lines
5.6 KiB
C++
|
|
// Portage UE 5.5 - voir LPFixOrphanBoundEventsCommandlet.h
|
||
|
|
|
||
|
|
#include "LPFixOrphanBoundEventsCommandlet.h"
|
||
|
|
|
||
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||
|
|
#include "EdGraph/EdGraph.h"
|
||
|
|
#include "EdGraph/EdGraphPin.h"
|
||
|
|
#include "EdGraphSchema_K2.h"
|
||
|
|
#include "Engine/Blueprint.h"
|
||
|
|
#include "K2Node_ComponentBoundEvent.h"
|
||
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
||
|
|
#include "Misc/PackageName.h"
|
||
|
|
#include "UObject/Package.h"
|
||
|
|
#include "UObject/SavePackage.h"
|
||
|
|
#include "UObject/UObjectGlobals.h"
|
||
|
|
|
||
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLPFixBoundEvents, Log, All);
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Reproduit le test que le compilateur applique lui-meme : le composant referencE par
|
||
|
|
* l'evenement doit exister sous forme de FObjectProperty sur la classe squelette du
|
||
|
|
* Blueprint. UK2Node_ComponentBoundEvent::IsDelegateValid() est prive, d'ou la copie.
|
||
|
|
*/
|
||
|
|
bool ComponentStillExists(const UBlueprint* Blueprint, FName ComponentPropertyName)
|
||
|
|
{
|
||
|
|
if (!Blueprint || ComponentPropertyName.IsNone())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const UClass* Scope = Blueprint->SkeletonGeneratedClass
|
||
|
|
? Blueprint->SkeletonGeneratedClass
|
||
|
|
: Blueprint->GeneratedClass;
|
||
|
|
return Scope && FindFProperty<FObjectProperty>(Scope, ComponentPropertyName) != nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Compte les noeuds atteignables depuis la broche Exec de sortie de l'evenement. */
|
||
|
|
int32 CountDownstreamNodes(const UEdGraphNode* EventNode)
|
||
|
|
{
|
||
|
|
TSet<const UEdGraphNode*> Seen;
|
||
|
|
TArray<const UEdGraphNode*> Pending;
|
||
|
|
Pending.Add(EventNode);
|
||
|
|
Seen.Add(EventNode);
|
||
|
|
|
||
|
|
while (Pending.Num() > 0)
|
||
|
|
{
|
||
|
|
const UEdGraphNode* Current = Pending.Pop();
|
||
|
|
for (const UEdGraphPin* Pin : Current->Pins)
|
||
|
|
{
|
||
|
|
if (!Pin || Pin->Direction != EGPD_Output || Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
for (const UEdGraphPin* Linked : Pin->LinkedTo)
|
||
|
|
{
|
||
|
|
const UEdGraphNode* Next = Linked ? Linked->GetOwningNode() : nullptr;
|
||
|
|
if (Next && !Seen.Contains(Next))
|
||
|
|
{
|
||
|
|
Seen.Add(Next);
|
||
|
|
Pending.Add(Next);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Seen.Num() - 1; // on ne compte pas l'evenement lui-meme
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ULPFixOrphanBoundEventsCommandlet::ULPFixOrphanBoundEventsCommandlet()
|
||
|
|
{
|
||
|
|
IsClient = false;
|
||
|
|
IsServer = false;
|
||
|
|
IsEditor = true;
|
||
|
|
LogToConsole = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32 ULPFixOrphanBoundEventsCommandlet::Main(const FString& Params)
|
||
|
|
{
|
||
|
|
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixBoundEvents, Warning, TEXT("=== Evenements de composant orphelins (R-05)%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 NodesFound = 0;
|
||
|
|
int32 NodesRemoved = 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 RemovedHere = 0;
|
||
|
|
|
||
|
|
for (UEdGraph* Graph : Graphs)
|
||
|
|
{
|
||
|
|
if (!Graph)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
TArray<UEdGraphNode*> Snapshot = Graph->Nodes;
|
||
|
|
for (UEdGraphNode* GraphNode : Snapshot)
|
||
|
|
{
|
||
|
|
UK2Node_ComponentBoundEvent* BoundEvent = Cast<UK2Node_ComponentBoundEvent>(GraphNode);
|
||
|
|
if (!BoundEvent)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
const FName ComponentName = BoundEvent->GetComponentPropertyName();
|
||
|
|
if (ComponentStillExists(Blueprint, ComponentName))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
++NodesFound;
|
||
|
|
const int32 Downstream = CountDownstreamNodes(BoundEvent);
|
||
|
|
UE_LOG(LogLPFixBoundEvents, Warning,
|
||
|
|
TEXT(" %s :: %s -> %s (composant '%s' introuvable, %d noeud(s) en aval)"),
|
||
|
|
*Blueprint->GetName(), *Graph->GetName(), *GraphNode->GetName(),
|
||
|
|
*ComponentName.ToString(), Downstream);
|
||
|
|
|
||
|
|
if (bDryRun)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
Graph->Modify();
|
||
|
|
BoundEvent->Modify();
|
||
|
|
BoundEvent->DestroyNode();
|
||
|
|
++NodesRemoved;
|
||
|
|
++RemovedHere;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (RemovedHere > 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(LogLPFixBoundEvents, 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;
|
||
|
|
SaveArgs.Error = GWarn;
|
||
|
|
|
||
|
|
if (UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPFixBoundEvents, Warning, TEXT(" -> %s sauvegarde (%d evenement(s) retire(s))."),
|
||
|
|
*Package->GetName(), RemovedHere);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
++SaveFailures;
|
||
|
|
UE_LOG(LogLPFixBoundEvents, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UE_LOG(LogLPFixBoundEvents, Warning,
|
||
|
|
TEXT("=== Termine : %d evenement(s) orphelin(s) trouve(s), %d retire(s) dans %d Blueprint(s), %d echec(s) de sauvegarde. ==="),
|
||
|
|
NodesFound, NodesRemoved, BlueprintsModified, SaveFailures);
|
||
|
|
|
||
|
|
return SaveFailures > 0 ? 1 : 0;
|
||
|
|
}
|