128 lines
3.7 KiB
C++
128 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "WingServer.h"
|
|
#include "WingHandler.h"
|
|
#include "WingUtils.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "AssetRegistry/IAssetRegistry.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "UObject/LinkerLoad.h"
|
|
#include "UObject/Package.h"
|
|
#include "Asset_Delete.generated.h"
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
|
|
UCLASS()
|
|
class UWing_Asset_Delete : public UObject, public IWingHandler
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UPROPERTY(meta=(Description="Asset to delete"))
|
|
FString Asset;
|
|
|
|
UPROPERTY(meta=(Optional, Description="If true, skip reference check and force delete"))
|
|
bool Force = false;
|
|
|
|
virtual FString GetDescription() const override
|
|
{
|
|
return TEXT("Delete a .uasset after verifying no references. "
|
|
"Use force=true to skip the reference check.");
|
|
}
|
|
|
|
virtual void Handle() override
|
|
{
|
|
// Verify the asset file exists on disk
|
|
FString PackageFilename = FPackageName::LongPackageNameToFilename(
|
|
Asset, FPackageName::GetAssetPackageExtension());
|
|
PackageFilename = FPaths::ConvertRelativePathToFull(PackageFilename);
|
|
|
|
if (!IFileManager::Get().FileExists(*PackageFilename))
|
|
{
|
|
UWingServer::Printf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename);
|
|
return;
|
|
}
|
|
|
|
// Check references
|
|
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
|
TArray<FName> Referencers;
|
|
Registry.GetReferencers(FName(*Asset), Referencers);
|
|
|
|
// Filter out self-references
|
|
Referencers.RemoveAll([this](const FName& Ref) {
|
|
return Ref.ToString() == Asset;
|
|
});
|
|
|
|
if (Referencers.Num() > 0 && !Force)
|
|
{
|
|
UWingServer::Printf(TEXT("ERROR: Asset is still referenced by %d package(s):\n"), Referencers.Num());
|
|
for (const FName& Ref : Referencers)
|
|
{
|
|
FString RefStr = Ref.ToString();
|
|
UPackage* RefPackage = FindPackage(nullptr, *RefStr);
|
|
UWingServer::Printf(TEXT(" %s%s\n"), *RefStr,
|
|
RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)"));
|
|
}
|
|
UWingServer::Print(TEXT("Use force=true to skip the reference check.\n"));
|
|
return;
|
|
}
|
|
|
|
// Force delete: unload the package from memory first
|
|
if (Force && Referencers.Num() > 0)
|
|
{
|
|
UWingServer::Printf(TEXT("WARNING: Force-deleting despite %d referencer(s).\n"), Referencers.Num());
|
|
}
|
|
|
|
// Mark the package, and all the objects in it, as NOT
|
|
// GC Roots. Also, make them undiscoverable.
|
|
UPackage* Package = FindPackage(nullptr, *Asset);
|
|
if (Package)
|
|
{
|
|
// Collect all objects in this package
|
|
TArray<UObject*> ObjectsInPackage;
|
|
GetObjectsWithPackage(Package, ObjectsInPackage);
|
|
|
|
// Clear flags and remove from root to allow GC
|
|
for (UObject* Obj : ObjectsInPackage)
|
|
{
|
|
if (Obj)
|
|
{
|
|
Obj->ClearFlags(RF_Standalone | RF_Public);
|
|
Obj->RemoveFromRoot();
|
|
}
|
|
}
|
|
Package->ClearFlags(RF_Standalone | RF_Public);
|
|
Package->RemoveFromRoot();
|
|
}
|
|
|
|
// The loader that loaded the package might still
|
|
// have a file lock on it. Unlock the file.
|
|
ResetLoaders(Package);
|
|
|
|
// Delete the file on disk
|
|
bool bDeleted = IFileManager::Get().Delete(*PackageFilename, false, true);
|
|
|
|
if (!bDeleted)
|
|
{
|
|
UWingServer::Printf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename);
|
|
return;
|
|
}
|
|
|
|
// Trigger an asset registry rescan so it notices the deletion
|
|
FString PackageDir;
|
|
int32 LastSlash;
|
|
if (Asset.FindLastChar(TEXT('/'), LastSlash))
|
|
{
|
|
PackageDir = Asset.Left(LastSlash);
|
|
Registry.ScanPathsSynchronous({PackageDir}, true);
|
|
}
|
|
|
|
UWingServer::Printf(TEXT("Deleted %s\n"), *Asset);
|
|
}
|
|
};
|