Compare commits
19 Commits
ee1c8bad3e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ea8fa30fda | |||
| 5c201945d8 | |||
| 6c34623476 | |||
| 9f8d77d873 | |||
| d4161cdc77 | |||
| f6bba1bcd0 | |||
| 63c4054aef | |||
| 82fb679da5 | |||
| a0a061656e | |||
| fdd7ba2fad | |||
| 640e940ab4 | |||
| e04f4dac78 | |||
| e92306c797 | |||
| ce998ff963 | |||
| 0363b07b65 | |||
| 12fd622700 | |||
| f3c82d3815 | |||
| 430fc18d5f | |||
| aeb4a1067c |
@@ -5,7 +5,7 @@
|
|||||||
"FriendlyName": "Asset Usage Audit",
|
"FriendlyName": "Asset Usage Audit",
|
||||||
"Description": "Reports which assets are used on which levels, resolving usage transitively through Blueprints and other assets, and handling hard/soft references and One File Per Actor. Exports selected assets and usage reports.",
|
"Description": "Reports which assets are used on which levels, resolving usage transitively through Blueprints and other assets, and handling hard/soft references and One File Per Actor. Exports selected assets and usage reports.",
|
||||||
"Category": "Editor",
|
"Category": "Editor",
|
||||||
"CreatedBy": "Next-Genium",
|
"CreatedBy": "MagentaDolphin",
|
||||||
"CanContainContent": false,
|
"CanContainContent": false,
|
||||||
"EnabledByDefault": true,
|
"EnabledByDefault": true,
|
||||||
"Modules": [
|
"Modules": [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetDeletionScan.h"
|
#include "AssetDeletionScan.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetDependencyClosure.h"
|
#include "AssetDependencyClosure.h"
|
||||||
|
|
||||||
@@ -252,8 +252,8 @@ namespace AssetDependencyClosure
|
|||||||
// comes from a level or from one of its external actor packages - the shape a
|
// comes from a level or from one of its external actor packages - the shape a
|
||||||
// streaming sublevel or a Level Instance actually has. A World named by an ordinary
|
// streaming sublevel or a Level Instance actually has. A World named by an ordinary
|
||||||
// asset is a different location that happens to be mentioned, and following it
|
// asset is a different location that happens to be mentioned, and following it
|
||||||
// exports someone else's map: measured on WP_Main, the chain
|
// exports someone else's map: measured on WP_Example, the chain
|
||||||
// BP_FirstPersonGameMode -> PDA_MenuSystemConfig -> L_MainLevel accounts for 9994
|
// BP_GameMode -> PDA_MenuConfig -> L_Other accounts for 9994
|
||||||
// packages. FLevelUsageResolver applies the same rule; the two must not disagree
|
// packages. FLevelUsageResolver applies the same rule; the two must not disagree
|
||||||
// about the same graph.
|
// about the same graph.
|
||||||
if (IsLevelPackage(Dependency))
|
if (IsLevelPackage(Dependency))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportLayout.h"
|
#include "AssetExportLayout.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportManifest.h"
|
#include "AssetExportManifest.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportNaming.h"
|
#include "AssetExportNaming.h"
|
||||||
#include "AssetUsageAuditCoreModule.h"
|
#include "AssetUsageAuditCoreModule.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetImportScanner.h"
|
#include "AssetImportScanner.h"
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ namespace AssetImportScanner
|
|||||||
/**
|
/**
|
||||||
* Package path implied by a file's position under the import root.
|
* Package path implied by a file's position under the import root.
|
||||||
*
|
*
|
||||||
* Correct for a MirrorTree export, which writes Game/Space/Art/SM_Rock.uasset, and a guess
|
* Correct for a MirrorTree export, which writes Game/Art/SM_Rock.uasset, and a guess
|
||||||
* for anything else. Returns None when the result would not be a valid package name.
|
* for anything else. Returns None when the result would not be a valid package name.
|
||||||
*/
|
*/
|
||||||
FName PackageFromFolderStructure(const FString& RelativePath)
|
FName PackageFromFolderStructure(const FString& RelativePath)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetImporter.h"
|
#include "AssetImporter.h"
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ namespace AssetImporter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This project is Perforce-primary, so an existing target is very likely read-only.
|
// Perforce-primary projects keep unopened files read-only, so an existing target is very likely read-only.
|
||||||
// Without clearing the flag the copy fails with an error that reads like a permissions
|
// Without clearing the flag the copy fails with an error that reads like a permissions
|
||||||
// problem rather than "the file is not checked out".
|
// problem rather than "the file is not checked out".
|
||||||
if (bTargetOnDisk && FileManager.IsReadOnly(*DestinationPath))
|
if (bTargetOnDisk && FileManager.IsReadOnly(*DestinationPath))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetTypeFilter.h"
|
#include "AssetTypeFilter.h"
|
||||||
|
|
||||||
@@ -79,9 +79,9 @@ TArray<FTopLevelAssetPath> GetPresetClassPaths(EAssetTypePreset Preset)
|
|||||||
};
|
};
|
||||||
|
|
||||||
case EAssetTypePreset::Sound:
|
case EAssetTypePreset::Sound:
|
||||||
// FMOD is this project's audio system. FMOD assets are included for completeness, but
|
// Audio middleware (FMOD) is a representative case: its assets are included for
|
||||||
// FMOD resolves events by string path outside the UObject graph, so their usage verdict
|
// completeness, but it resolves events by string path outside the UObject graph, so
|
||||||
// is unreliable by construction - the report marks them Unknown.
|
// the usage verdict is unreliable by construction - the report marks them Unknown.
|
||||||
return {
|
return {
|
||||||
Path(TEXT("/Script/Engine.SoundBase")),
|
Path(TEXT("/Script/Engine.SoundBase")),
|
||||||
Path(TEXT("/Script/FMODStudio.FMODAsset"))
|
Path(TEXT("/Script/FMODStudio.FMODAsset"))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditTypes.h"
|
#include "AssetUsageAuditTypes.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditor.h"
|
#include "AssetUsageAuditor.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageExporter.h"
|
#include "AssetUsageExporter.h"
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ FAssetUsageExporter::FResult FAssetUsageExporter::CopyExpandedPackages(const TAr
|
|||||||
|
|
||||||
const bool bWasPresent = FileManager.FileExists(*DestinationPath);
|
const bool bWasPresent = FileManager.FileExists(*DestinationPath);
|
||||||
|
|
||||||
// This repository is Perforce-primary, so the source .uasset is very often read-only on
|
// Perforce-primary repositories keep unopened files read-only, so the source .uasset is very often read-only on
|
||||||
// disk. Windows CopyFile carries that attribute across, which would make the export folder
|
// disk. Windows CopyFile carries that attribute across, which would make the export folder
|
||||||
// read-only too - and then an Overwrite-policy re-export fails on its own previous output.
|
// read-only too - and then an Overwrite-policy re-export fails on its own previous output.
|
||||||
// Clear the flag on both ends of the problem: before writing over an existing file, and
|
// Clear the flag on both ends of the problem: before writing over an existing file, and
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageGraph.h"
|
#include "AssetUsageGraph.h"
|
||||||
#include "AssetUsageAuditCoreModule.h"
|
#include "AssetUsageAuditCoreModule.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsagePaths.h"
|
#include "AssetUsagePaths.h"
|
||||||
#include "HAL/FileManager.h"
|
#include "HAL/FileManager.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageReportWriter.h"
|
#include "AssetUsageReportWriter.h"
|
||||||
#include "AssetUsageAuditCoreModule.h"
|
#include "AssetUsageAuditCoreModule.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "IndirectReferenceScanner.h"
|
#include "IndirectReferenceScanner.h"
|
||||||
#include "AssetUsageAuditCoreModule.h"
|
#include "AssetUsageAuditCoreModule.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "LevelUsageResolver.h"
|
#include "LevelUsageResolver.h"
|
||||||
|
|
||||||
@@ -243,8 +243,8 @@ void FLevelUsageResolver::TraverseLevel(int32 LevelIndex, const FLevelUsageResol
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reaching another World is NOT automatically a sublevel. An earlier version assumed it
|
// Reaching another World is NOT automatically a sublevel. An earlier version assumed it
|
||||||
// was, and on WP_Main that assumption pulled the whole of L_MainLevel in through
|
// was, and on WP_Example that assumption pulled the whole of L_Other in through
|
||||||
// BP_FirstPersonGameMode -> PDA_MenuSystemConfig: 9994 of 18136 rows belonged to a
|
// BP_GameMode -> PDA_MenuConfig: 9994 of 18136 rows belonged to a
|
||||||
// different map. A crossing only counts when the reference comes from the level package
|
// different map. A crossing only counts when the reference comes from the level package
|
||||||
// itself or from one of its external actor packages, which is the shape a streaming
|
// itself or from one of its external actor packages, which is the shape a streaming
|
||||||
// sublevel or a Level Instance actually has.
|
// sublevel or a Level Instance actually has.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ namespace AssetDependencyClosure
|
|||||||
* Maps reached through an ordinary asset rather than through a level, and therefore not
|
* Maps reached through an ordinary asset rather than through a level, and therefore not
|
||||||
* followed - nor included.
|
* followed - nor included.
|
||||||
*
|
*
|
||||||
* The same rule FLevelUsageResolver applies, and for the same measured reason: on WP_Main
|
* The same rule FLevelUsageResolver applies, and for the same measured reason: on WP_Example
|
||||||
* the chain BP_FirstPersonGameMode -> PDA_MenuSystemConfig -> L_MainLevel drags in 9994
|
* the chain BP_GameMode -> PDA_MenuConfig -> L_Other drags in 9994
|
||||||
* packages belonging to a different map. Without this the export dialog would quote an
|
* packages belonging to a different map. Without this the export dialog would quote an
|
||||||
* honest number for a wrong set.
|
* honest number for a wrong set.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ enum class EExportCollisionPolicy : uint8
|
|||||||
*/
|
*/
|
||||||
struct ASSETUSAGEAUDITCORE_API FAssetUsageRow
|
struct ASSETUSAGEAUDITCORE_API FAssetUsageRow
|
||||||
{
|
{
|
||||||
/** Package name, e.g. /Game/Space/Art/SM_Rock. */
|
/** Package name, e.g. /Game/Art/SM_Rock. */
|
||||||
FName PackageName;
|
FName PackageName;
|
||||||
|
|
||||||
/** Asset name without path. */
|
/** Asset name without path. */
|
||||||
@@ -154,7 +154,7 @@ struct ASSETUSAGEAUDITCORE_API FAssetUsageRow
|
|||||||
*/
|
*/
|
||||||
FTopLevelAssetPath GeneratedClassPath;
|
FTopLevelAssetPath GeneratedClassPath;
|
||||||
|
|
||||||
/** Path relative to the project root, e.g. Content/Space/Art/SM_Rock.uasset. */
|
/** Path relative to the project root, e.g. Content/Art/SM_Rock.uasset. */
|
||||||
FString PathFromProjectRoot;
|
FString PathFromProjectRoot;
|
||||||
|
|
||||||
EAssetUsageVerdict Verdict = EAssetUsageVerdict::Unknown;
|
EAssetUsageVerdict Verdict = EAssetUsageVerdict::Unknown;
|
||||||
@@ -172,7 +172,7 @@ struct ASSETUSAGEAUDITCORE_API FAssetUsageRow
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Human-readable chain explaining the verdict, e.g.
|
* Human-readable chain explaining the verdict, e.g.
|
||||||
* "WP_Main -> __ExternalActors__/.../A2B -> BP_Rock -> SM_Rock".
|
* "WP_Example -> __ExternalActors__/.../A2B -> BP_Rock -> SM_Rock".
|
||||||
* A verdict without a route is unactionable for an artist.
|
* A verdict without a route is unactionable for an artist.
|
||||||
*/
|
*/
|
||||||
FString Route;
|
FString Route;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ namespace AssetUsagePaths
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Package name to a path relative to the project root, as the report requires.
|
* Package name to a path relative to the project root, as the report requires.
|
||||||
* "/Game/Space/Art/SM_Rock" -> "Content/Space/Art/SM_Rock.uasset"
|
* "/Game/Art/SM_Rock" -> "Content/Art/SM_Rock.uasset"
|
||||||
*
|
*
|
||||||
* @param bIsLevel Chooses the .umap extension over .uasset.
|
* @param bIsLevel Chooses the .umap extension over .uasset.
|
||||||
* @return Empty for packages with no project-relative form, e.g. /Script or /Engine.
|
* @return Empty for packages with no project-relative form, e.g. /Script or /Engine.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
struct FIndirectReference
|
struct FIndirectReference
|
||||||
{
|
{
|
||||||
/** Package name the literal resolved to, e.g. /Game/Space/Core/GameModes/BP_FirstPersonGameMode. */
|
/** Package name the literal resolved to, e.g. /Game/Core/GameModes/BP_GameMode. */
|
||||||
FName PackageName;
|
FName PackageName;
|
||||||
|
|
||||||
/** Absolute path of the file the literal was found in. */
|
/** Absolute path of the file the literal was found in. */
|
||||||
@@ -28,7 +28,7 @@ struct FIndirectReference
|
|||||||
* Finds asset paths referenced from text rather than from a package.
|
* Finds asset paths referenced from text rather than from a package.
|
||||||
*
|
*
|
||||||
* This exists because of a concrete, measured failure mode on this project: Config/DefaultEngine.ini
|
* This exists because of a concrete, measured failure mode on this project: Config/DefaultEngine.ini
|
||||||
* names BP_FirstPersonGameMode, BP_MenuSystemGameInstance, RefinedMenuMap and Gyms_Geoda as plain
|
* names BP_GameMode, BP_MenuGameInstance, RefinedMenuMap and Gyms_Geoda as plain
|
||||||
* strings. No asset references them, so the Asset Registry reports zero referencers and a naive
|
* strings. No asset references them, so the Asset Registry reports zero referencers and a naive
|
||||||
* audit calls the project's GameMode unused.
|
* audit calls the project's GameMode unused.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ struct FLevelUsageResolveOptions
|
|||||||
*
|
*
|
||||||
* Off by default, and that default is load-bearing. A map does not only reference its own
|
* Off by default, and that default is load-bearing. A map does not only reference its own
|
||||||
* sublevels: anything it can reach may name an unrelated map, and on this project it does.
|
* sublevels: anything it can reach may name an unrelated map, and on this project it does.
|
||||||
* WP_Main -> BP_FirstPersonGameMode -> PDA_MenuSystemConfig -> L_MainLevel drags in the whole
|
* WP_Example -> BP_GameMode -> PDA_MenuConfig -> L_Other drags in the whole
|
||||||
* of L_MainLevel, which measured at 9994 of WP_Main's 18136 rows - two thirds of the answer
|
* of L_Other, which measured at 9994 of WP_Example's 18136 rows - two thirds of the answer
|
||||||
* was another level's content.
|
* was another level's content.
|
||||||
*
|
*
|
||||||
* With this off, a foreign map is still reported as referenced, but its contents are attributed
|
* With this off, a foreign map is still reported as referenced, but its contents are attributed
|
||||||
@@ -72,7 +72,7 @@ struct FLevelUsageStats
|
|||||||
* Foreign maps referenced but deliberately not expanded into.
|
* Foreign maps referenced but deliberately not expanded into.
|
||||||
*
|
*
|
||||||
* A non-zero value here is the amount of another level's content that would otherwise have
|
* A non-zero value here is the amount of another level's content that would otherwise have
|
||||||
* been attributed to this one. Worth surfacing: on WP_Main it was two thirds of the report.
|
* been attributed to this one. Worth surfacing: on WP_Example it was two thirds of the report.
|
||||||
*/
|
*/
|
||||||
int32 ForeignLevelsNotExpanded = 0;
|
int32 ForeignLevelsNotExpanded = 0;
|
||||||
double ScanLevelAssetsSeconds = 0.0;
|
double ScanLevelAssetsSeconds = 0.0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
using UnrealBuildTool;
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditEditorModule.h"
|
#include "AssetUsageAuditEditorModule.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditLibrary.h"
|
#include "AssetUsageAuditLibrary.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditSettings.h"
|
#include "AssetUsageAuditSettings.h"
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ UAssetUsageAuditSettings::UAssetUsageAuditSettings()
|
|||||||
// third-party, so leaving these in makes the first run unreadable for an artist looking for
|
// third-party, so leaving these in makes the first run unreadable for an artist looking for
|
||||||
// their own content.
|
// their own content.
|
||||||
//
|
//
|
||||||
// Deliberately NOT excluded, despite being a bought pack: Content/MenuSystemPro. It supplies
|
// Deliberately NOT excluded, despite being a bought pack: Content/a purchased UI pack. It supplies
|
||||||
// BP_MenuSystemGameInstance, which DefaultEngine.ini names as the project's GameInstance.
|
// BP_MenuGameInstance, which DefaultEngine.ini names as the project's GameInstance.
|
||||||
// Excluding it would hide a load-bearing asset from every report.
|
// Excluding it would hide a load-bearing asset from every report.
|
||||||
ExcludedPackagePaths = {
|
ExcludedPackagePaths = {
|
||||||
TEXT("Content/3rdParty"),
|
TEXT("Content/3rdParty"),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditUserSettings.h"
|
#include "AssetUsageAuditUserSettings.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "SAssetDeleteDialog.h"
|
#include "SAssetDeleteDialog.h"
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ public:
|
|||||||
return SNew(STextBlock)
|
return SNew(STextBlock)
|
||||||
.Text(LOCTEXT("ReadOnly", "read-only on disk"))
|
.Text(LOCTEXT("ReadOnly", "read-only on disk"))
|
||||||
.ColorAndOpacity(FSlateColor(CautionColor))
|
.ColorAndOpacity(FSlateColor(CautionColor))
|
||||||
.ToolTipText(LOCTEXT("ReadOnlyTip", "The file is not writable. This project is Perforce-primary, so an unopened file is read-only and the delete will fail on it. Check it out first."));
|
.ToolTipText(LOCTEXT("ReadOnlyTip", "The file is not writable. Perforce-primary projects keep unopened files read-only, so an unopened file is read-only and the delete will fail on it. Check it out first."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return SNew(STextBlock)
|
return SNew(STextBlock)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "SAssetExportDialog.h"
|
#include "SAssetExportDialog.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "SAssetImportDialog.h"
|
#include "SAssetImportDialog.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "SAssetUsageAuditPanel.h"
|
#include "SAssetUsageAuditPanel.h"
|
||||||
|
|
||||||
@@ -375,7 +375,7 @@ void SAssetUsageAuditPanel::Construct(const FArguments& InArgs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Name the level when there is one, count them when there are several:
|
// Name the level when there is one, count them when there are several:
|
||||||
// a button reading "WP_Main" is worth more than one reading "1 level".
|
// a button reading "WP_Example" is worth more than one reading "1 level".
|
||||||
if (LevelFilters.Num() == 1)
|
if (LevelFilters.Num() == 1)
|
||||||
{
|
{
|
||||||
return FText::FromString(FPackageName::GetShortName(*LevelFilters.CreateConstIterator()));
|
return FText::FromString(FPackageName::GetShortName(*LevelFilters.CreateConstIterator()));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Run a full audit and write JSON + CSV reports.
|
* Run a full audit and write JSON + CSV reports.
|
||||||
*
|
*
|
||||||
* @param LevelPackageNames Levels to analyse, e.g. "/Game/Space/Maps/WP_Main". Empty means all.
|
* @param LevelPackageNames Levels to analyse, e.g. "/Game/Maps/WP_Example". Empty means all.
|
||||||
* @param OutputDirectory Destination. Empty uses the configured default.
|
* @param OutputDirectory Destination. Empty uses the configured default.
|
||||||
* @param OutReportPath Absolute path of the JSON report on success.
|
* @param OutReportPath Absolute path of the JSON report on success.
|
||||||
* @return true when both reports were written.
|
* @return true when both reports were written.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
/**
|
/**
|
||||||
* Team-shared configuration, written to Config/DefaultEditor.ini.
|
* Team-shared configuration, written to Config/DefaultEditor.ini.
|
||||||
*
|
*
|
||||||
* That file is Perforce-tracked (only Plugins/** is p4-ignored), so the exclusion list and the
|
* That file is normally under version control, so the exclusion list and the
|
||||||
* type presets are reviewable and shared. Per-user state - last filter, column layout - belongs
|
* type presets are reviewable and shared. Per-user state - last filter, column layout - belongs
|
||||||
* in UAssetUsageAuditUserSettings instead, which stays in Saved/Config.
|
* in UAssetUsageAuditUserSettings instead, which stays in Saved/Config.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
using UnrealBuildTool;
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "Modules/ModuleManager.h"
|
#include "Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetDeletionScan.h"
|
#include "AssetDeletionScan.h"
|
||||||
|
|
||||||
@@ -399,7 +399,7 @@ void AssetDeletionScanSpec::Define()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This project is Perforce-primary, so read-only is the normal state of an unopened
|
// Perforce-primary projects keep unopened files read-only, so read-only is the normal state of an unopened
|
||||||
// file. Reporting it up front is the difference between a warning and a delete that
|
// file. Reporting it up front is the difference between a warning and a delete that
|
||||||
// fails halfway through the selection.
|
// fails halfway through the selection.
|
||||||
TestEqual(TEXT("matches the filesystem"),
|
TestEqual(TEXT("matches the filesystem"),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetDependencyClosure.h"
|
#include "AssetDependencyClosure.h"
|
||||||
|
|
||||||
@@ -232,8 +232,8 @@ void AssetDependencyClosureSpec::Define()
|
|||||||
|
|
||||||
It("does not follow a map that an ordinary asset merely mentions", [this]()
|
It("does not follow a map that an ordinary asset merely mentions", [this]()
|
||||||
{
|
{
|
||||||
// The defect this guards against: exporting WP_Main pulled in the whole of L_MainLevel
|
// The defect this guards against: exporting WP_Example pulled in the whole of L_Other
|
||||||
// through BP_FirstPersonGameMode -> PDA_MenuSystemConfig, 9994 packages belonging to a
|
// through BP_GameMode -> PDA_MenuConfig, 9994 packages belonging to a
|
||||||
// different location. FLevelUsageResolver already refused that crossing; the closure
|
// different location. FLevelUsageResolver already refused that crossing; the closure
|
||||||
// did not, so the two disagreed about the same graph.
|
// did not, so the two disagreed about the same graph.
|
||||||
IAssetRegistry& Registry = ReadyRegistry();
|
IAssetRegistry& Registry = ReadyRegistry();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageExporter.h"
|
#include "AssetUsageExporter.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportLayout.h"
|
#include "AssetExportLayout.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportManifest.h"
|
#include "AssetExportManifest.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetExportNaming.h"
|
#include "AssetExportNaming.h"
|
||||||
#include "AssetUsageAuditTypes.h"
|
#include "AssetUsageAuditTypes.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetImportScanner.h"
|
#include "AssetImportScanner.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageExporter.h"
|
#include "AssetUsageExporter.h"
|
||||||
#include "AssetExportNaming.h"
|
#include "AssetExportNaming.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageExporter.h"
|
#include "AssetUsageExporter.h"
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ BEGIN_DEFINE_SPEC(AssetUsageExporterLiveSpec,
|
|||||||
/**
|
/**
|
||||||
* The OFPA level with the fewest external actors.
|
* The OFPA level with the fewest external actors.
|
||||||
*
|
*
|
||||||
* Smallest on purpose: WP_Main owns roughly 16000 external packages and copying those in a test
|
* Smallest on purpose: WP_Example owns roughly 16000 external packages and copying those in a test
|
||||||
* would move gigabytes. Search stops as soon as something small enough turns up.
|
* would move gigabytes. Search stops as soon as something small enough turns up.
|
||||||
*/
|
*/
|
||||||
static FName FindSmallestOfpaLevel(int32& OutExternalCount)
|
static FName FindSmallestOfpaLevel(int32& OutExternalCount)
|
||||||
@@ -254,7 +254,7 @@ void AssetUsageExporterLiveSpec::Define()
|
|||||||
|
|
||||||
It("leaves the exported file writable even when the source is read-only", [this]()
|
It("leaves the exported file writable even when the source is read-only", [this]()
|
||||||
{
|
{
|
||||||
// This project is Perforce-primary, so unopened files are read-only on disk and Windows
|
// Perforce-primary projects keep unopened files read-only, so unopened files are read-only on disk and Windows
|
||||||
// CopyFile carries the attribute across. A read-only export folder is useless to an
|
// CopyFile carries the attribute across. A read-only export folder is useless to an
|
||||||
// artist and breaks the exporter's own Overwrite policy on the next run.
|
// artist and breaks the exporter's own Overwrite policy on the next run.
|
||||||
const FName Package = FindOrdinaryAssetPackage();
|
const FName Package = FindOrdinaryAssetPackage();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageGraph.h"
|
#include "AssetUsageGraph.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsagePaths.h"
|
#include "AssetUsagePaths.h"
|
||||||
#include "Misc/AutomationTest.h"
|
#include "Misc/AutomationTest.h"
|
||||||
@@ -16,27 +16,27 @@ void AssetUsagePathsSpec::Define()
|
|||||||
{
|
{
|
||||||
It("detects an OFPA external actor package", [this]()
|
It("detects an OFPA external actor package", [this]()
|
||||||
{
|
{
|
||||||
// Real shape from this project: /Game/__ExternalActors__/Space/Maps/WP_Main/8/AB/<22 chars>
|
// Real shape from a live project: /Game/__ExternalActors__/Maps/WP_Example/8/AB/<22 chars>
|
||||||
TestTrue(TEXT("external actor"),
|
TestTrue(TEXT("external actor"),
|
||||||
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/__ExternalActors__/Space/Maps/WP_Main/8/AB/QWERTYUIOPASDFGHJKLZXC"))));
|
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/__ExternalActors__/Maps/WP_Example/8/AB/QWERTYUIOPASDFGHJKLZXC"))));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("detects an external objects package", [this]()
|
It("detects an external objects package", [this]()
|
||||||
{
|
{
|
||||||
TestTrue(TEXT("external object"),
|
TestTrue(TEXT("external object"),
|
||||||
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/__ExternalObjects__/Space/Maps/L_3i_Hub/1/2A/ABC"))));
|
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/__ExternalObjects__/Maps/L_3i_Hub/1/2A/ABC"))));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("does not flag an ordinary content package", [this]()
|
It("does not flag an ordinary content package", [this]()
|
||||||
{
|
{
|
||||||
TestFalse(TEXT("ordinary asset"),
|
TestFalse(TEXT("ordinary asset"),
|
||||||
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/Space/Art/SM_Rock"))));
|
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/Art/SM_Rock"))));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("does not flag a level that merely lives next to external actors", [this]()
|
It("does not flag a level that merely lives next to external actors", [this]()
|
||||||
{
|
{
|
||||||
TestFalse(TEXT("the level itself is not external"),
|
TestFalse(TEXT("the level itself is not external"),
|
||||||
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/Space/Maps/WP_Main"))));
|
AssetUsagePaths::IsExternalPackage(FName(TEXT("/Game/Maps/WP_Example"))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ void AssetUsagePathsSpec::Define()
|
|||||||
{
|
{
|
||||||
It("flags /Script packages", [this]()
|
It("flags /Script packages", [this]()
|
||||||
{
|
{
|
||||||
TestTrue(TEXT("script"), AssetUsagePaths::IsScriptPackage(FName(TEXT("/Script/Space"))));
|
TestTrue(TEXT("script"), AssetUsagePaths::IsScriptPackage(FName(TEXT("/Script/MyGame"))));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("does not flag content that starts with the same letters", [this]()
|
It("does not flag content that starts with the same letters", [this]()
|
||||||
@@ -123,7 +123,7 @@ void AssetUsagePathsSpec::Define()
|
|||||||
{
|
{
|
||||||
const TArray<FString> Excluded;
|
const TArray<FString> Excluded;
|
||||||
TestFalse(TEXT("nothing excluded"),
|
TestFalse(TEXT("nothing excluded"),
|
||||||
AssetUsagePaths::IsPathExcluded(FName(TEXT("/Game/Space/Art/SM_Rock")), Excluded));
|
AssetUsagePaths::IsPathExcluded(FName(TEXT("/Game/Art/SM_Rock")), Excluded));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -132,21 +132,21 @@ void AssetUsagePathsSpec::Define()
|
|||||||
It("converts an asset package", [this]()
|
It("converts an asset package", [this]()
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("asset"),
|
TestEqual(TEXT("asset"),
|
||||||
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/Space/Art/SM_Rock")), false),
|
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/Art/SM_Rock")), false),
|
||||||
FString(TEXT("Content/Space/Art/SM_Rock.uasset")));
|
FString(TEXT("Content/Art/SM_Rock.uasset")));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("uses the umap extension for levels", [this]()
|
It("uses the umap extension for levels", [this]()
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("level"),
|
TestEqual(TEXT("level"),
|
||||||
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/Space/Maps/WP_Main")), true),
|
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/Maps/WP_Example")), true),
|
||||||
FString(TEXT("Content/Space/Maps/WP_Main.umap")));
|
FString(TEXT("Content/Maps/WP_Example.umap")));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("returns empty for script and engine packages rather than inventing a path", [this]()
|
It("returns empty for script and engine packages rather than inventing a path", [this]()
|
||||||
{
|
{
|
||||||
TestTrue(TEXT("script"),
|
TestTrue(TEXT("script"),
|
||||||
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Script/Space")), false).IsEmpty());
|
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Script/MyGame")), false).IsEmpty());
|
||||||
TestTrue(TEXT("engine"),
|
TestTrue(TEXT("engine"),
|
||||||
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Engine/BasicShapes/Cube")), false).IsEmpty());
|
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Engine/BasicShapes/Cube")), false).IsEmpty());
|
||||||
});
|
});
|
||||||
@@ -154,8 +154,8 @@ void AssetUsagePathsSpec::Define()
|
|||||||
It("converts an external actor package, which is still under /Game", [this]()
|
It("converts an external actor package, which is still under /Game", [this]()
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("external actor"),
|
TestEqual(TEXT("external actor"),
|
||||||
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/__ExternalActors__/Space/Maps/WP_Main/8/AB/ABC")), false),
|
AssetUsagePaths::ToProjectRelativePath(FName(TEXT("/Game/__ExternalActors__/Maps/WP_Example/8/AB/ABC")), false),
|
||||||
FString(TEXT("Content/__ExternalActors__/Space/Maps/WP_Main/8/AB/ABC.uasset")));
|
FString(TEXT("Content/__ExternalActors__/Maps/WP_Example/8/AB/ABC.uasset")));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageReportWriter.h"
|
#include "AssetUsageReportWriter.h"
|
||||||
#include "AssetUsageAuditor.h"
|
#include "AssetUsageAuditor.h"
|
||||||
@@ -17,7 +17,7 @@ namespace
|
|||||||
FAssetUsageAuditResult Result;
|
FAssetUsageAuditResult Result;
|
||||||
|
|
||||||
Result.Header.GeneratedAt = FDateTime(2026, 9, 2, 12, 0, 0);
|
Result.Header.GeneratedAt = FDateTime(2026, 9, 2, 12, 0, 0);
|
||||||
Result.Header.ProjectName = TEXT("Space");
|
Result.Header.ProjectName = TEXT("MyProject");
|
||||||
Result.Header.EngineVersion = TEXT("5.6.1");
|
Result.Header.EngineVersion = TEXT("5.6.1");
|
||||||
Result.Header.ToolVersion = TEXT("0.1");
|
Result.Header.ToolVersion = TEXT("0.1");
|
||||||
Result.Header.LevelsScanned = 2;
|
Result.Header.LevelsScanned = 2;
|
||||||
@@ -25,22 +25,22 @@ namespace
|
|||||||
Result.Header.AppliedFilters.Add(TEXT("Types: StaticMesh"));
|
Result.Header.AppliedFilters.Add(TEXT("Types: StaticMesh"));
|
||||||
|
|
||||||
FAssetUsageRow& Used = Result.Rows.AddDefaulted_GetRef();
|
FAssetUsageRow& Used = Result.Rows.AddDefaulted_GetRef();
|
||||||
Used.PackageName = FName(TEXT("/Game/Space/Art/SM_Rock"));
|
Used.PackageName = FName(TEXT("/Game/Art/SM_Rock"));
|
||||||
Used.AssetName = FName(TEXT("SM_Rock"));
|
Used.AssetName = FName(TEXT("SM_Rock"));
|
||||||
Used.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
Used.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
||||||
Used.PathFromProjectRoot = TEXT("Content/Space/Art/SM_Rock.uasset");
|
Used.PathFromProjectRoot = TEXT("Content/Art/SM_Rock.uasset");
|
||||||
Used.Verdict = EAssetUsageVerdict::UsedOnLevel;
|
Used.Verdict = EAssetUsageVerdict::UsedOnLevel;
|
||||||
Used.Levels = { FName(TEXT("/Game/Space/Maps/WP_Main")), FName(TEXT("/Game/Space/Maps/L_3i_Hub")) };
|
Used.Levels = { FName(TEXT("/Game/Maps/WP_Example")), FName(TEXT("/Game/Maps/L_3i_Hub")) };
|
||||||
Used.HardReferenceCount = 3;
|
Used.HardReferenceCount = 3;
|
||||||
Used.SoftReferenceCount = 1;
|
Used.SoftReferenceCount = 1;
|
||||||
Used.Provenance = EAssetUsageProvenance::HardReference | EAssetUsageProvenance::ExternalActor;
|
Used.Provenance = EAssetUsageProvenance::HardReference | EAssetUsageProvenance::ExternalActor;
|
||||||
Used.Route = TEXT("WP_Main -> BP_Rock -> SM_Rock");
|
Used.Route = TEXT("WP_Example -> BP_Rock -> SM_Rock");
|
||||||
|
|
||||||
FAssetUsageRow& Unused = Result.Rows.AddDefaulted_GetRef();
|
FAssetUsageRow& Unused = Result.Rows.AddDefaulted_GetRef();
|
||||||
Unused.PackageName = FName(TEXT("/Game/Space/Art/SM_Orphan"));
|
Unused.PackageName = FName(TEXT("/Game/Art/SM_Orphan"));
|
||||||
Unused.AssetName = FName(TEXT("SM_Orphan"));
|
Unused.AssetName = FName(TEXT("SM_Orphan"));
|
||||||
Unused.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
Unused.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
||||||
Unused.PathFromProjectRoot = TEXT("Content/Space/Art/SM_Orphan.uasset");
|
Unused.PathFromProjectRoot = TEXT("Content/Art/SM_Orphan.uasset");
|
||||||
Unused.Verdict = EAssetUsageVerdict::Unreferenced;
|
Unused.Verdict = EAssetUsageVerdict::Unreferenced;
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
@@ -123,7 +123,7 @@ void AssetUsageReportWriterSpec::Define()
|
|||||||
const FString Csv = FAssetUsageReportWriter::BuildCsv(Sample, Options);
|
const FString Csv = FAssetUsageReportWriter::BuildCsv(Sample, Options);
|
||||||
|
|
||||||
TestTrue(TEXT("levels joined"),
|
TestTrue(TEXT("levels joined"),
|
||||||
Csv.Contains(TEXT("/Game/Space/Maps/WP_Main|/Game/Space/Maps/L_3i_Hub")));
|
Csv.Contains(TEXT("/Game/Maps/WP_Example|/Game/Maps/L_3i_Hub")));
|
||||||
});
|
});
|
||||||
|
|
||||||
It("carries the verdict verbatim", [this]()
|
It("carries the verdict verbatim", [this]()
|
||||||
@@ -172,7 +172,7 @@ void AssetUsageReportWriterSpec::Define()
|
|||||||
TestTrue(TEXT("header object"), Root->TryGetObjectField(TEXT("header"), Header));
|
TestTrue(TEXT("header object"), Root->TryGetObjectField(TEXT("header"), Header));
|
||||||
if (Header)
|
if (Header)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("project"), (*Header)->GetStringField(TEXT("project")), FString(TEXT("Space")));
|
TestEqual(TEXT("project"), (*Header)->GetStringField(TEXT("project")), FString(TEXT("MyProject")));
|
||||||
TestEqual(TEXT("levelsScanned"), (*Header)->GetIntegerField(TEXT("levelsScanned")), 2);
|
TestEqual(TEXT("levelsScanned"), (*Header)->GetIntegerField(TEXT("levelsScanned")), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditor.h"
|
#include "AssetUsageAuditor.h"
|
||||||
#include "AssetUsageGraph.h"
|
#include "AssetUsageGraph.h"
|
||||||
@@ -45,7 +45,7 @@ void AssetUsageVerdictSpec::Define()
|
|||||||
|
|
||||||
It("reports ReferencedFromConfigOrSource when only a text literal names it", [this]()
|
It("reports ReferencedFromConfigOrSource when only a text literal names it", [this]()
|
||||||
{
|
{
|
||||||
// This is the BP_FirstPersonGameMode case: no asset references it, only
|
// This is the BP_GameMode case: no asset references it, only
|
||||||
// Config/DefaultEngine.ini does. Reporting it Unreferenced would be a real defect.
|
// Config/DefaultEngine.ini does. Reporting it Unreferenced would be a real defect.
|
||||||
const EAssetUsageVerdict Verdict = FAssetUsageAuditor::ClassifyVerdict(false, false, true, false);
|
const EAssetUsageVerdict Verdict = FAssetUsageAuditor::ClassifyVerdict(false, false, true, false);
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ void AssetUsageVerdictSpec::Define()
|
|||||||
{
|
{
|
||||||
// A level depends on the whole table, so every row looks used even when none is.
|
// A level depends on the whole table, so every row looks used even when none is.
|
||||||
FAssetUsageNode Node;
|
FAssetUsageNode Node;
|
||||||
Node.PackageName = FName(TEXT("/Game/Space/Data/DT_Weapons"));
|
Node.PackageName = FName(TEXT("/Game/Data/DT_Weapons"));
|
||||||
Node.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.DataTable"));
|
Node.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.DataTable"));
|
||||||
|
|
||||||
TestTrue(TEXT("DataTable is a blind spot"), FAssetUsageAuditor::IsRegistryBlindSpot(Node));
|
TestTrue(TEXT("DataTable is a blind spot"), FAssetUsageAuditor::IsRegistryBlindSpot(Node));
|
||||||
@@ -135,7 +135,7 @@ void AssetUsageVerdictSpec::Define()
|
|||||||
It("does not flag an ordinary static mesh", [this]()
|
It("does not flag an ordinary static mesh", [this]()
|
||||||
{
|
{
|
||||||
FAssetUsageNode Node;
|
FAssetUsageNode Node;
|
||||||
Node.PackageName = FName(TEXT("/Game/Space/Art/SM_Rock"));
|
Node.PackageName = FName(TEXT("/Game/Art/SM_Rock"));
|
||||||
Node.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
Node.ClassPath = FTopLevelAssetPath(TEXT("/Script/Engine.StaticMesh"));
|
||||||
|
|
||||||
TestFalse(TEXT("StaticMesh is not a blind spot"), FAssetUsageAuditor::IsRegistryBlindSpot(Node));
|
TestFalse(TEXT("StaticMesh is not a blind spot"), FAssetUsageAuditor::IsRegistryBlindSpot(Node));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "IndirectReferenceScanner.h"
|
#include "IndirectReferenceScanner.h"
|
||||||
#include "Misc/AutomationTest.h"
|
#include "Misc/AutomationTest.h"
|
||||||
@@ -28,70 +28,70 @@ void IndirectReferenceScannerSpec::Define()
|
|||||||
It("finds the GameMode from DefaultEngine.ini and strips the _C class suffix", [this]()
|
It("finds the GameMode from DefaultEngine.ini and strips the _C class suffix", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(
|
const TArray<FString> Found = Extract(
|
||||||
TEXT("GlobalDefaultGameMode=/Game/Space/Core/GameModes/BP_FirstPersonGameMode.BP_FirstPersonGameMode_C"));
|
TEXT("GlobalDefaultGameMode=/Game/Core/GameModes/BP_GameMode.BP_GameMode_C"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Space/Core/GameModes/BP_FirstPersonGameMode")));
|
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Core/GameModes/BP_GameMode")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
It("finds the GameInstance from MenuSystemPro", [this]()
|
It("finds the GameInstance from a purchased pack", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(
|
const TArray<FString> Found = Extract(
|
||||||
TEXT("GameInstanceClass=/Game/MenuSystemPro/Blueprints/GameFramework/BP_MenuSystemGameInstance.BP_MenuSystemGameInstance_C"));
|
TEXT("GameInstanceClass=/Game/UIPack/Blueprints/GameFramework/BP_MenuGameInstance.BP_MenuGameInstance_C"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0],
|
TestEqual(TEXT("package"), Found[0],
|
||||||
FString(TEXT("/Game/MenuSystemPro/Blueprints/GameFramework/BP_MenuSystemGameInstance")));
|
FString(TEXT("/Game/UIPack/Blueprints/GameFramework/BP_MenuGameInstance")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
It("finds a map reference with an object suffix", [this]()
|
It("finds a map reference with an object suffix", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(TEXT("EditorStartupMap=/Game/Space/Maps/Gyms/Gyms_Geoda.Gyms_Geoda"));
|
const TArray<FString> Found = Extract(TEXT("EditorStartupMap=/Game/Maps/Gyms/Gyms_Geoda.Gyms_Geoda"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Space/Maps/Gyms/Gyms_Geoda")));
|
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Maps/Gyms/Gyms_Geoda")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
It("finds a bare package path with no object suffix", [this]()
|
It("finds a bare package path with no object suffix", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(TEXT("GameDefaultMap=/Game/Space/Maps/RefinedMenuMap"));
|
const TArray<FString> Found = Extract(TEXT("GameDefaultMap=/Game/Maps/RefinedMenuMap"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Space/Maps/RefinedMenuMap")));
|
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Maps/RefinedMenuMap")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
It("strips surrounding quotes and parentheses from an ini struct value", [this]()
|
It("strips surrounding quotes and parentheses from an ini struct value", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(TEXT("+DirectoriesToAlwaysCook=(Path=\"/Game/Space/Art\")"));
|
const TArray<FString> Found = Extract(TEXT("+DirectoriesToAlwaysCook=(Path=\"/Game/Art\")"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Space/Art")));
|
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Art")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
It("handles a C++ literal wrapped in TEXT()", [this]()
|
It("handles a C++ literal wrapped in TEXT()", [this]()
|
||||||
{
|
{
|
||||||
const TArray<FString> Found = Extract(
|
const TArray<FString> Found = Extract(
|
||||||
TEXT("static const FSoftObjectPath Path(TEXT(\"/Game/Space/Art/SM_Rock\"));"));
|
TEXT("static const FSoftObjectPath Path(TEXT(\"/Game/Art/SM_Rock\"));"));
|
||||||
|
|
||||||
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
TestEqual(TEXT("one hit"), Found.Num(), 1);
|
||||||
if (Found.Num() == 1)
|
if (Found.Num() == 1)
|
||||||
{
|
{
|
||||||
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Space/Art/SM_Rock")));
|
TestEqual(TEXT("package"), Found[0], FString(TEXT("/Game/Art/SM_Rock")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsagePaths.h"
|
#include "AssetUsagePaths.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// NextGenium 2026. Asset Usage Audit.
|
// MagentaDolphin 2026. Asset Usage Audit.
|
||||||
|
|
||||||
#include "AssetUsageAuditTypes.h"
|
#include "AssetUsageAuditTypes.h"
|
||||||
#include "AssetUsagePaths.h"
|
#include "AssetUsagePaths.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user