Files
AssetUsageAudit/Source/AssetUsageAuditCore/Public/AssetDependencyClosure.h
T
Admin 000cc5735c fix: correct authorship attribution to MagentaDolphin
The plugin was authored outside studio work but carried NextGenium
attribution in the .uplugin descriptor and in every source header.

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

129 lines
5.3 KiB
C++

// MagentaDolphin 2026. Asset Usage Audit.
#pragma once
#include "CoreMinimal.h"
class IAssetRegistry;
/**
* Everything a set of packages needs in order to be usable somewhere else.
*
* Exists because "export the ticked meshes" without this produces meshes that open pink: the
* package file of a StaticMesh holds no materials and no textures, only references to them. The
* same applies to a Niagara system and its sprites, and to a Blueprint and everything it spawns.
*
* Deliberately a separate step rather than something FAssetUsageExporter does internally. The
* exporter stays a dumb file copier that needs no Asset Registry and is testable without one, and
* the caller keeps the chance to show "40 ticked, 380 will be written" before anything is copied -
* which matters, because that ratio surprises people.
*
* This is NOT IAssetTools::MigratePackages. Migrate walks the same closure but also decides where
* files go, prompts, and cannot be told to stop at a folder boundary. Here the traversal is bounded
* by the same exclusion list the audit uses, and engine content is left out by default because the
* destination project already has it.
*/
namespace AssetDependencyClosure
{
struct FOptions
{
/**
* Package prefixes not to descend into, in either /Game/... or Content/... form.
* Normally the audit's own exclusion list, so an export cannot pull in content the report
* deliberately ignores.
*/
TArray<FString> ExcludePackagePaths;
/**
* Include /Engine and /Temp packages in the result.
*
* Off by default. A mesh using DefaultMaterial genuinely depends on /Engine content, but
* copying it into the export folder is almost never what someone wants - the destination
* project ships the same file, and overwriting it there is worse than useless.
*/
bool bIncludeEnginePackages = false;
/**
* Stop after this many hops from a seed. Zero means no limit.
*
* A limit is a blunt instrument and changes the answer rather than just shortening it, so it
* is off by default. It exists for the "just the materials, not the whole graph" case.
*/
int32 MaxDepth = 0;
/** Called as (Visited, Queued). Return false to stop; the partial result is still returned. */
TFunction<bool(int32, int32)> OnProgress;
};
// Exported: Summarise is defined out of line, so the Editor module cannot link without this.
struct ASSETUSAGEAUDITCORE_API FStats
{
/** Seeds that were valid package names to begin with. */
int32 SeedCount = 0;
/** Size of the returned array, seeds included. */
int32 TotalCount = 0;
/** Reached but dropped, per reason. Kept apart so a surprising result can be explained. */
int32 SkippedScript = 0;
int32 SkippedEngine = 0;
int32 SkippedExcluded = 0;
/**
* Maps reached through an ordinary asset rather than through a level, and therefore not
* followed - nor included.
*
* The same rule FLevelUsageResolver applies, and for the same measured reason: on WP_Main
* the chain BP_FirstPersonGameMode -> PDA_MenuSystemConfig -> L_MainLevel drags in 9994
* packages belonging to a different map. Without this the export dialog would quote an
* honest number for a wrong set.
*
* Excluded rather than merely not expanded, unlike in the audit. The exporter expands any
* .umap it is handed into its One File Per Actor packages, so including the map file would
* pull the foreign level's contents back in through the exporter instead of the closure.
*/
int32 ForeignLevelsSkipped = 0;
/** Deepest hop count actually reached. Tells you whether MaxDepth did anything. */
int32 DeepestHop = 0;
bool bStoppedEarly = false;
FString Summarise() const;
};
/**
* Seeds plus everything they depend on, transitively.
*
* Seeds come first and in their original order, so a caller can still tell what was asked for.
* The traversal uses AssetUsageAudit::MakeTraversalQuery() - package category, no requirements -
* for the same reason the audit does: a Hard-only query silently drops every soft edge, which
* includes every One File Per Actor package and every TSoftObjectPtr a Blueprint resolves.
*/
/** One ticked asset and everything it pulls in, the seed first. */
struct FSeedClosure
{
FName Seed;
TArray<FName> Packages;
};
/**
* Closure per seed, rather than one closure over all of them.
*
* Needed by the folder-per-asset layout: a texture shared by forty meshes has to appear in all
* forty folders, and the flat Gather deliberately returns it once. Costs one traversal per
* seed, so it is the slower call by construction - use Gather when a single list will do.
*/
ASSETUSAGEAUDITCORE_API TArray<FSeedClosure> GatherPerSeed(
IAssetRegistry& Registry,
const TArray<FName>& Seeds,
const FOptions& Options,
FStats& OutStats);
ASSETUSAGEAUDITCORE_API TArray<FName> Gather(
IAssetRegistry& Registry,
const TArray<FName>& Seeds,
const FOptions& Options,
FStats& OutStats);
}