using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using UnityEditor.Build.Content; using UnityEngine; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.Util; using UnityEngine.Serialization; using UnityEngine.U2D; namespace UnityEditor.AddressableAssets.Settings { interface IReferenceEntryData { string AssetPath { get; } string address { get; set; } bool IsInResources { get; set; } HashSet labels { get; } } internal struct ImplicitAssetEntry : IReferenceEntryData { public string AssetPath { get; set; } public string address { get; set; } public bool IsInResources { get; set; } public HashSet labels { get; set; } } /// /// Contains data for an addressable asset entry. /// [Serializable] public class AddressableAssetEntry : ISerializationCallbackReceiver, IReferenceEntryData { #pragma warning disable 0618 static Type typeof_AddressableAssetEntryCollection = typeof(AddressableAssetEntryCollection); #pragma warning restore 0618 internal const string EditorSceneListName = "EditorSceneList"; internal const string EditorSceneListPath = "Scenes In Build"; internal const string ResourcesName = "Resources"; internal const string ResourcesPath = "*/Resources/"; [FormerlySerializedAs("m_guid")] [SerializeField] string m_GUID; [FormerlySerializedAs("m_address")] [SerializeField] string m_Address; [FormerlySerializedAs("m_readOnly")] [SerializeField] bool m_ReadOnly; [FormerlySerializedAs("m_serializedLabels")] [SerializeField] List m_SerializedLabels; HashSet m_Labels = new HashSet(); /// /// If true, this asset was changed after being built into an Addressable Group marked 'Cannot Change Post Release'. /// [SerializeField] public bool FlaggedDuringContentUpdateRestriction = false; internal virtual bool HasSettings() { return false; } /// /// Flag indicating if an AssetEntry is a folder or not. /// public bool IsFolder { get; set; } /// /// List of AddressableAssetEntries that are considered sub-assets of a main Asset. Typically used for Folder entires. /// [NonSerialized] public List SubAssets = new List(); [NonSerialized] AddressableAssetGroup m_ParentGroup; /// /// The asset group that this entry belongs to. An entry can only belong to a single group at a time. /// public AddressableAssetGroup parentGroup { get { return m_ParentGroup; } set { m_ParentGroup = value; } } /// /// The id for the bundle file. /// public string BundleFileId { get; set; } /// /// The asset guid. /// public string guid { get { return m_GUID; } } /// /// The address of the entry. This is treated as the primary key in the ResourceManager system. /// public string address { get { return m_Address; } set { SetAddress(value); } } /// /// Set the address of the entry. /// /// The address. /// Post modification event. public void SetAddress(string addr, bool postEvent = true) { if (m_Address != addr) { m_Address = addr; if (string.IsNullOrEmpty(m_Address)) m_Address = AssetPath; if (m_GUID.Length > 0 && m_Address.Contains('[') && m_Address.Contains(']')) Debug.LogErrorFormat("Address '{0}' cannot contain '[ ]'.", m_Address); SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent); } } /// /// Read only state of the entry. /// public bool ReadOnly { get { return m_ReadOnly; } set { if (m_ReadOnly != value) { m_ReadOnly = value; SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, true); } } } /// /// Is the asset in a resource folder. /// public bool IsInResources { get; set; } /// /// Is scene in scene list. /// public bool IsInSceneList { get; set; } /// /// Is a sub asset. For example an asset in an addressable folder. /// public bool IsSubAsset { get; set; } /// /// Stores a reference to the parent entry. Only used if the asset is a sub asset. /// public AddressableAssetEntry ParentEntry { get; set; } bool m_CheckedIsScene; bool m_IsScene; /// /// Is this entry for a scene. /// public bool IsScene { get { if (!m_CheckedIsScene) { m_CheckedIsScene = true; m_IsScene = AssetPath.EndsWith(".unity", StringComparison.OrdinalIgnoreCase); } return m_IsScene; } } /// /// The set of labels for this entry. There is no inherent limit to the number of labels. /// public HashSet labels { get { return m_Labels; } } internal Type m_cachedMainAssetType = null; /// /// The System.Type of the main Object referenced by an AddressableAssetEntry /// public Type MainAssetType { get { if (m_cachedMainAssetType == null) { m_cachedMainAssetType = AssetDatabase.GetMainAssetTypeAtPath(AssetPath); if (m_cachedMainAssetType == null) return typeof(object); // do not cache a bad type lookup. } return m_cachedMainAssetType; } } /// /// Set or unset a label on this entry. /// /// The label name. /// Setting to true will add the label, false will remove it. /// When enable is true, setting force to true will force the label to exist on the parent AddressableAssetSettings object if it does not already. /// Post modification event. /// public bool SetLabel(string label, bool enable, bool force = false, bool postEvent = true) { if (enable) { if (force) parentGroup.Settings.AddLabel(label, postEvent); if (m_Labels.Add(label)) { SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent); return true; } } else { if (m_Labels.Remove(label)) { SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent); return true; } } return false; } /// /// Creates a list of keys that can be used to load this entry. /// /// The list of keys. This will contain the address, the guid as a Hash128 if valid, all assigned labels, and the scene index if applicable. public List CreateKeyList() => CreateKeyList(true, true, true); /// /// Creates a list of keys that can be used to load this entry. /// /// The list of keys. This will contain the address, the guid as a Hash128 if valid, all assigned labels, and the scene index if applicable. internal List CreateKeyList(bool includeAddress, bool includeGUID, bool includeLabels) { var keys = new List(); //the address must be the first key if (includeAddress) keys.Add(address); if (includeGUID && !string.IsNullOrEmpty(guid)) keys.Add(guid); if (IsScene && IsInSceneList) { int index = BuiltinSceneCache.GetSceneIndex(new GUID(guid)); if (index != -1) keys.Add(index); } if (includeLabels && labels != null && labels.Count > 0) { var labelsToRemove = new HashSet(); var currentLabels = parentGroup.Settings.GetLabels(); foreach (var l in labels) { if (currentLabels.Contains(l)) keys.Add(l); else labelsToRemove.Add(l); } foreach (var l in labelsToRemove) labels.Remove(l); } return keys; } internal AddressableAssetEntry(string guid, string address, AddressableAssetGroup parent, bool readOnly) { if (guid.Length > 0 && address.Contains('[') && address.Contains(']')) Debug.LogErrorFormat("Address '{0}' cannot contain '[ ]'.", address); m_GUID = guid; m_Address = address; m_ReadOnly = readOnly; parentGroup = parent; IsInResources = false; IsInSceneList = false; } Hash128 m_CurrentHash; internal Hash128 currentHash { get { if (!m_CurrentHash.isValid) { m_CurrentHash.Append(m_GUID); m_CurrentHash.Append(m_Address); m_CurrentHash.Append(m_Labels.Count); var flags = (m_ReadOnly ? 1 : 0) | (IsInResources ? 2 : 0) | (IsInSceneList ? 4 : 0) | (IsSubAsset ? 8 : 0); m_CurrentHash.Append(flags); foreach (var l in m_Labels) m_CurrentHash.Append(l); } return m_CurrentHash; } } internal void SetDirty(AddressableAssetSettings.ModificationEvent e, object o, bool postEvent) { m_CurrentHash = default; if (parentGroup != null) parentGroup.SetDirty(e, o, postEvent, true); } internal void SetCachedPath(string newCachedPath) { if (newCachedPath != m_cachedAssetPath) { m_cachedAssetPath = newCachedPath; m_MainAsset = null; m_TargetAsset = null; } } internal void SetSubObjectType(Type type) { m_cachedMainAssetType = type; } internal string m_cachedAssetPath = null; /// /// The path of the asset. /// public string AssetPath { get { if (string.IsNullOrEmpty(m_cachedAssetPath)) { if (string.IsNullOrEmpty(guid)) SetCachedPath(string.Empty); else if (guid == EditorSceneListName) SetCachedPath(EditorSceneListPath); else if (guid == ResourcesName) SetCachedPath(ResourcesPath); else SetCachedPath(AssetDatabase.GUIDToAssetPath(guid)); } return m_cachedAssetPath; } } private UnityEngine.Object m_MainAsset; /// /// The main asset object for this entry. /// public UnityEngine.Object MainAsset { get { if (m_MainAsset == null || !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_MainAsset, out string guid, out long localId)) { AddressableAssetEntry e = this; while (string.IsNullOrEmpty(e.AssetPath)) { if (e.ParentEntry == null) return null; e = e.ParentEntry; } m_MainAsset = AssetDatabase.LoadMainAssetAtPath(e.AssetPath); } return m_MainAsset; } } private UnityEngine.Object m_TargetAsset; /// /// The asset object for this entry. /// public UnityEngine.Object TargetAsset { get { if (m_TargetAsset == null || !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_TargetAsset, out string guid, out long localId)) { if (!string.IsNullOrEmpty(AssetPath) || !IsSubAsset) { m_TargetAsset = MainAsset; return m_TargetAsset; } if (ParentEntry == null || !string.IsNullOrEmpty(AssetPath) || string.IsNullOrEmpty(ParentEntry.AssetPath)) return null; var mainAsset = ParentEntry.MainAsset; if (ResourceManagerConfig.ExtractKeyAndSubKey(address, out string mainKey, out string subObjectName)) { if (mainAsset != null && mainAsset.GetType() == typeof(SpriteAtlas)) { m_TargetAsset = (mainAsset as SpriteAtlas).GetSprite(subObjectName); return m_TargetAsset; } var subObjects = AssetDatabase.LoadAllAssetRepresentationsAtPath(ParentEntry.AssetPath); foreach (var s in subObjects) { if (s != null && s.name == subObjectName) { m_TargetAsset = s; break; } } } } return m_TargetAsset; } } /// /// The asset load path. This is used to determine the internal id of resource locations. /// /// True if the asset will be contained in an asset bundle. /// Return the runtime path that should be used to load this entry. public string GetAssetLoadPath(bool isBundled) { return GetAssetLoadPath(isBundled, null); } /// /// The asset load path. This is used to determine the internal id of resource locations. /// /// True if the asset will be contained in an asset bundle. /// The internal ids of the asset, typically shortened versions of the asset's GUID. /// Return the runtime path that should be used to load this entry. public string GetAssetLoadPath(bool isBundled, HashSet otherLoadPaths) { if (!IsScene) { if (IsInResources) { return GetResourcesPath(AssetPath); } else { if (isBundled) return parentGroup.GetSchema().GetAssetLoadPath(AssetPath, otherLoadPaths, p => guid); return AssetPath; } } else { if (isBundled) return parentGroup.GetSchema().GetAssetLoadPath(AssetPath, otherLoadPaths, p => guid); var path = AssetPath; int i = path.LastIndexOf(".unity", StringComparison.OrdinalIgnoreCase); if (i > 0) path = path.Substring(0, i); i = path.IndexOf("assets/", StringComparison.OrdinalIgnoreCase); if (i == 0) path = path.Substring("assets/".Length); return path; } } static string GetResourcesPath(string path) { path = path.Replace('\\', '/'); int ri = path.LastIndexOf("/resources/", StringComparison.OrdinalIgnoreCase); if (ri >= 0) path = path.Substring(ri + "/resources/".Length); int i = path.LastIndexOf('.'); if (i > 0) path = path.Substring(0, i); return path; } /// /// Gathers all asset entries. Each explicit entry may contain multiple sub entries. For example, addressable folders create entries for each asset contained within. /// /// The generated list of entries. For simple entries, this will contain just the entry itself if specified. /// Determines if the entry should be contained in the result list or just sub entries. /// Determines if full recursion should be done when gathering entries. /// Determines if sub objects such as sprites should be included. /// Optional predicate to run against each entry, only returning those that pass. A null filter will return all entries public void GatherAllAssets(List assets, bool includeSelf, bool recurseAll, bool includeSubObjects, Func entryFilter = null) { if (assets == null) assets = new List(); if (guid == EditorSceneListName) { GatherEditorSceneEntries(assets, entryFilter); } else if (guid == ResourcesName) { GatherResourcesEntries(assets, recurseAll, entryFilter); } else { if (string.IsNullOrEmpty(AssetPath)) return; if (AssetDatabase.IsValidFolder(AssetPath)) { IsFolder = true; List folderEntries = new List(); GatherFolderEntries(folderEntries, recurseAll, includeSubObjects, entryFilter); SubAssets = folderEntries; assets.AddRange(folderEntries); } else { #pragma warning disable 0618 bool isEntryCollection = MainAssetType == typeof_AddressableAssetEntryCollection; #pragma warning restore 0618 if (isEntryCollection) { GatherAssetEntryCollectionEntries(assets, entryFilter); } else { if (entryFilter == null || entryFilter(this)) { if (includeSelf) assets.Add(this); if (includeSubObjects) { var mainType = AssetDatabase.GetMainAssetTypeAtPath(AssetPath); if (mainType == typeof(SpriteAtlas)) GatherSpriteAtlasEntries(assets); else GatherSubObjectEntries(assets); } } } } } } void GatherSubObjectEntries(List assets) { var objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetPath); for (int i = 0; i < objs.Length; i++) { var o = objs[i]; var namedAddress = string.Format("{0}[{1}]", address, o == null ? "missing reference" : o.name); if (o == null) { Debug.LogWarning(string.Format("NullReference in entry {0}\nAssetPath: {1}\nAddressableAssetGroup: {2}", address, AssetPath, parentGroup.Name)); assets.Add(new AddressableAssetEntry("", namedAddress, parentGroup, true)); } else { var newEntry = parentGroup.Settings.CreateEntry("", namedAddress, parentGroup, true); newEntry.IsSubAsset = true; newEntry.ParentEntry = this; newEntry.IsInResources = IsInResources; newEntry.SetSubObjectType(o.GetType()); assets.Add(newEntry); } } } void GatherSpriteAtlasEntries(List assets) { var settings = parentGroup.Settings; var atlas = AssetDatabase.LoadAssetAtPath(AssetPath); var sprites = new Sprite[atlas.spriteCount]; atlas.GetSprites(sprites); for (int i = 0; i < atlas.spriteCount; i++) { var spriteName = sprites[i] == null ? "missing reference" : sprites[i].name; if (sprites[i] == null) { Debug.LogWarning(string.Format("NullReference in entry {0}\nAssetPath: {1}\nAddressableAssetGroup: {2}", address, AssetPath, parentGroup.Name)); assets.Add(new AddressableAssetEntry("", spriteName, parentGroup, true)); } else { if (spriteName.EndsWith("(Clone)", StringComparison.Ordinal)) spriteName = spriteName.Replace("(Clone)", ""); var namedAddress = string.Format("{0}[{1}]", address, spriteName); var newEntry = settings.CreateEntry("", namedAddress, parentGroup, true); newEntry.IsSubAsset = true; newEntry.ParentEntry = this; newEntry.IsInResources = IsInResources; assets.Add(newEntry); } } } #pragma warning disable 0618 internal void GatherAssetEntryCollectionEntries(List assets, Func entryFilter) { var settings = parentGroup.Settings; var col = AssetDatabase.LoadAssetAtPath(AssetPath); if (col != null) { foreach (var e in col.Entries) { var entry = settings.CreateSubEntryIfUnique(e.guid, e.address, this); if (entry != null) { entry.IsInResources = e.IsInResources; foreach (var l in e.labels) entry.SetLabel(l, true, false, false); foreach (var l in m_Labels) entry.SetLabel(l, true, false, false); if (entryFilter == null || entryFilter(entry)) assets.Add(entry); } } } } #pragma warning restore 0618 internal void GatherFolderEntries(List assets, bool recurseAll, bool includeSubObjects, Func entryFilter) { var path = AssetPath; var settings = parentGroup.Settings; foreach (var file in AddressablesFileEnumeration.EnumerateAddressableFolder(path, settings, recurseAll)) { var subGuid = AssetDatabase.AssetPathToGUID(file); var entry = settings.CreateSubEntryIfUnique(subGuid, address + GetRelativePath(file, path), this); if (entry != null) { entry.IsInResources = IsInResources; //if this is a sub-folder of Resources, copy it on down entry.m_Labels = m_Labels; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); if (includeSubObjects) { var mainType = AssetDatabase.GetMainAssetTypeAtPath(entry.AssetPath); if (mainType == typeof(SpriteAtlas)) entry.GatherSpriteAtlasEntries(assets); else entry.GatherSubObjectEntries(assets); } } } if (!recurseAll) { foreach (var fo in Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly)) { var folder = fo.Replace('\\', '/'); if (AssetDatabase.IsValidFolder(folder)) { var entry = settings.CreateSubEntryIfUnique(AssetDatabase.AssetPathToGUID(folder), address + GetRelativePath(folder, path), this); if (entry != null) { entry.IsInResources = IsInResources; //if this is a sub-folder of Resources, copy it on down entry.m_Labels = m_Labels; entry.IsFolder = true; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); } } } } } /// /// Gets an entry for this folder entry /// /// /// /// /// Assumes that this asset entry is a valid folder asset internal AddressableAssetEntry GetFolderSubEntry(string subAssetGuid, string subAssetPath) { string assetPath = AssetPath; if (string.IsNullOrEmpty(assetPath) || !subAssetPath.StartsWith(assetPath, StringComparison.Ordinal)) return null; var settings = parentGroup.Settings; AddressableAssetEntry assetEntry = settings.FindAssetEntry(subAssetGuid); if (assetEntry != null) { if (assetEntry.IsSubAsset && assetEntry.ParentEntry == this) return assetEntry; return null; } string relativePath = subAssetPath.Remove(0, assetPath.Length + 1); string[] splitRelativePath = relativePath.Split('/'); string folderPath = assetPath; for (int i = 0; i < splitRelativePath.Length - 1; ++i) { folderPath = folderPath + "/" + splitRelativePath[i]; string folderGuid = AssetDatabase.AssetPathToGUID(folderPath); if (!AddressableAssetUtility.IsPathValidForEntry(folderPath)) return null; var folderEntry = settings.CreateSubEntryIfUnique(folderGuid, address + "/" + folderPath.Remove(assetPath.Length), this); if (folderEntry != null) { folderEntry.IsInResources = IsInResources; folderEntry.m_Labels = m_Labels; folderEntry.IsFolder = true; } else return null; } assetEntry = settings.CreateSubEntryIfUnique(subAssetGuid, address + "/" + relativePath, this); if (assetEntry != null) { assetEntry.m_Labels = m_Labels; assetEntry.IsFolder = false; } if (assetEntry == null || assetEntry.IsSubAsset == false) return null; return assetEntry; } #pragma warning disable 0618 internal AddressableAssetEntry GetAssetCollectionSubEntry(string subAssetGuid) { if (AssetPath.EndsWith(".asset", StringComparison.OrdinalIgnoreCase) && MainAssetType == typeof_AddressableAssetEntryCollection) { List implicitEntries = new List(); GatherAssetEntryCollectionEntries(implicitEntries, null); return implicitEntries.FirstOrDefault(ie => ie.guid == subAssetGuid); } return null; } #pragma warning restore 0618 internal void GatherResourcesEntries(List assets, bool recurseAll, Func entryFilter) { var settings = parentGroup.Settings; var pd = parentGroup.GetSchema(); if (pd.IncludeResourcesFolders) { foreach (var resourcesDir in GetResourceDirectories()) { foreach (var file in Directory.GetFiles(resourcesDir, "*.*", recurseAll ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)) { if (AddressableAssetUtility.IsPathValidForEntry(file)) { var g = AssetDatabase.AssetPathToGUID(file); var addr = GetResourcesPath(file); var entry = settings.CreateSubEntryIfUnique(g, addr, this); if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection { entry.IsInResources = true; entry.m_Labels = m_Labels; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); } } } if (!recurseAll) { foreach (var folder in Directory.GetDirectories(resourcesDir)) { if (AssetDatabase.IsValidFolder(folder)) { var entry = settings.CreateSubEntryIfUnique(AssetDatabase.AssetPathToGUID(folder), GetResourcesPath(folder), this); if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection { entry.IsInResources = true; entry.m_Labels = m_Labels; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); } } } } } } } static IEnumerable GetResourceDirectories() { string[] resourcesGuids = AssetDatabase.FindAssets("Resources", new string[] {"Assets", "Packages"}); foreach (string resourcesGuid in resourcesGuids) { string resourcesAssetPath = AssetDatabase.GUIDToAssetPath(resourcesGuid); if (resourcesAssetPath.EndsWith("/resources", StringComparison.OrdinalIgnoreCase) && AssetDatabase.IsValidFolder(resourcesAssetPath) && Directory.Exists(resourcesAssetPath)) { yield return resourcesAssetPath; } } } void GatherEditorSceneEntries(List assets, Func entryFilter) { var settings = parentGroup.Settings; var pd = parentGroup.GetSchema(); if (pd.IncludeBuildSettingsScenes) { foreach (var s in BuiltinSceneCache.scenes) { if (s.enabled) { var entry = settings.CreateSubEntryIfUnique(s.guid.ToString(), Path.GetFileNameWithoutExtension(s.path), this); if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection { entry.IsInSceneList = true; entry.m_Labels = m_Labels; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); } } } } } internal AddressableAssetEntry GetImplicitEntry(string implicitAssetGuid, string implicitAssetPath) { var e = GetAssetCollectionSubEntry(implicitAssetGuid); return e != null ? e : GetFolderSubEntry(implicitAssetGuid, implicitAssetPath); } string GetRelativePath(string file, string path) { return file.Substring(path.Length); } /// /// Implementation of ISerializationCallbackReceiver. Converts data to serializable form before serialization. /// public void OnBeforeSerialize() { m_SerializedLabels = new List(); foreach (var t in m_Labels) m_SerializedLabels.Add(t); } /// /// Implementation of ISerializationCallbackReceiver. Converts data from serializable form after deserialization. /// public void OnAfterDeserialize() { m_Labels = new HashSet(); foreach (var s in m_SerializedLabels) m_Labels.Add(s); m_SerializedLabels = null; m_cachedMainAssetType = null; m_cachedAssetPath = null; m_CheckedIsScene = false; m_MainAsset = null; m_TargetAsset = null; SubAssets?.Clear(); } /// /// Returns the address of the AddressableAssetEntry. /// /// The address of the AddressableAssetEntry public override string ToString() { return m_Address; } /// /// Create all entries for this addressable asset. This will expand subassets (Sprites, Meshes, etc) and also different representations. /// /// The list of entries to fill in. /// Whether the entry is bundles or not. This will affect the load path. /// The provider type for the main entry. /// Keys of dependencies /// Extra data to append to catalog entries. /// Any unknown provider types are added to this set in order to ensure they are not stripped. public void CreateCatalogEntries(List entries, bool isBundled, string providerType, IEnumerable dependencies, object extraData, HashSet providerTypes) { CreateCatalogEntries(entries, isBundled, providerType, dependencies, extraData, null, providerTypes, true, true, true, null); } /// /// Create all entries for this addressable asset. This will expand subassets (Sprites, Meshes, etc) and also different representations. /// /// The list of entries to fill in. /// Whether the entry is bundles or not. This will affect the load path. /// The provider type for the main entry. /// Keys of dependencies /// Extra data to append to catalog entries. /// Map of guids to AssetLoadInfo for object identifiers in an Asset. If null, ContentBuildInterface gathers object ids automatically. /// Any unknown provider types are added to this set in order to ensure they are not stripped. /// Flag indicating if address locations should be included /// Flag indicating if guid locations should be included /// Flag indicating if label locations should be included /// The internal ids of the asset, typically shortened versions of the asset's GUID. public void CreateCatalogEntries(List entries, bool isBundled, string providerType, IEnumerable dependencies, object extraData, Dictionary depInfo, HashSet providerTypes, bool includeAddress, bool includeGUID, bool includeLabels, HashSet assetsInBundle) { if (string.IsNullOrEmpty(AssetPath)) return; string assetPath = GetAssetLoadPath(isBundled, assetsInBundle); List keyList = CreateKeyList(includeAddress, includeGUID, includeLabels); if (keyList.Count == 0) return; //The asset may have previously been invalid. Since then, it may have been re-imported. //This can occur in particular when using ScriptedImporters with complex, multi-step behavior. //Double-check the type here in case the asset has been imported correctly after we cached its type. if (MainAssetType == typeof(DefaultAsset)) m_cachedMainAssetType = null; Type mainType = AddressableAssetUtility.MapEditorTypeToRuntimeType(MainAssetType, false); if ((mainType == null || mainType == typeof(DefaultAsset)) && !IsInResources) { var t = MainAssetType; Debug.LogWarningFormat("Type {0} is in editor assembly {1}. Asset location with internal id {2} will be stripped and not included in the build.", t.Name, t.Assembly.FullName, assetPath); return; } Type runtimeProvider = GetRuntimeProviderType(providerType, mainType); if (runtimeProvider != null) providerTypes.Add(runtimeProvider); if (!IsScene) { ObjectIdentifier[] ids = depInfo != null ? depInfo[new GUID(guid)].includedObjects.ToArray() : ContentBuildInterface.GetPlayerObjectIdentifiersInAsset(new GUID(guid), EditorUserBuildSettings.activeBuildTarget); foreach (var t in GatherMainAndReferencedSerializedTypes(ids)) entries.Add(new ContentCatalogDataEntry(t, assetPath, providerType, keyList, dependencies, extraData)); } else if (mainType != null && mainType != typeof(DefaultAsset)) { entries.Add(new ContentCatalogDataEntry(mainType, assetPath, providerType, keyList, dependencies, extraData)); } } static internal IEnumerable GatherMainAndReferencedSerializedTypes(ObjectIdentifier[] ids) { if (ids.Length > 0) { Type[] typesForObjs = ContentBuildInterface.GetTypeForObjects(ids); HashSet typesSeen = new HashSet(); foreach (var objType in typesForObjs) { if (!typeof(UnityEngine.Object).IsAssignableFrom(objType)) continue; if (typeof(Component).IsAssignableFrom(objType)) continue; Type rtType = AddressableAssetUtility.MapEditorTypeToRuntimeType(objType, false); if (rtType != null && rtType != typeof(DefaultAsset) && !typesSeen.Contains(rtType)) { yield return rtType; typesSeen.Add(rtType); } } } } static internal List GatherMainObjectTypes(ObjectIdentifier[] ids) { List typesSeen = new List(); foreach (ObjectIdentifier id in ids) { Type objType = ContentBuildInterface.GetTypeForObject(id); if (typeof(Component).IsAssignableFrom(objType)) continue; Type rtType = AddressableAssetUtility.MapEditorTypeToRuntimeType(objType, false); if (rtType != null && rtType != typeof(DefaultAsset) && !typesSeen.Contains(rtType)) typesSeen.Add(rtType); } return typesSeen; } internal Type GetRuntimeProviderType(string providerType, Type mainEntryType) { if (string.IsNullOrEmpty(providerType)) return null; if (mainEntryType == typeof(SpriteAtlas)) return typeof(AtlasSpriteProvider); return Assembly.GetAssembly(typeof(ResourceProviderBase)).GetType(providerType); } } }