示例#1
0
        /// <inheritdoc />
        public ReturnCode Run()
        {
            TaskInput input = new TaskInput();

            input.InternalFilenameToWriteResults = m_Results.WriteResults;
#if UNITY_2019_3_OR_NEWER
            input.InternalFilenameToAdditionalFiles = m_Content.AdditionalFiles;
#endif
            input.InternalFilenameToBundleName   = m_WriteData.FileToBundle;
            input.GetCompressionForIdentifier    = (x) => m_Parameters.GetCompressionForIdentifier(x);
            input.GetOutputFilePathForIdentifier = (x) => m_Parameters.GetOutputFilePathForIdentifier(x);
            input.BuildCache               = m_Parameters.UseCache ? m_Cache : null;
            input.ProgressTracker          = m_Tracker;
            input.TempOutputFolder         = m_Parameters.TempOutputFolder;
            input.AssetToFilesDependencies = m_WriteData.AssetToFiles;
            input.Log = m_Log;

            input.Threaded = SupportsMultiThreadedArchiving && ScriptableBuildPipeline.threadedArchiving;

            TaskOutput output;
            ReturnCode code = Run(input, out output);

            if (code == ReturnCode.Success)
            {
                foreach (var item in output.BundleDetails)
                {
                    m_Results.BundleInfos.Add(item.Key, item.Value);
                }
            }

            return(code);
        }
        public ReturnCode Run()
        {
            Dictionary <string, ulong> fileOffsets = new Dictionary <string, ulong>();
            List <KeyValuePair <string, List <ResourceFile> > > bundleResources;
            {
                Dictionary <string, List <ResourceFile> > bundleToResources = new Dictionary <string, List <ResourceFile> >();
                foreach (var pair in m_Results.WriteResults)
                {
                    string bundle = m_WriteData.FileToBundle[pair.Key];
                    List <ResourceFile> resourceFiles;
                    bundleToResources.GetOrAdd(bundle, out resourceFiles);
                    resourceFiles.AddRange(pair.Value.resourceFiles);

                    foreach (ResourceFile serializedFile in pair.Value.resourceFiles)
                    {
                        if (!serializedFile.serializedFile)
                        {
                            continue;
                        }

                        ObjectSerializedInfo firstObject = pair.Value.serializedObjects.First(x => x.header.fileName == serializedFile.fileAlias);
                        fileOffsets[serializedFile.fileName] = firstObject.header.offset;
                    }
                }
                bundleResources = bundleToResources.ToList();
            }

            Dictionary <string, HashSet <string> > bundleDependencies = new Dictionary <string, HashSet <string> >();

            foreach (var files in m_WriteData.AssetToFiles.Values)
            {
                if (files.IsNullOrEmpty())
                {
                    continue;
                }

                string           bundle = m_WriteData.FileToBundle[files.First()];
                HashSet <string> dependencies;
                bundleDependencies.GetOrAdd(bundle, out dependencies);
                dependencies.UnionWith(files.Select(x => m_WriteData.FileToBundle[x]));
                dependencies.Remove(bundle);
            }

            IList <CacheEntry> entries      = bundleResources.Select(x => GetCacheEntry(x.Key, x.Value, m_Parameters.GetCompressionForIdentifier(x.Key))).ToList();
            IList <CachedInfo> cachedInfo   = null;
            IList <CachedInfo> uncachedInfo = null;

            if (m_Parameters.UseCache && m_Cache != null)
            {
                m_Cache.LoadCachedData(entries, out cachedInfo);

                uncachedInfo = new List <CachedInfo>();
            }

            for (int i = 0; i < bundleResources.Count; i++)
            {
                string           bundleName    = bundleResources[i].Key;
                ResourceFile[]   resourceFiles = bundleResources[i].Value.ToArray();
                BuildCompression compression   = m_Parameters.GetCompressionForIdentifier(bundleName);

                string        writePath;
                BundleDetails details;
                if (cachedInfo != null && cachedInfo[i] != null)
                {
                    if (!m_Tracker.UpdateInfoUnchecked(string.Format("{0} (Cached)", bundleName)))
                    {
                        return(ReturnCode.Canceled);
                    }

                    details          = (BundleDetails)cachedInfo[i].Data[0];
                    details.FileName = string.Format("{0}/{1}", m_Parameters.OutputFolder, bundleName);

                    HashSet <string> dependencies;
                    if (bundleDependencies.TryGetValue(bundleName, out dependencies))
                    {
                        details.Dependencies = dependencies.ToArray();
                    }
                    else
                    {
                        details.Dependencies = new string[0];
                    }
                    writePath = string.Format("{0}/{1}", m_Cache.GetCachedArtifactsDirectory(entries[i]), bundleName);
                }
                else
                {
                    if (!m_Tracker.UpdateInfoUnchecked(bundleName))
                    {
                        return(ReturnCode.Canceled);
                    }

                    details   = new BundleDetails();
                    writePath = string.Format("{0}/{1}", m_Parameters.TempOutputFolder, bundleName);
                    if (m_Parameters.UseCache && m_Cache != null)
                    {
                        writePath = string.Format("{0}/{1}", m_Cache.GetCachedArtifactsDirectory(entries[i]), bundleName);
                    }
                    Directory.CreateDirectory(Path.GetDirectoryName(writePath));

                    details.FileName = string.Format("{0}/{1}", m_Parameters.OutputFolder, bundleName);
                    details.Crc      = ContentBuildInterface.ArchiveAndCompress(resourceFiles, writePath, compression);
                    details.Hash     = CalculateHashVersion(fileOffsets, resourceFiles);

                    HashSet <string> dependencies;
                    if (bundleDependencies.TryGetValue(bundleName, out dependencies))
                    {
                        details.Dependencies = dependencies.ToArray();
                    }
                    else
                    {
                        details.Dependencies = new string[0];
                    }

                    if (uncachedInfo != null)
                    {
                        uncachedInfo.Add(GetCachedInfo(m_Cache, entries[i], resourceFiles, details));
                    }
                }

                SetOutputInformation(writePath, details.FileName, bundleName, details);
            }

            if (m_Parameters.UseCache && m_Cache != null)
            {
                m_Cache.SaveCachedData(uncachedInfo);
            }

            return(ReturnCode.Success);
        }
        private BuildLayout CreateBuildLayout()
        {
            LayoutLookupTables lookup = new LayoutLookupTables();

            foreach (string bundleName in m_WriteData.FileToBundle.Values.Distinct())
            {
                BuildLayout.Bundle bundle = new BuildLayout.Bundle();
                bundle.Name = bundleName;
                string path = m_Parameters.GetOutputFilePathForIdentifier(bundle.Name);
                UnityEngine.BuildCompression compression = m_Parameters.GetCompressionForIdentifier(bundle.Name);
                bundle.FileSize    = (ulong)new FileInfo(path).Length;
                bundle.Compression = compression.compression.ToString();
                lookup.Bundles.Add(bundle.Name, bundle);
            }

            // create files
            foreach (KeyValuePair <string, string> fileBundle in m_WriteData.FileToBundle)
            {
                BuildLayout.Bundle bundle = lookup.Bundles[fileBundle.Value];
                BuildLayout.File   f      = new BuildLayout.File();
                f.Name = fileBundle.Key;

                WriteResult result = m_Results.WriteResults[f.Name];
                foreach (ResourceFile rf in result.resourceFiles)
                {
                    var sf = new BuildLayout.SubFile();
                    sf.IsSerializedFile = rf.serializedFile;
                    sf.Name             = rf.fileAlias;
                    sf.Size             = (ulong)new FileInfo(rf.fileName).Length;
                    f.SubFiles.Add(sf);
                }

                bundle.Files.Add(f);
                lookup.Files.Add(f.Name, f);
            }

            // create assets
            foreach (KeyValuePair <GUID, List <string> > assetFile in m_WriteData.AssetToFiles)
            {
                BuildLayout.File          file = lookup.Files[assetFile.Value[0]];
                BuildLayout.ExplicitAsset a    = new BuildLayout.ExplicitAsset();
                a.Guid      = assetFile.Key.ToString();
                a.AssetPath = AssetDatabase.GUIDToAssetPath(a.Guid);
                file.Assets.Add(a);
                lookup.GuidToExplicitAsset.Add(a.Guid, a);
            }

            Dictionary <string, List <BuildLayout.DataFromOtherAsset> > guidToPulledInBuckets = new Dictionary <string, List <BuildLayout.DataFromOtherAsset> >();

            foreach (BuildLayout.File file in lookup.Files.Values)
            {
                Dictionary <string, AssetBucket> buckets = new Dictionary <string, AssetBucket>();
                WriteResult writeResult = m_Results.WriteResults[file.Name];
                List <ObjectSerializedInfo> sceneObjects = new List <ObjectSerializedInfo>();

                foreach (ObjectSerializedInfo info in writeResult.serializedObjects)
                {
                    string sourceGuid = string.Empty;
                    if (info.serializedObject.guid.Empty())
                    {
                        if (info.serializedObject.filePath.Equals("temp:/assetbundle", StringComparison.OrdinalIgnoreCase))
                        {
                            file.BundleObjectInfo      = new BuildLayout.AssetBundleObjectInfo();
                            file.BundleObjectInfo.Size = info.header.size;
                            continue;
                        }
                        else if (info.serializedObject.filePath.StartsWith("temp:/preloaddata", StringComparison.OrdinalIgnoreCase))
                        {
                            file.PreloadInfoSize = (int)info.header.size;
                            continue;
                        }
                        else if (info.serializedObject.filePath.StartsWith("temp:/", StringComparison.OrdinalIgnoreCase))
                        {
                            sceneObjects.Add(info);
                            continue;
                        }
                        else if (!string.IsNullOrEmpty(info.serializedObject.filePath))
                        {
                            AssetBucket pathBucket = GetOrCreate(buckets, info.serializedObject.filePath.ToString());
                            pathBucket.isFilePathBucket = true;
                            pathBucket.objs.Add(info);
                            continue;
                        }
                    }

                    AssetBucket bucket = GetOrCreate(buckets, info.serializedObject.guid.ToString());
                    bucket.objs.Add(info);
                }

                if (sceneObjects.Count > 0)
                {
                    BuildLayout.ExplicitAsset sceneAsset = file.Assets.First(x => x.AssetPath.EndsWith(".unity"));
                    AssetBucket bucket = GetOrCreate(buckets, sceneAsset.Guid);
                    bucket.objs.AddRange(sceneObjects);
                }

                // Update buckets with a reference to their explicit asset
                file.Assets.ForEach(eAsset =>
                {
                    if (!buckets.TryGetValue(eAsset.Guid, out AssetBucket b))
                    {
                        b = GetOrCreate(buckets, eAsset.Guid); // some assets might not pull in any objects
                    }
                    b.ExplictAsset = eAsset;
                });

                // Create entries for buckets that are implicitly pulled in
                Dictionary <string, BuildLayout.DataFromOtherAsset> guidToOtherData = new Dictionary <string, BuildLayout.DataFromOtherAsset>();
                foreach (AssetBucket bucket in buckets.Values.Where(x => x.ExplictAsset == null))
                {
                    string assetPath = bucket.isFilePathBucket ? bucket.guid : AssetDatabase.GUIDToAssetPath(bucket.guid);
                    if (assetPath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) || assetPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                    {
                        file.MonoScriptCount++;
                        file.MonoScriptSize += bucket.CalcObjectSize();
                        continue;
                    }
                    var otherData = new BuildLayout.DataFromOtherAsset();
                    otherData.AssetPath      = assetPath;
                    otherData.AssetGuid      = bucket.guid;
                    otherData.SerializedSize = bucket.CalcObjectSize();
                    otherData.StreamedSize   = bucket.CalcStreamedSize();
                    otherData.ObjectCount    = bucket.objs.Count;
                    file.OtherAssets.Add(otherData);
                    guidToOtherData[otherData.AssetGuid] = otherData;

                    if (!guidToPulledInBuckets.TryGetValue(otherData.AssetGuid, out List <BuildLayout.DataFromOtherAsset> bucketList))
                    {
                        bucketList = guidToPulledInBuckets[otherData.AssetGuid] = new List <BuildLayout.DataFromOtherAsset>();
                    }
                    bucketList.Add(otherData);
                }

                // Add references
                foreach (BuildLayout.ExplicitAsset asset in file.Assets)
                {
                    AssetBucket bucket = buckets[asset.Guid];
                    asset.SerializedSize = bucket.CalcObjectSize();
                    asset.StreamedSize   = bucket.CalcStreamedSize();

                    IEnumerable <ObjectIdentifier> refs = null;
                    if (m_DependencyData.AssetInfo.TryGetValue(new GUID(asset.Guid), out AssetLoadInfo info))
                    {
                        refs = info.referencedObjects;
                    }
                    else
                    {
                        refs = m_DependencyData.SceneInfo[new GUID(asset.Guid)].referencedObjects;
                    }
                    foreach (string refGUID in refs.Select(x => x.guid.Empty() ? x.filePath : x.guid.ToString()).Distinct())
                    {
                        if (guidToOtherData.TryGetValue(refGUID, out BuildLayout.DataFromOtherAsset dfoa))
                        {
                            dfoa.ReferencingAssets.Add(asset);
                            asset.InternalReferencedOtherAssets.Add(dfoa);
                        }
                        else if (buckets.TryGetValue(refGUID, out AssetBucket refBucket))
                        {
                            asset.InternalReferencedExplicitAssets.Add(refBucket.ExplictAsset);
                        }
                        else if (lookup.GuidToExplicitAsset.TryGetValue(refGUID, out BuildLayout.ExplicitAsset refAsset))
                        {
                            asset.ExternallyReferencedAssets.Add(refAsset);
                        }
                    }
                }
            }

            BuildLayout layout = new BuildLayout();

            // This is the addressables section. Everything above could technically be moved to SBP.
            {
                AddressableAssetsBuildContext aaContext = (AddressableAssetsBuildContext)m_AaBuildContext;
                // Map from GUID to AddrssableAssetEntry
                Dictionary <string, AddressableAssetEntry> guidToEntry = aaContext.assetEntries.ToDictionary(x => x.guid, x => x);

                // create groups
                foreach (AddressableAssetGroup group in aaContext.Settings.groups)
                {
                    var grp = new BuildLayout.Group();
                    grp.Name = group.Name;
                    grp.Guid = group.Guid;

                    foreach (AddressableAssetGroupSchema schema in group.Schemas)
                    {
                        var sd = new BuildLayout.SchemaData();
                        sd.Guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(schema));
                        sd.Type = schema.GetType().Name.ToString();

                        BundledAssetGroupSchema bSchema = schema as BundledAssetGroupSchema;
                        if (bSchema != null)
                        {
                            sd.KvpDetails.Add(new Tuple <string, string>("PackingMode", bSchema.BundleMode.ToString()));
                            sd.KvpDetails.Add(new Tuple <string, string>("Compression", bSchema.Compression.ToString()));
                        }
                        grp.Schemas.Add(sd);
                    }

                    lookup.GroupLookup.Add(group.Guid, grp);
                    layout.Groups.Add(grp);
                }

                // go through all the bundles and put them in groups
                foreach (BuildLayout.Bundle b in lookup.Bundles.Values)
                {
                    if (aaContext.bundleToImmediateBundleDependencies.TryGetValue(b.Name, out List <string> deps))
                    {
                        b.Dependencies = deps.Select(x => lookup.Bundles[x]).Where(x => b != x).ToList();
                    }
                    if (aaContext.bundleToExpandedBundleDependencies.TryGetValue(b.Name, out List <string> deps2))
                    {
                        b.ExpandedDependencies = deps2.Select(x => lookup.Bundles[x]).Where(x => b != x).ToList();
                    }

                    if (aaContext.bundleToAssetGroup.TryGetValue(b.Name, out string grpName))
                    {
                        lookup.GroupLookup[grpName].Bundles.Add(b);
                    }
                    else
                    {
                        layout.BuiltInBundles.Add(b);
                    }
                }

                // Apply the addressable name to the asset
                foreach (BuildLayout.ExplicitAsset a in BuildLayoutHelpers.EnumerateAssets(layout))
                {
                    if (guidToEntry.TryGetValue(a.Guid, out AddressableAssetEntry entry))
                    {
                        a.AddressableName = entry.address;
                    }
                }

                // The addressables build script can rename the bundles
                foreach (BuildLayout.Bundle b in BuildLayoutHelpers.EnumerateBundles(layout))
                {
                    if (m_BundleNameRemap.TryGetValue(b.Name, out string newName))
                    {
                        b.Name = newName;
                    }
                }
            }

            return(layout);
        }