EM_Task/UnrealEd/Public/Commandlets/AssetRegistryGenerator.h

406 lines
17 KiB
C
Raw Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Paths.h"
#include "AssetData.h"
#include "Misc/AssetRegistryInterface.h"
#include "AssetRegistryState.h"
#include "UObject/UObjectHash.h"
class FSandboxPlatformFile;
class IAssetRegistry;
class ITargetPlatform;
class IChunkDataGenerator;
class UChunkDependencyInfo;
struct FChunkDependencyTreeNode;
/**
* Helper class for generating streaming install manifests
*/
class FAssetRegistryGenerator
{
public:
/**
* Constructor
*/
FAssetRegistryGenerator(const ITargetPlatform* InPlatform);
/**
* Destructor
*/
~FAssetRegistryGenerator();
/**
* Initializes manifest generator - creates manifest lists, hooks up delegates.
*/
void Initialize(const TArray<FName>& StartupPackages);
const ITargetPlatform* GetTargetPlatform() const { return TargetPlatform; }
/**
* Loads asset registry from a previous run that is used for iterative or DLC cooking
*/
bool LoadPreviousAssetRegistry(const FString& Filename);
/**
* Computes differences between the previous asset registry and the current one
*
* @param ModifiedPackages list of packages which existed before and now, but need to be recooked
* @param NewPackages list of packages that did not exist before, but exist now
* @param RemovedPackages list of packages that existed before, but do not any more
* @param IdenticalCookedPackages list of cooked packages that have not changed
* @param IdenticalUncookedPackages list of uncooked packages that have not changed. These were filtered out by platform or editor only
* @param bRecurseModifications if true, modified packages are recursed to X in X->Y->Z chains. Otherwise, only Y and Z are seen as modified
* @param bRecurseModifications if true, modified script/c++ packages are recursed, if false only asset references are recursed
*/
void ComputePackageDifferences(TSet<FName>& ModifiedPackages, TSet<FName>& NewPackages, TSet<FName>& RemovedPackages, TSet<FName>& IdenticalCookedPackages, TSet<FName>& IdenticalUncookedPackages, bool bRecurseModifications, bool bRecurseScriptModifications);
/**
* Tracks packages that were kept from a previous cook.
* Updates the current asset registry from the previous one for all kept packages.
*/
void UpdateKeptPackages(const TArray<FName>& InKeptPackages);
/**
* GenerateChunkManifest
* generate chunk manifest for the packages passed in using the asset registry to determine dependencies
*
* @param CookedPackages list of packages which were cooked
* @param DevelopmentOnlyPackages list of packages that were specifically not cooked, but to add to the development asset registry
* @param InSandboxFile sandbox to load/save data
* @param bGenerateStreamingInstallManifest should we build a streaming install manifest
*/
void BuildChunkManifest(const TSet<FName>& CookedPackages, const TSet<FName>& DevelopmentOnlyPackages, FSandboxPlatformFile* InSandboxFile, bool bGenerateStreamingInstallManifest);
/**
* Register a chunk data generator with this generator.
* @note Should be called prior to SaveManifests.
*/
void RegisterChunkDataGenerator(TSharedRef<IChunkDataGenerator> InChunkDataGenerator);
/**
* PreSave
* Notify generator that we are about to save the registry and chunk manifests
*/
void PreSave(const TSet<FName>& InCookedPackages);
/**
* PostSave
* Notify generator that we are finished saving registry
*/
void PostSave();
/**
* ContainsMap
* Does this package contain a map file (determined by finding if this package contains a UWorld / ULevel object)
*
* @param PackageName long package name of the package we want to determine if contains a map
* @return return if the package contains a UWorld / ULevel object (contains a map)
*/
bool ContainsMap(const FName& PackageName) const;
/**
* Returns editable version of the asset package state being generated
*/
FAssetPackageData* GetAssetPackageData(const FName& PackageName);
/**
* Adds a package to chunk manifest (just calls the other AddPackageToChunkManifestFunction with more parameters)
*
* @param Package Package to add to one of the manifests
* @param SandboxFilename Cooked sandbox path of the package to add to a manifest
* @param LastLoadedMapName Name of the last loaded map (can be empty)
* @param the SandboxPlatformFile used during cook
*/
void AddPackageToChunkManifest(const FName& PackageFName, const FString& PackagePathName, const FString& SandboxFilename, const FString& LastLoadedMapName, FSandboxPlatformFile* InSandboxFile);
/**
* Add a package to the manifest but don't assign it to any chunk yet, packages which are not assigned by the end of the cook will be put into chunk 0
*
* @param Package which is unassigned
* @param The sandbox file path of the package
*/
void AddUnassignedPackageToManifest(UPackage* Package, const FString& PackageSandboxPath);
/**
* Deletes temporary manifest directories.
*/
void CleanManifestDirectories();
/**
* Saves all generated manifests for each target platform.
*
* @param InSandboxFile the InSandboxFile used during cook
* @param InExtraFlavorChunkSize the ChunkSize used during cooking for InExtraFlavor, value greater than 0 will trigger a cook for extra flavor with specified chunk size
*/
bool SaveManifests(FSandboxPlatformFile* InSandboxFile, int64 InExtraFlavorChunkSize = 0);
/**
* Saves generated asset registry data for each platform.
*/
bool SaveAssetRegistry(const FString& SandboxPath, bool bSerializeDevelopmentAssetRegistry = true, bool bForceNoFilterAssets = false);
/**
* Writes out CookerOpenOrder.log file
*/
bool WriteCookerOpenOrder(FSandboxPlatformFile* InSandboxFile);
/**
* Follows an assets dependency chain to build up a list of package names in the same order as the runtime would attempt to load them
*
* @param InPackageName - The name of the package containing the asset to (potentially) add to the file order
* @param OutFileOrder - Output array which collects the package names, maintaining order
* @param OutEncounteredArray - Temporary collection of package names we've seen. Similar to OutFileOrder but updated BEFORE following dependencies so as to avoid circular references
* @param InPackageNameSet - The source package name list. Used to distinguish between dependencies on other packages and internal objects
* @param InTopLevelAssets - Names of packages containing top level assets such as maps
*/
void AddAssetToFileOrderRecursive(const FName& InPackageName, TArray<FName>& OutFileOrder, TSet<FName>& OutEncounteredNames, const TSet<FName>& InPackageNameSet, const TSet<FName>& InTopLevelAssets);
/**
* Get pakchunk file index from ChunkID
*
* @param ChunkID
* @return Index of target pakchunk file
*/
int32 GetPakchunkIndex(int32 ChunkId) const;
/**
* Returns the chunks
*/
void GetChunkAssignments(TArray<TSet<FName>>& OutAssignments) const;
/**
* Ensures all assets in the input package are present in the registry
* @param Package - Package to process
* @return - Array of FAssetData entries for all assets in the input package
*/
typedef TArray<const FAssetData*, TInlineAllocator<1>> FCreateOrFindArray;
FCreateOrFindArray CreateOrFindAssetDatas(const UPackage& Package);
/**
* Updates asset registry state using data collected from a remote worker.
* Synchronizes both package-level metadata and individual asset tags.
*/
void UpdateAssetDataFromWorker(FName AssetPath, FName AssetClass, uint32 PackageFlags, TMap<FName, FString>& NewTags);
private:
/** State of the asset registry that is being built for this platform */
FAssetRegistryState State;
/** Base state, which is either a release build or an iterative cook */
FAssetRegistryState PreviousState;
/** List of packages that were loaded at startup */
TSet<FName> StartupPackages;
/** List of packages that were successfully cooked */
TSet<FName> CookedPackages;
/** List of packages that were filtered out from cooking */
TSet<FName> DevelopmentOnlyPackages;
/** List of packages that were kept from a previous cook */
TArray<FName> KeptPackages;
/** Map of Package name to Sandbox Paths */
typedef TMap<FName, FString> FChunkPackageSet;
/** Holds a reference to asset registry */
IAssetRegistry& AssetRegistry;
/** Platform to generate the manifest for */
const ITargetPlatform* TargetPlatform;
/** List of all asset packages that were created while loading the last package in the cooker. */
TSet<FName> AssetsLoadedWithLastPackage;
/** Lookup for the original ChunkID Mappings */
TMap<FName, TArray<int32>> PackageChunkIDMap;
/** Set of packages containing a map */
TSet<FName> PackagesContainingMaps;
/** Should the chunks be generated or only asset registry */
bool bGenerateChunks;
/** True if we should use the AssetManager, false to use the deprecated path */
bool bUseAssetManager;
/** Highest chunk id, being used for geneating dependency tree */
int32 HighestChunkId;
/** Array of Maps with chunks<->packages assignments */
TArray<FChunkPackageSet*> ChunkManifests;
/** Map of packages that has not been assigned to chunks */
FChunkPackageSet UnassignedPackageSet;
/** Map of all cooked Packages */
FChunkPackageSet AllCookedPackageSet;
/** Array of Maps with chunks<->packages assignments. This version contains all dependent packages */
TArray<FChunkPackageSet*> FinalChunkManifests;
/** Additional data generators used when creating chunks */
TArray<TSharedRef<IChunkDataGenerator>> ChunkDataGenerators;
/** Lookup table of used package names used when searching references. */
TSet<FName> InspectedNames;
/** */
UChunkDependencyInfo* DependencyInfo;
/** Required flags a dependency must have if it is to be followed when adding package dependencies to chunks.*/
UE::AssetRegistry::EDependencyQuery DependencyQuery;
/** Mapping from chunk id to pakchunk file index. If not defined, Pakchunk index will be the same as chunk id by default */
TMap<int32, int32> ChunkIdPakchunkIndexMapping;
struct FReferencePair
{
FReferencePair() {}
FReferencePair(const FName& InName, uint32 InParentIndex)
: PackageName(InName), ParentNodeIndex(InParentIndex)
{}
bool operator==(const FReferencePair& RHS) const
{
return PackageName == RHS.PackageName;
}
FName PackageName;
uint32 ParentNodeIndex;
};
/**
* Updates disk data with CookedHash and DiskSize from previous asset registry
* for all packages kept from a previous cook.
*/
void UpdateKeptPackagesDiskData(const TArray<FName>& InKeptPackages);
/**
* Updates AssetData with TagsAndValues from previous asset registry
* for all packages kept from a previous cook.
*/
void UpdateKeptPackagesAssetData();
/**
* Updates AssetData with TagsAndValues corresponding to any collections
* flagged for inclusion as asset registry tags.
*/
void UpdateCollectionAssetData();
/**
* Adds a package to chunk manifest
*
* @param The sandbox filepath of the package
* @param The package name
* @param The ID of the chunk to assign it to
*/
void AddPackageToManifest(const FString& PackageSandboxPath, FName PackageName, int32 ChunkId);
/**
* Remove a package from a chunk manifest. Does nothing if package doesn't exist in chunk.
*
* @param The package name
* @param The ID of the chunk to assign it to
*/
void RemovePackageFromManifest(FName PackageName, int32 ChunkId);
/**
* Walks the dependency graph of assets and assigns packages to correct chunks.
*
* @param the InSandboxFile used during cook
*/
void FixupPackageDependenciesForChunks(FSandboxPlatformFile* InSandboxFile);
/**
* Attaches encryption key guids into the registry data for encrypted primary assets
*/
void InjectEncryptionData(FAssetRegistryState& TargetState);
void AddPackageAndDependenciesToChunk(FChunkPackageSet* ThisPackageSet, FName InPkgName, const FString& InSandboxFile, int32 PakchunkIndex, FSandboxPlatformFile* SandboxPlatformFile);
/**
* Returns the path of the temporary packaging directory for the specified platform.
*/
FString GetTempPackagingDirectoryForPlatform(const FString& Platform) const
{
return FPaths::ProjectSavedDir() / TEXT("TmpPackaging") / Platform;
}
/**
*
*/
int64 GetMaxChunkSizePerPlatform(const ITargetPlatform* Platform) const;
/**
* Returns an array of chunks ID for a package name that have been assigned during the cook process.
*/
FORCEINLINE TArray<int32> GetExistingPackageChunkAssignments(FName PackageFName)
{
TArray<int32> ExistingChunkIDs;
for (uint32 ChunkIndex = 0, MaxChunk = ChunkManifests.Num(); ChunkIndex < MaxChunk; ++ChunkIndex)
{
if (ChunkManifests[ChunkIndex] && ChunkManifests[ChunkIndex]->Contains(PackageFName))
{
ExistingChunkIDs.AddUnique(ChunkIndex);
}
}
if (StartupPackages.Contains(PackageFName))
{
ExistingChunkIDs.AddUnique(0);
}
return ExistingChunkIDs;
}
/**
* Returns an array of chunks IDs for a package that have been assigned in the editor.
*/
FORCEINLINE TArray<int32> GetAssetRegistryChunkAssignments(const FName& PackageFName)
{
TArray<int32> RegistryChunkIDs;
auto* FoundIDs = PackageChunkIDMap.Find(PackageFName);
if (FoundIDs)
{
RegistryChunkIDs = *FoundIDs;
}
return RegistryChunkIDs;
}
/** Generate manifest for a single package */
void GenerateChunkManifestForPackage(const FName& PackageFName, const FString& PackagePathName, const FString& SandboxFilename, const FString& LastLoadedMapName, FSandboxPlatformFile* InSandboxFile);
/** Deletes the temporary packaging directory for the specified platform */
bool CleanTempPackagingDirectory(const FString& Platform) const;
/** Returns true if the specific platform desires a chunk manifest */
bool ShouldPlatformGenerateStreamingInstallManifest(const ITargetPlatform* Platform) const;
/** Generates and saves streaming install chunk manifest */
bool GenerateStreamingInstallManifest(int64 InExtraFlavorChunkSize, FSandboxPlatformFile* InSandboxFile);
/** Gather a list of dependencies required by to completely load this package */
bool GatherAllPackageDependencies(FName PackageName, TArray<FName>& DependentPackageNames);
/** Gather the list of dependencies that link the source to the target. Output array includes the target */
bool GetPackageDependencyChain(FName SourcePackage, FName TargetPackage, TSet<FName>& VisitedPackages, TArray<FName>& OutDependencyChain);
/** Get an array of Packages this package will import */
bool GetPackageDependencies(FName PackageName, TArray<FName>& DependentPackageNames, UE::AssetRegistry::EDependencyQuery InDependencyQuery);
/** Save a CSV dump of chunk asset information, if bWriteIndividualFiles is true it writes a CSV per chunk in addition to AllChunksInfo */
bool GenerateAssetChunkInformationCSV(const FString& OutputPath, bool bWriteIndividualFiles = false);
/** Finds the asset belonging to ChunkID with the smallest number of links to Packages In PackageNames */
void FindShortestReferenceChain(TArray<FReferencePair> PackageNames, int32 ChunkID, uint32& OutParentIndex, FString& OutChainPath);
/** Helper function for FindShortestReferenceChain */
FString GetShortestReferenceChain(FName PackageName, int32 ChunkID);
/** Deprecated method to remove redundant chunks */
void ResolveChunkDependencyGraph(const FChunkDependencyTreeNode& Node, const TSet<FName>& BaseAssetSet, TArray<TArray<FName>>& OutPackagesMovedBetweenChunks);
/** Helper function to verify Chunk asset assignment is valid */
bool CheckChunkAssetsAreNotInChild(const FChunkDependencyTreeNode& Node);
/** Helper function to create a given collection. */
bool CreateOrEmptyCollection(FName CollectionName);
/** Helper function to fill a given collection with a set of packages */
void WriteCollection(FName CollectionName, const TArray<FName>& PackageNames);
/** Initialize ChunkIdPakchunkIndexMapping and PakchunkIndexChunkIdMapping. */
void InitializeChunkIdPakchunkIndexMapping();
/**
* Helper function to find or create asset data for the input object. If the asset is not in the registry it will be added.
*/
const FAssetData* CreateOrFindAssetData(UObject& Object);
};