Files
AssetUsageAudit/Source/AssetUsageAuditCore/Public/AssetUsageAuditor.h
T
Admin 72bf94d142 feat: asset usage audit plugin
Editor tool for the LA and 3D departments: which assets each level uses,
which are used nowhere, and export of a chosen set.

Three modules. AssetUsageAuditCore holds the whole analysis and links no
UI and no editor-only asset pipeline, so it stays runnable from a
commandlet; AssetUsageAuditEditor holds the Slate panel and everything
that needs UnrealEd or AssetTools; AssetUsageAuditTests holds 152 specs.

Load-bearing decisions, each of which produces a wrong answer if undone:

- Dependency queries are always Package + NoRequirements, never Hard. The
  map-to-external-actor edges the OFPA gatherer emits carry Game|Build
  without Hard, so a Hard query drops all 16117 external actor packages
  in this project. There is deliberately no Hard constant in the code.
- Crossing into another map is allowed only from a level or its external
  actor package. Without that rule WP_Main reported 18136 assets, of
  which 9994 belonged to L_MainLevel, reached through the GameMode.
- The verdict has five states, never a bool. The registry cannot see
  FMOD events, DataTable rows or string-built paths; those are Unknown,
  and the tool never proposes a deletion.
- Copying .uasset files does not preserve references - they are stored as
  full package paths. Only the Migrate layout produces something Unreal
  can open; the others write a manifest so the graph can be rebuilt.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 17:07:59 +07:00

86 lines
3.0 KiB
C++

// NextGenium 2026. Asset Usage Audit.
#pragma once
#include "CoreMinimal.h"
#include "AssetUsageAuditTypes.h"
#include "AssetTypeFilter.h"
#include "AssetUsageGraph.h"
#include "LevelUsageResolver.h"
class IAssetRegistry;
struct FAssetUsageAuditRequest
{
/** Roots to sweep. Empty means /Game. */
TArray<FString> IncludePackagePaths;
/** Package or content-relative prefixes to skip, e.g. "Content/3rdParty". */
TArray<FString> ExcludePackagePaths;
/** Levels to consider. Empty means every level found. */
TArray<FName> LevelPackages;
/** Type filter applied to the reported rows, not to the traversal. */
FAssetTypeFilter TypeFilter;
/** Scan Config/ and Source/ for path literals the registry cannot see. */
bool bScanIndirectReferences = true;
/** Drop rows for __ExternalActors__ / __ExternalObjects__ packages. */
bool bHideExternalPackages = true;
/** Drop redirector rows; they are plumbing, not content an artist acts on. */
bool bHideRedirectors = true;
/** Report only assets that no level reaches. */
bool bOnlyUnusedAssets = false;
FLevelUsageResolveOptions ResolveOptions;
};
// Exported: CountByVerdict is defined out of line, so the Editor module cannot link without this.
struct ASSETUSAGEAUDITCORE_API FAssetUsageAuditResult
{
TArray<FAssetUsageRow> Rows;
FAssetUsageReportHeader Header;
FAssetUsageGraphStats GraphStats;
FLevelUsageStats LevelStats;
int32 CountByVerdict(EAssetUsageVerdict Verdict) const;
};
/**
* Runs the whole analysis: graph, level reachability, indirect scan, verdicts.
*
* Free of UI and of the editor asset pipeline, so the Slate panel and a future commandlet can
* call exactly the same code and cannot drift apart.
*/
class ASSETUSAGEAUDITCORE_API FAssetUsageAuditor
{
public:
static FAssetUsageAuditResult Run(IAssetRegistry& AssetRegistry, FAssetUsageAuditRequest& Request);
/**
* Classify one asset.
*
* Pure and exposed for testing: the ordering of these rules is the difference between a tool
* people trust and one that tells an artist to delete the GameMode.
*
* @param bReachableFromLevel A level's traversal reached this asset.
* @param bHasReferencers Something in the graph depends on it.
* @param bFoundInConfigOrSource A path literal in Config/ or Source/ named it.
* @param bIsBlindSpot Its type is one the registry cannot track reliably, e.g. FMOD.
*/
static EAssetUsageVerdict ClassifyVerdict(bool bReachableFromLevel, bool bHasReferencers, bool bFoundInConfigOrSource, bool bIsBlindSpot);
/**
* True for asset types whose real usage the Asset Registry cannot see.
*
* FMOD is the concrete case on this project: it resolves events by string path through the
* FMOD Studio runtime, entirely outside the UObject reference graph, so "no referencers"
* carries no information at all for an FMOD asset.
*/
static bool IsRegistryBlindSpot(const FAssetUsageNode& Node);
};