initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.AddressableAssets.Build.DataBuilders;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
namespace UnityEditor.AddressableAssets.Build.BuildPipelineTasks
{
/// <summary>
/// The BuildTask used to append the asset hash to the internal bundle name.
/// </summary>
public class AddHashToBundleNameTask : IBuildTask
{
/// <summary>
/// The task version.
/// </summary>
public int Version
{
get { return 1; }
}
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBundleBuildContent m_BuildContent;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.InOut, true)]
IBuildSpriteData m_SpriteData;
[InjectContext(ContextUsage.In)]
IAddressableAssetsBuildContext m_AaBuildContext;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
#pragma warning restore 649
/// <summary>
/// Runs the AddHashToBundleNameTask.
/// </summary>
/// <returns>Success.</returns>
public ReturnCode Run()
{
var aa = m_AaBuildContext as AddressableAssetsBuildContext;
if (!aa.Settings.UniqueBundleIds)
return ReturnCode.Success;
var newBundleLayout = new Dictionary<string, List<GUID>>();
foreach (var bid in m_BuildContent.BundleLayout)
{
var hash = GetAssetsHash(bid.Value, aa);
var newName = $"{bid.Key}_{hash}";
newBundleLayout.Add(newName, bid.Value);
string assetGroup;
if (aa.bundleToAssetGroup.TryGetValue(bid.Key, out assetGroup))
{
aa.bundleToAssetGroup.Remove(bid.Key);
aa.bundleToAssetGroup.Add(newName, assetGroup);
}
}
m_BuildContent.BundleLayout.Clear();
foreach (var bid in newBundleLayout)
m_BuildContent.BundleLayout.Add(bid.Key, bid.Value);
return ReturnCode.Success;
}
internal RawHash GetAssetsHash(List<GUID> assets, AddressableAssetsBuildContext context)
{
assets.Sort();
var hashes = new HashSet<Hash128>();
foreach (var g in assets)
{
AssetLoadInfo assetInfo;
if (m_DependencyData.AssetInfo.TryGetValue(g, out assetInfo))
{
var diskOnlyReferencedObjects = assetInfo.referencedObjects.Where(ro => context.Settings.FindAssetEntry(ro.guid.ToString()) == null).ToList();
GetAssetHashes(hashes, g, diskOnlyReferencedObjects, m_Cache != null && m_Parameters.UseCache);
}
}
return HashingMethods.Calculate(hashes.ToArray());
}
void GetAssetHashes(HashSet<Hash128> hashes, GUID g, List<ObjectIdentifier> referencedObjects, bool useCache)
{
if (useCache)
{
hashes.Add(m_Cache.GetCacheEntry(g, Version).Hash);
foreach (var reference in referencedObjects)
hashes.Add(m_Cache.GetCacheEntry(reference).Hash);
}
else
hashes.Add(AssetDatabase.GetAssetDependencyHash(AssetDatabase.GUIDToAssetPath(g.ToString())));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d624c3bd70705446adc7ee473ceb159
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d34ce007ffbd4e74c9151022c3fb56df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,76 @@
using System;
using UnityEditor.Build.Pipeline;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.AddressableAssets.Build.BuildPipelineTasks
{
/// <summary>
/// The BuildTask used to extract write data from the build.
/// </summary>
public class ExtractDataTask : IBuildTask
{
/// <summary>
/// The ExtractDataTask version.
/// </summary>
public int Version
{
get { return 1; }
}
/// <summary>
/// Get the injected dependency data of the task.
/// </summary>
public IDependencyData DependencyData
{
get { return m_DependencyData; }
}
/// <summary>
/// Get the injected write data of the task.
/// </summary>
public IBundleWriteData WriteData
{
get { return m_WriteData; }
}
/// <summary>
/// Get the injected build cache of the task.
/// </summary>
public IBuildCache BuildCache
{
get { return m_BuildCache; }
}
/// <summary>
/// The build context of the task.
/// </summary>
public IBuildContext BuildContext
{
get { return m_BuildContext; }
}
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In)]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IBuildCache m_BuildCache;
[InjectContext(ContextUsage.In)]
internal IBuildContext m_BuildContext;
#pragma warning restore 649
/// <summary>
/// Runs the ExtractDataTask. The data for this task is all injected context so no operations are performed in the Run step.
/// </summary>
/// <returns>Success.</returns>
public ReturnCode Run()
{
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9dafe60ff4149624880b04d0cea88719
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,384 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.AddressableAssets.Build.DataBuilders;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEngine;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using static UnityEditor.AddressableAssets.Settings.AddressablesFileEnumeration;
namespace UnityEditor.AddressableAssets.Build.BuildPipelineTasks
{
/// <summary>
/// The BuildTask used to create location lists for Addressable assets.
/// </summary>
public class GenerateLocationListsTask : IBuildTask
{
const int k_Version = 1;
/// <summary>
/// The GenerateLocationListsTask version.
/// </summary>
public int Version
{
get { return k_Version; }
}
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IAddressableAssetsBuildContext m_AaBuildContext;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
#pragma warning restore 649
/// <summary>
/// Runs the build task with the injected context.
/// </summary>
/// <returns>The success or failure ReturnCode</returns>
public ReturnCode Run()
{
Input input = new Input();
var aaContext = (AddressableAssetsBuildContext)m_AaBuildContext;
input.FileToBundle = m_WriteData.FileToBundle;
input.AssetToFiles = m_WriteData.AssetToFiles;
input.AssetToAssetInfo = m_DependencyData != null ? m_DependencyData.AssetInfo : null;
input.Logger = m_Log;
input.Settings = aaContext.Settings;
input.BundleToAssetGroup = aaContext.bundleToAssetGroup;
input.AddressableAssetEntries = aaContext.assetEntries;
input.Target = m_Parameters.Target;
Output output = ProcessInput(input);
if (aaContext.locations == null)
aaContext.locations = output.Locations;
else
aaContext.locations.AddRange(output.Locations);
if (aaContext.GuidToCatalogLocation == null)
aaContext.GuidToCatalogLocation = output.GuidToLocation;
else foreach (KeyValuePair<GUID,List<ContentCatalogDataEntry>> pair in output.GuidToLocation)
aaContext.GuidToCatalogLocation[pair.Key] = pair.Value;
aaContext.assetGroupToBundles = output.AssetGroupToBundles;
if (aaContext.providerTypes == null)
aaContext.providerTypes = output.ProviderTypes;
else
aaContext.providerTypes.UnionWith(output.ProviderTypes);
aaContext.bundleToExpandedBundleDependencies = output.BundleToExpandedBundleDependencies;
aaContext.bundleToImmediateBundleDependencies = output.BundleToImmediateBundleDependencies;
return ReturnCode.Success;
}
/// <summary>
/// Storage for data gathered by the build pipeline.
/// </summary>
public struct Input
{
/// <summary>
/// Mapping from serialized filename to the bundle name
/// </summary>
public Dictionary<string, string> FileToBundle;
/// <summary>
/// Mapping of an asset to all the serialized files needed to load it. The first entry is the file that contains the asset itself.
/// </summary>
public Dictionary<GUID, List<string>> AssetToFiles;
/// <summary>
/// Map of Guid to AssetLoadInfo
/// </summary>
public Dictionary<GUID, AssetLoadInfo> AssetToAssetInfo;
/// <summary>
/// The logger used during the build.
/// </summary>
public IBuildLogger Logger;
/// <summary>
/// The current AddressableAssetSettings to be processed.
/// </summary>
public AddressableAssetSettings Settings;
/// <summary>
/// Mapping of the AssetBundle to the AddressableAssetGroup it was derived from
/// </summary>
public Dictionary<string, string> BundleToAssetGroup;
/// <summary>
/// All the AddressableAssetEntries to process
/// </summary>
public List<AddressableAssetEntry> AddressableAssetEntries;
/// <summary>
/// The BuildTarget to build for.
/// </summary>
public BuildTarget Target;
}
/// <summary>
/// Storage for location data, including: dependencies, locations, and provider types.
/// </summary>
public struct Output
{
/// <summary>
/// Content Catalog entries that were built into the Catalog.
/// </summary>
public List<ContentCatalogDataEntry> Locations;
/// <summary>
/// A mapping of Asset GUID's to resulting ContentCatalogDataEntry entries.
/// </summary>
internal Dictionary<GUID, List<ContentCatalogDataEntry>> GuidToLocation;
/// <summary>
/// A mapping of AddressableAssetGroups to the AssetBundles generated from its data.
/// </summary>
public Dictionary<AddressableAssetGroup, List<string>> AssetGroupToBundles;
/// <summary>
/// A hash set of all the provider types included in the build.
/// </summary>
public HashSet<Type> ProviderTypes;
/// <summary>
/// A mapping of AssetBundles to the direct dependencies
/// </summary>
public Dictionary<string, List<string>> BundleToImmediateBundleDependencies;
/// <summary>
/// A mapping of AssetBundles to their expanded dependencies.
/// </summary>
public Dictionary<string, List<string>> BundleToExpandedBundleDependencies;
}
static AddressableAssetGroup GetGroupFromBundle(string bundleName, Dictionary<string, string> bundleToAssetGroupGUID, AddressableAssetSettings settings)
{
if (!bundleToAssetGroupGUID.TryGetValue(bundleName, out string groupGuid))
return settings.DefaultGroup;
return settings.FindGroup(g => g != null && g.Guid == groupGuid);
}
static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dict, TKey key) where TValue : new()
{
TValue val;
if (!dict.TryGetValue(key, out val))
{
val = new TValue();
dict.Add(key, val);
}
return val;
}
class BundleEntry
{
public string BundleName;
public HashSet<BundleEntry> Dependencies = new HashSet<BundleEntry>();
public HashSet<BundleEntry> ExpandedDependencies;
public List<GUID> Assets = new List<GUID>();
public AddressableAssetGroup Group;
public HashSet<string> AssetInternalIds = new HashSet<string>();
}
static private void ExpandDependencies(BundleEntry entry)
{
HashSet<BundleEntry> visited = new HashSet<BundleEntry>();
Queue<BundleEntry> toVisit = new Queue<BundleEntry>();
toVisit.Enqueue(entry);
while (toVisit.Count > 0)
{
BundleEntry cur = toVisit.Dequeue();
visited.Add(cur);
foreach (BundleEntry dep in cur.Dependencies)
if (!visited.Contains(dep))
toVisit.Enqueue(dep);
}
entry.ExpandedDependencies = visited;
}
static BundleEntry GetOrCreateBundleEntry(string bundleName, Dictionary<string, BundleEntry> bundleToEntry)
{
if (!bundleToEntry.TryGetValue(bundleName, out BundleEntry e))
bundleToEntry.Add(bundleName, e = new BundleEntry() {BundleName = bundleName});
return e;
}
/// <summary>
/// Processes the Input data from the build and returns an organized struct of information, including dependencies and catalog loctions.
/// </summary>
/// <param name="input">Data captured as part of the build process.</param>
/// <returns>An object that contains organized information about dependencies and catalog locations.</returns>
public static Output ProcessInput(Input input)
{
var locations = new List<ContentCatalogDataEntry>();
var assetGroupToBundles = new Dictionary<AddressableAssetGroup, List<string>>();
var bundleToEntry = new Dictionary<string, BundleEntry>();
var providerTypes = new HashSet<Type>();
// Create a bundle entry for every bundle that our assets could reference
foreach (List<string> files in input.AssetToFiles.Values)
files.ForEach(x => GetOrCreateBundleEntry(input.FileToBundle[x], bundleToEntry));
// build list of assets each bundle has as well as the dependent bundles
using (input.Logger.ScopedStep(LogLevel.Info, "Calculate Bundle Dependencies"))
{
foreach (KeyValuePair<GUID, List<string>> k in input.AssetToFiles)
{
string bundle = input.FileToBundle[k.Value[0]];
BundleEntry bundleEntry = bundleToEntry[bundle];
bundleEntry.Assets.Add(k.Key);
bundleEntry.Dependencies.UnionWith(k.Value.Select(x => bundleToEntry[input.FileToBundle[x]]));
}
}
using (input.Logger.ScopedStep(LogLevel.Info, "ExpandDependencies"))
{
foreach (BundleEntry bEntry in bundleToEntry.Values)
ExpandDependencies(bEntry);
}
// Assign each bundle a group
foreach (BundleEntry bEntry in bundleToEntry.Values)
bEntry.Group = GetGroupFromBundle(bEntry.BundleName, input.BundleToAssetGroup, input.Settings);
// Create a location for each bundle
foreach (BundleEntry bEntry in bundleToEntry.Values)
{
string bundleProvider = GetBundleProviderName(bEntry.Group);
string bundleInternalId = GetLoadPath(bEntry.Group, bEntry.BundleName, input.Target);
locations.Add(new ContentCatalogDataEntry(typeof(IAssetBundleResource), bundleInternalId, bundleProvider, new object[] {bEntry.BundleName}));
}
Dictionary<GUID, List<ContentCatalogDataEntry>> guidToLocation = new Dictionary<GUID, List<ContentCatalogDataEntry>>();
using (input.Logger.ScopedStep(LogLevel.Info, "Calculate Locations"))
{
// build a mapping of asset guid to AddressableAssetEntry
Dictionary<string, AddressableAssetEntry> guidToEntry = input.AddressableAssetEntries.ToDictionary(x => x.guid, x => x);
foreach (BundleEntry bEntry in bundleToEntry.Values)
{
string assetProvider = GetAssetProviderName(bEntry.Group);
var schema = bEntry.Group.GetSchema<BundledAssetGroupSchema>();
foreach (GUID assetGUID in bEntry.Assets)
{
if (guidToEntry.TryGetValue(assetGUID.ToString(), out AddressableAssetEntry entry))
{
int indexAddedStart = locations.Count;
entry.CreateCatalogEntries(locations, true, assetProvider, bEntry.ExpandedDependencies.Select(x => x.BundleName), null, input.AssetToAssetInfo, providerTypes,
schema.IncludeAddressInCatalog, schema.IncludeGUIDInCatalog, schema.IncludeLabelsInCatalog, bEntry.AssetInternalIds);
if (indexAddedStart < locations.Count)
guidToLocation.Add(assetGUID, locations.GetRange(indexAddedStart, locations.Count-indexAddedStart));
}
}
}
}
// create the assetGroupToBundles mapping
foreach (BundleEntry bEntry in bundleToEntry.Values)
GetOrCreate(assetGroupToBundles, bEntry.Group).Add(bEntry.BundleName);
var output = new Output();
output.Locations = locations;
output.GuidToLocation = guidToLocation;
output.ProviderTypes = providerTypes;
output.AssetGroupToBundles = assetGroupToBundles;
output.BundleToImmediateBundleDependencies = bundleToEntry.Values.ToDictionary(x => x.BundleName, x => x.Dependencies.Select(y => y.BundleName).ToList());
output.BundleToExpandedBundleDependencies =
bundleToEntry.Values.ToDictionary(x => x.BundleName, x => x.ExpandedDependencies.Where(y => !x.Dependencies.Contains(y)).Select(y => y.BundleName).ToList());
return output;
}
/// <summary>
/// Runs the build task with a give context and write data.
/// </summary>
/// <param name="aaBuildContext">The addressables build context.</param>
/// <param name="writeData">The write data used to generate the location lists.</param>
/// <returns>The success or failure ReturnCode</returns>
[Obsolete("This method uses nonoptimized code. Use nonstatic version Run() instead.")]
public static ReturnCode Run(IAddressableAssetsBuildContext aaBuildContext, IBundleWriteData writeData)
{
var task = new GenerateLocationListsTask();
task.m_AaBuildContext = aaBuildContext;
task.m_WriteData = writeData;
return task.Run();
}
internal static string GetBundleProviderName(AddressableAssetGroup group)
{
return group.GetSchema<BundledAssetGroupSchema>().GetBundleCachedProviderId();
}
internal static string GetAssetProviderName(AddressableAssetGroup group)
{
return group.GetSchema<BundledAssetGroupSchema>().GetAssetCachedProviderId();
}
internal static string GetLoadPath(AddressableAssetGroup group, string name, BuildTarget target)
{
var bagSchema = group.GetSchema<BundledAssetGroupSchema>();
if (bagSchema == null || bagSchema.LoadPath == null)
{
Debug.LogError("Unable to determine load path for " + name + ". Check that your default group is not '" + AddressableAssetSettings.PlayerDataGroupName + "'");
return string.Empty;
}
string loadPath = bagSchema.LoadPath.GetValue(group.Settings);
loadPath = loadPath.Replace('\\', '/');
if (loadPath.EndsWith("/"))
loadPath += name;
else
loadPath = loadPath + "/" + name;
if (!string.IsNullOrEmpty(bagSchema.UrlSuffix))
loadPath += bagSchema.UrlSuffix;
if (!ResourceManagerConfig.ShouldPathUseWebRequest(loadPath) && !bagSchema.UseUnityWebRequestForLocalBundles)
{
char separator = PathSeparatorForPlatform(target);
if (separator != '/')
loadPath = loadPath.Replace('/', separator);
}
return loadPath;
}
internal static char PathSeparatorForPlatform(BuildTarget target)
{
switch (target)
{
case BuildTarget.StandaloneWindows64:
case BuildTarget.StandaloneWindows:
case BuildTarget.XboxOne:
return '\\';
case BuildTarget.GameCoreXboxOne:
return '\\';
case BuildTarget.Android:
return '\\';
default:
return '/';
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b19f99025db4a1e44a095aa3f1284c54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: