using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
///
/// Updates the layout for bundle objects.
///
public class UpdateBundleObjectLayout : IBuildTask
{
///
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In, true)]
IBundleExplictObjectLayout m_Layout;
[InjectContext]
IBundleBuildContent m_Content;
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IDeterministicIdentifiers m_PackingMethod;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
///
public ReturnCode Run()
{
if (m_Layout == null || m_Layout.ExplicitObjectLocation.IsNullOrEmpty())
return ReturnCode.SuccessNotRun;
var ObjectToAssetReferences = new Dictionary>();
var ObjectToFiles = new Dictionary>();
using (m_Log.ScopedStep(LogLevel.Info, "PopulateReferencesMaps", true))
{
var task = Task.Run(() =>
{
using (m_Log.ScopedStep(LogLevel.Info, "Populate Assets Map", $"Count={m_DependencyData.AssetInfo.Count}"))
{
foreach (KeyValuePair dependencyPair in m_DependencyData.AssetInfo)
{
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.includedObjects, ObjectToAssetReferences);
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.referencedObjects, ObjectToAssetReferences);
}
}
using (m_Log.ScopedStep(LogLevel.Info, "Populate Scenes Map", $"Count={m_DependencyData.SceneInfo.Count}"))
{
foreach (KeyValuePair dependencyPair in m_DependencyData.SceneInfo)
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.referencedObjects, ObjectToAssetReferences);
}
});
using (m_Log.ScopedStep(LogLevel.Info, "Populate Files Map", $"Count={m_WriteData.FileToObjects.Count}"))
{
foreach (KeyValuePair> filePair in m_WriteData.FileToObjects)
PopulateReferencesMap(filePair.Key, filePair.Value, ObjectToFiles);
}
task.Wait();
}
using (m_Log.ScopedStep(LogLevel.Info, "UpdateWriteData"))
{
foreach (var group in m_Layout.ExplicitObjectLocation.GroupBy(s => s.Value))
{
IEnumerable objectIDs = group.Select(s => s.Key);
string bundleName = group.Key;
string internalName = string.Format(CommonStrings.AssetBundleNameFormat, m_PackingMethod.GenerateInternalFileName(bundleName));
foreach (var objectID in objectIDs)
{
UpdateAssetToFilesMap(internalName, ObjectToAssetReferences[objectID], m_WriteData.AssetToFiles);
RemoveObjectIDFromFiles(objectID, ObjectToFiles[objectID], m_WriteData.FileToObjects);
}
// Add new mapping for File to Bundle
UpdateFileToBundleMap(bundleName, internalName, m_WriteData.FileToBundle, m_Content.BundleLayout);
// Update File to Object map
UpdateFileToObjectMap(internalName, objectIDs, m_WriteData.FileToObjects);
}
}
return ReturnCode.Success;
}
internal static void PopulateReferencesMap(T key, IList objects, Dictionary> map)
{
foreach (var obj in objects)
{
map.GetOrAdd(obj, out var set);
set.Add(key);
}
}
internal static void UpdateAssetToFilesMap(string file, List assetsToUpdate, Dictionary> AssetToFiles)
{
foreach (var asset in assetsToUpdate)
{
var assetFiles = AssetToFiles[asset];
if (!assetFiles.Contains(file))
assetFiles.Add(file);
}
}
internal static void RemoveObjectIDFromFiles(ObjectIdentifier objectID, List files, Dictionary> FileToObjects)
{
foreach (var file in files)
FileToObjects[file].Remove(objectID);
}
internal static void UpdateFileToBundleMap(string bundleName, string file, Dictionary FileToBundle, Dictionary> BundleLayout)
{
if (!FileToBundle.ContainsKey(file))
{
FileToBundle.Add(file, bundleName);
// NOTE: We want the output result to know about the new bundle, but since we are only
// assigning individual objects to this bundle and not full assets, the asset list will be empty
BundleLayout.Add(bundleName, new List());
}
}
internal static void UpdateFileToObjectMap(string file, IEnumerable newObjectIDs, Dictionary> FileToObjects)
{
// This is called after remove, thus we can just AddRange as we already know these objects are not in any file
FileToObjects.GetOrAdd(file, out var objectIDs);
objectIDs.AddRange(newObjectIDs);
}
}
}