Esempio n. 1
0
        internal static bool IsCacheDataValid(AddressableAssetSettings settings, AddressablesContentState cacheData)
        {
            if (cacheData == null)
            {
                return(false);
            }

            if (cacheData.editorVersion != Application.unityVersion)
            {
                Addressables.LogWarningFormat("Building content update with Unity editor version `{0}`, data was created with version `{1}`.  This may result in incompatible data.", Application.unityVersion, cacheData.editorVersion);
            }

            if (string.IsNullOrEmpty(cacheData.remoteCatalogLoadPath))
            {
                Addressables.LogError("Previous build had 'Build Remote Catalog' disabled.  You cannot update a player that has no remote catalog specified");
                return(false);
            }
            if (!settings.BuildRemoteCatalog)
            {
                Addressables.LogError("Current settings have 'Build Remote Catalog' disabled.  You cannot update a player that has no remote catalog to look to.");
                return(false);
            }

            if (cacheData.remoteCatalogLoadPath != settings.RemoteCatalogLoadPath.GetValue(settings))
            {
                Addressables.LogErrorFormat("Current 'Remote Catalog Load Path' does not match load path of original player.  Player will only know to look up catalog at original location. Original: {0}  Current: {1}", cacheData.remoteCatalogLoadPath, settings.RemoteCatalogLoadPath.GetValue(settings));
                return(false);
            }

            return(true);
        }
        internal void SetAllValues(AddressableAssetSettings settings, BuildTargetGroup buildTargetGroup, BuildTarget buildTarget, string playerBuildVersion)
        {
            AddressableSettings = settings;

            TargetGroup           = buildTargetGroup;
            Target                = buildTarget;
            PlayerVersion         = playerBuildVersion;
            ProfilerEventsEnabled = ProjectConfigData.PostProfilerEvents;
            Registry              = new FileRegistry();
            PreviousContentState  = null;
        }
Esempio n. 3
0
        /// <summary>
        /// Save the content update information for a set of AddressableAssetEntry objects.
        /// </summary>
        /// <param name="path">File to write content stat info to.  If file already exists, it will be deleted before the new file is created.</param>
        /// <param name="entries">The entries to save.</param>
        /// <param name="dependencyData">The raw dependency information generated from the build.</param>
        /// <param name="playerVersion">The player version to save. This is usually set to AddressableAssetSettings.PlayerBuildVersion.</param>
        /// <param name="remoteCatalogPath">The server path (if any) that contains an updateable content catalog.  If this is empty, updates cannot occur.</param>
        /// <returns>True if the file is saved, false otherwise.</returns>
        public static bool SaveContentState(string path, List <AddressableAssetEntry> entries, IDependencyData dependencyData, string playerVersion, string remoteCatalogPath)
        {
            try
            {
                IList <CachedAssetState> cachedInfos = new List <CachedAssetState>();
                foreach (var assetData in dependencyData.AssetInfo)
                {
                    CachedAssetState cachedAssetState;
                    if (GetCachedAssetStateForData(assetData.Key, assetData.Value.referencedObjects.Select(x => x.guid), out cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }
                foreach (var sceneData in dependencyData.SceneInfo)
                {
                    CachedAssetState cachedAssetState;
                    if (GetCachedAssetStateForData(sceneData.Key, sceneData.Value.referencedObjects.Select(x => x.guid), out cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }

                var cacheData = new AddressablesContentState
                {
                    cachedInfos           = cachedInfos.ToArray(),
                    playerVersion         = playerVersion,
                    editorVersion         = Application.unityVersion,
                    remoteCatalogLoadPath = remoteCatalogPath
                };
                var formatter = new BinaryFormatter();
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                var dir = Path.GetDirectoryName(path);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                formatter.Serialize(stream, cacheData);
                stream.Flush();
                stream.Close();
                stream.Dispose();
                return(true);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return(false);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Get a Dictionary of all modified values and their dependencies.  Dependencies will be Addressable and part of a group
        /// with static content enabled.
        /// </summary>
        /// <param name="settings">Addressable asset settings.</param>
        /// <param name="cachePath">The cache data path.</param>
        /// <returns>A dictionary mapping explicit changed entries to their dependencies.</returns>
        public static Dictionary <AddressableAssetEntry, List <AddressableAssetEntry> > GatherModifiedEntriesWithDependencies(AddressableAssetSettings settings, string cachePath)
        {
            var modifiedData = new Dictionary <AddressableAssetEntry, List <AddressableAssetEntry> >();
            AddressablesContentState cacheData = LoadContentState(cachePath);

            if (cacheData == null)
            {
                return(modifiedData);
            }

            GatherExplicitModifiedEntries(settings, ref modifiedData, cacheData);
            GetStaticContentDependenciesForEntries(settings, ref modifiedData, GetGroupGuidToCacheBundleNameMap(cacheData));
            return(modifiedData);
        }
Esempio n. 5
0
        internal static Dictionary <string, string> GetGroupGuidToCacheBundleNameMap(AddressablesContentState cacheData)
        {
            var bundleIdToCacheInfo = new Dictionary <string, string>();

            foreach (CachedBundleState bundleInfo in cacheData.cachedBundles)
            {
                if (bundleInfo != null && bundleInfo.data is AssetBundleRequestOptions options)
                {
                    bundleIdToCacheInfo[bundleInfo.bundleFileId] = options.BundleName;
                }
            }

            var groupGuidToCacheBundleName = new Dictionary <string, string>();

            foreach (CachedAssetState cacheInfo in cacheData.cachedInfos)
            {
                if (cacheInfo != null && bundleIdToCacheInfo.TryGetValue(cacheInfo.bundleFileId, out string bundleName))
                {
                    groupGuidToCacheBundleName[cacheInfo.groupGuid] = bundleName;
                }
            }
            return(groupGuidToCacheBundleName);
        }
Esempio n. 6
0
        internal static void GatherExplicitModifiedEntries(AddressableAssetSettings settings, ref Dictionary <AddressableAssetEntry, List <AddressableAssetEntry> > dependencyMap, AddressablesContentState cacheData)
        {
            List <string> noBundledAssetGroupSchema = new List <string>();
            List <string> noStaticContent           = new List <string>();

            var allEntries = new List <AddressableAssetEntry>();

            settings.GetAllAssets(allEntries, false, g =>
            {
                if (g == null)
                {
                    return(false);
                }

                if (!g.HasSchema <BundledAssetGroupSchema>())
                {
                    noBundledAssetGroupSchema.Add(g.Name);
                    return(false);
                }

                if (!g.HasSchema <ContentUpdateGroupSchema>())
                {
                    noStaticContent.Add(g.Name);
                    return(false);
                }

                if (!g.GetSchema <ContentUpdateGroupSchema>().StaticContent)
                {
                    noStaticContent.Add(g.Name);
                    return(false);
                }

                return(true);
            });

            StringBuilder builder = new StringBuilder();

            builder.AppendFormat("Skipping Prepare for Content Update on {0} group(s):\n\n",
                                 noBundledAssetGroupSchema.Count + noStaticContent.Count);


            AddInvalidGroupsToLogMessage(builder, noBundledAssetGroupSchema, "Group Did Not Contain BundledAssetGroupSchema");
            AddInvalidGroupsToLogMessage(builder, noStaticContent, "Static Content Not Enabled In Schemas");

            Debug.Log(builder.ToString());

            var entryToCacheInfo = new Dictionary <string, CachedAssetState>();

            foreach (var cacheInfo in cacheData.cachedInfos)
            {
                if (cacheInfo != null)
                {
                    entryToCacheInfo[cacheInfo.asset.guid.ToString()] = cacheInfo;
                }
            }
            var modifiedEntries = new List <AddressableAssetEntry>();

            foreach (var entry in allEntries)
            {
                CachedAssetState cachedInfo;
                if (!entryToCacheInfo.TryGetValue(entry.guid, out cachedInfo) || HasAssetOrDependencyChanged(cachedInfo))
                {
                    modifiedEntries.Add(entry);
                }
            }

            AddAllDependentScenesFromModifiedEntries(modifiedEntries);
            foreach (var entry in modifiedEntries)
            {
                if (!dependencyMap.ContainsKey(entry))
                {
                    dependencyMap.Add(entry, new List <AddressableAssetEntry>());
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Save the content update information for a set of AddressableAssetEntry objects.
        /// </summary>
        /// <param name="locations">The ContentCatalogDataEntry locations that were built into the Content Catalog.</param>
        /// <param name="path">File to write content stat info to.  If file already exists, it will be deleted before the new file is created.</param>
        /// <param name="entries">The entries to save.</param>
        /// <param name="dependencyData">The raw dependency information generated from the build.</param>
        /// <param name="playerVersion">The player version to save. This is usually set to AddressableAssetSettings.PlayerBuildVersion.</param>
        /// <param name="remoteCatalogPath">The server path (if any) that contains an updateable content catalog.  If this is empty, updates cannot occur.</param>
        /// <param name="carryOverCacheState">Cached state that needs to carry over from the previous build.  This mainly affects Content Update.</param>
        /// <returns>True if the file is saved, false otherwise.</returns>
        public static bool SaveContentState(List <ContentCatalogDataEntry> locations, string path, List <AddressableAssetEntry> entries, IDependencyData dependencyData, string playerVersion, string remoteCatalogPath, List <CachedAssetState> carryOverCacheState)
        {
            try
            {
                var cachedInfos = GetCachedAssetStates(locations, entries, dependencyData);

                var cachedBundleInfos = new List <CachedBundleState>();
                foreach (ContentCatalogDataEntry ccEntry in locations)
                {
                    if (typeof(IAssetBundleResource).IsAssignableFrom(ccEntry.ResourceType))
                    {
                        cachedBundleInfos.Add(new CachedBundleState()
                        {
                            bundleFileId = ccEntry.InternalId, data = ccEntry.Data
                        });
                    }
                }

                if (carryOverCacheState != null)
                {
                    foreach (var cs in carryOverCacheState)
                    {
                        cachedInfos.Add(cs);
                    }
                }

                var cacheData = new AddressablesContentState
                {
                    cachedInfos           = cachedInfos.ToArray(),
                    playerVersion         = playerVersion,
                    editorVersion         = Application.unityVersion,
                    remoteCatalogLoadPath = remoteCatalogPath,
                    cachedBundles         = cachedBundleInfos.ToArray()
                };
                var formatter = new BinaryFormatter();
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                var dir = Path.GetDirectoryName(path);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                formatter.Serialize(stream, cacheData);
                stream.Flush();
                stream.Close();
                stream.Dispose();
                return(true);
            }
            catch (UnauthorizedAccessException uae)
            {
                if (!AddressableAssetUtility.IsVCAssetOpenForEdit(path))
                {
                    Debug.LogErrorFormat("Cannot access the file {0}. It may be locked by version control.", path);
                }
                else
                {
                    Debug.LogException(uae);
                }
                return(false);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return(false);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Save the content update information for a set of AddressableAssetEntry objects.
        /// </summary>
        /// <param name="locations">The ContentCatalogDataEntry locations that were built into the Content Catalog.</param>
        /// <param name="path">File to write content stat info to.  If file already exists, it will be deleted before the new file is created.</param>
        /// <param name="entries">The entries to save.</param>
        /// <param name="dependencyData">The raw dependency information generated from the build.</param>
        /// <param name="playerVersion">The player version to save. This is usually set to AddressableAssetSettings.PlayerBuildVersion.</param>
        /// <param name="remoteCatalogPath">The server path (if any) that contains an updateable content catalog.  If this is empty, updates cannot occur.</param>
        /// <returns>True if the file is saved, false otherwise.</returns>
        public static bool SaveContentState(List <ContentCatalogDataEntry> locations, string path, List <AddressableAssetEntry> entries, IDependencyData dependencyData, string playerVersion, string remoteCatalogPath)
        {
            try
            {
                IList <CachedAssetState> cachedInfos = new List <CachedAssetState>();
                foreach (var assetData in dependencyData.AssetInfo)
                {
                    AddressableAssetEntry   addressableAssetEntry = entries.FirstOrDefault((e) => e.guid == assetData.Key.ToString());
                    ContentCatalogDataEntry catalogAssetEntry     = locations.FirstOrDefault((e) =>
                    {
                        if (e.Keys.Count <= 1)
                        {
                            return(false);
                        }
                        return((e.Keys[1] as string) == assetData.Key.ToString());
                    });
                    CachedAssetState cachedAssetState;
                    if (addressableAssetEntry != null && catalogAssetEntry != null &&
                        GetCachedAssetStateForData(assetData.Key, addressableAssetEntry.BundleFileId, addressableAssetEntry.parentGroup.Guid, catalogAssetEntry.Data, assetData.Value.referencedObjects.Select(x => x.guid), out cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }
                foreach (var sceneData in dependencyData.SceneInfo)
                {
                    AddressableAssetEntry   addressableSceneEntry = entries.FirstOrDefault((e) => e.guid == sceneData.Key.ToString());
                    ContentCatalogDataEntry catalogSceneEntry     = locations.FirstOrDefault((e) =>
                    {
                        if (e.Keys.Count <= 1)
                        {
                            return(false);
                        }
                        return((e.Keys[1] as string) == sceneData.Key.ToString());
                    });
                    CachedAssetState cachedAssetState;
                    if (addressableSceneEntry != null && catalogSceneEntry != null &&
                        GetCachedAssetStateForData(sceneData.Key, addressableSceneEntry.BundleFileId, addressableSceneEntry.parentGroup.Guid, catalogSceneEntry.Data, sceneData.Value.referencedObjects.Select(x => x.guid), out cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }

                var cacheData = new AddressablesContentState
                {
                    cachedInfos           = cachedInfos.ToArray(),
                    playerVersion         = playerVersion,
                    editorVersion         = Application.unityVersion,
                    remoteCatalogLoadPath = remoteCatalogPath
                };
                var formatter = new BinaryFormatter();
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                var dir = Path.GetDirectoryName(path);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                formatter.Serialize(stream, cacheData);
                stream.Flush();
                stream.Close();
                stream.Dispose();
                return(true);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return(false);
            }
        }
        /// <summary>
        /// Save the content update information for a set of AddressableAssetEntry objects.
        /// </summary>
        /// <param name="locations">The ContentCatalogDataEntry locations that were built into the Content Catalog.</param>
        /// <param name="path">File to write content stat info to.  If file already exists, it will be deleted before the new file is created.</param>
        /// <param name="entries">The entries to save.</param>
        /// <param name="dependencyData">The raw dependency information generated from the build.</param>
        /// <param name="playerVersion">The player version to save. This is usually set to AddressableAssetSettings.PlayerBuildVersion.</param>
        /// <param name="remoteCatalogPath">The server path (if any) that contains an updateable content catalog.  If this is empty, updates cannot occur.</param>
        /// <param name="carryOverCacheState">Cached state that needs to carry over from the previous build.  This mainly affects Content Update.</param>
        /// <returns>True if the file is saved, false otherwise.</returns>
        public static bool SaveContentState(List <ContentCatalogDataEntry> locations, string path, List <AddressableAssetEntry> entries, IDependencyData dependencyData, string playerVersion, string remoteCatalogPath, List <CachedAssetState> carryOverCacheState)
        {
            try
            {
                Dictionary <string, AddressableAssetEntry>   guidToEntries   = new Dictionary <string, AddressableAssetEntry>();
                Dictionary <string, ContentCatalogDataEntry> key1ToCCEntries = new Dictionary <string, ContentCatalogDataEntry>();

                foreach (AddressableAssetEntry entry in entries)
                {
                    if (!guidToEntries.ContainsKey(entry.guid))
                    {
                        guidToEntries[entry.guid] = entry;
                    }
                }
                foreach (ContentCatalogDataEntry ccEntry in locations)
                {
                    if (ccEntry != null && ccEntry.Keys != null && ccEntry.Keys.Count > 1 && (ccEntry.Keys[1] as string) != null && !key1ToCCEntries.ContainsKey(ccEntry.Keys[1] as string))
                    {
                        key1ToCCEntries[ccEntry.Keys[1] as string] = ccEntry;
                    }
                }

                IList <CachedAssetState> cachedInfos = new List <CachedAssetState>();
                foreach (var assetData in dependencyData.AssetInfo)
                {
                    guidToEntries.TryGetValue(assetData.Key.ToString(), out AddressableAssetEntry addressableAssetEntry);
                    key1ToCCEntries.TryGetValue(assetData.Key.ToString(), out ContentCatalogDataEntry catalogAssetEntry);
                    if (addressableAssetEntry != null && catalogAssetEntry != null &&
                        GetCachedAssetStateForData(assetData.Key, addressableAssetEntry.BundleFileId, addressableAssetEntry.parentGroup.Guid, catalogAssetEntry.Data, assetData.Value.referencedObjects.Select(x => x.guid), out CachedAssetState cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }

                foreach (var sceneData in dependencyData.SceneInfo)
                {
                    guidToEntries.TryGetValue(sceneData.Key.ToString(), out AddressableAssetEntry addressableSceneEntry);
                    key1ToCCEntries.TryGetValue(sceneData.Key.ToString(), out ContentCatalogDataEntry catalogSceneEntry);
                    if (addressableSceneEntry != null && catalogSceneEntry != null &&
                        GetCachedAssetStateForData(sceneData.Key, addressableSceneEntry.BundleFileId, addressableSceneEntry.parentGroup.Guid, catalogSceneEntry.Data, sceneData.Value.referencedObjects.Select(x => x.guid), out CachedAssetState cachedAssetState))
                    {
                        cachedInfos.Add(cachedAssetState);
                    }
                }

                if (carryOverCacheState != null)
                {
                    foreach (var cs in carryOverCacheState)
                    {
                        cachedInfos.Add(cs);
                    }
                }

                var cacheData = new AddressablesContentState
                {
                    cachedInfos           = cachedInfos.ToArray(),
                    playerVersion         = playerVersion,
                    editorVersion         = Application.unityVersion,
                    remoteCatalogLoadPath = remoteCatalogPath
                };
                var formatter = new BinaryFormatter();
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                var dir = Path.GetDirectoryName(path);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write);
                formatter.Serialize(stream, cacheData);
                stream.Flush();
                stream.Close();
                stream.Dispose();
                return(true);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return(false);
            }
        }
        internal static void GetStaticContentDependenciesForEntries(AddressableAssetSettings settings, ref Dictionary <AddressableAssetEntry, List <AddressableAssetEntry> > dependencyMap, AddressablesContentState cacheData = null)
        {
            Dictionary <AddressableAssetGroup, bool> groupHasStaticContentMap = new Dictionary <AddressableAssetGroup, bool>();

            if (dependencyMap == null)
            {
                return;
            }

            HashSet <string> groupGuidsWithUnchangedBundleName = GetGroupGuidsWithUnchangedBundleName(settings, dependencyMap, cacheData);

            foreach (AddressableAssetEntry entry in dependencyMap.Keys)
            {
                //since the entry here is from our list of modified entries we know that it must be a part of a static content group.
                //Since it's part of a static content update group we can go ahead and set the value to true in the dictionary without explicitly checking it.
                if (!groupHasStaticContentMap.ContainsKey(entry.parentGroup))
                {
                    groupHasStaticContentMap.Add(entry.parentGroup, true);
                }

                string[] dependencies = AssetDatabase.GetDependencies(entry.AssetPath);
                foreach (string dependency in dependencies)
                {
                    string guid     = AssetDatabase.AssetPathToGUID(dependency);
                    var    depEntry = settings.FindAssetEntry(guid);
                    if (depEntry == null)
                    {
                        continue;
                    }

                    if (!groupHasStaticContentMap.TryGetValue(depEntry.parentGroup, out bool groupHasStaticContentEnabled))
                    {
                        groupHasStaticContentEnabled = depEntry.parentGroup.HasSchema <ContentUpdateGroupSchema>() &&
                                                       depEntry.parentGroup.GetSchema <ContentUpdateGroupSchema>().StaticContent;

                        if (groupGuidsWithUnchangedBundleName.Contains(depEntry.parentGroup.Guid))
                        {
                            continue;
                        }

                        groupHasStaticContentMap.Add(depEntry.parentGroup, groupHasStaticContentEnabled);
                    }

                    if (!dependencyMap.ContainsKey(depEntry) && groupHasStaticContentEnabled)
                    {
                        if (!dependencyMap.ContainsKey(entry))
                        {
                            dependencyMap.Add(entry, new List <AddressableAssetEntry>());
                        }
                        dependencyMap[entry].Add(depEntry);
                    }
                }
            }
        }
        internal static HashSet <string> GetGroupGuidsWithUnchangedBundleName(AddressableAssetSettings settings, Dictionary <AddressableAssetEntry, List <AddressableAssetEntry> > dependencyMap, AddressablesContentState cacheData)
        {
            var result = new HashSet <string>();

            if (cacheData == null)
            {
                return(result);
            }

            Dictionary <string, string> groupGuidToCacheBundleName = GetGroupGuidToCacheBundleNameMap(cacheData);

            foreach (AddressableAssetGroup group in settings.groups)
            {
                if (group == null || !group.HasSchema <BundledAssetGroupSchema>())
                {
                    continue;
                }

                var schema = group.GetSchema <BundledAssetGroupSchema>();
                List <AssetBundleBuild> bundleInputDefinitions = new List <AssetBundleBuild>();

                BuildScriptPackedMode.PrepGroupBundlePacking(group, bundleInputDefinitions, schema, x => !dependencyMap.ContainsKey(x));
                BuildScriptPackedMode.HandleDuplicateBundleNames(bundleInputDefinitions);

                for (int i = 0; i < bundleInputDefinitions.Count; i++)
                {
                    string bundleName = Path.GetFileNameWithoutExtension(bundleInputDefinitions[i].assetBundleName);
                    if (groupGuidToCacheBundleName.TryGetValue(group.Guid, out string cacheBundleName) && cacheBundleName == bundleName)
                    {
                        result.Add(group.Guid);
                    }
                }
            }
            return(result);
        }