using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.Build.Content; using UnityEditor.Build.Pipeline.Interfaces; using UnityEditor.Build.Pipeline.Utilities; using UnityEditor.Build.Player; using UnityEditor.Build.Utilities; using UnityEngine; internal class AutoBuildCacheUtility : IDisposable { public AutoBuildCacheUtility() { BuildCacheUtility.ClearCacheHashes(); } public void Dispose() { BuildCacheUtility.ClearCacheHashes(); } } internal static class BuildCacheUtility { internal static Dictionary, CacheEntry> m_GuidToHash = new Dictionary, CacheEntry>(); static Dictionary, CacheEntry> m_PathToHash = new Dictionary, CacheEntry>(); static Dictionary, CacheEntry> m_TypeToHash = new Dictionary, CacheEntry>(); static Dictionary m_ObjectToType = new Dictionary(); static TypeDB m_TypeDB; internal static HashSet m_ExplicitAssets = new HashSet(); #if !ENABLE_TYPE_HASHING static Hash128 m_UnityVersion = HashingMethods.Calculate(Application.unityVersion).ToHash128(); #endif public static void SetCurrentBuildContent(IBuildContent content) { m_ExplicitAssets.Clear(); foreach (GUID asset in content.Assets) m_ExplicitAssets.Add(asset); foreach (GUID scene in content.Scenes) m_ExplicitAssets.Add(scene); } public static CacheEntry GetCacheEntry(GUID asset, int version = 1) { CacheEntry entry; KeyValuePair key = new KeyValuePair(asset, version); if (m_GuidToHash.TryGetValue(key, out entry)) return entry; entry = new CacheEntry { Guid = asset, Version = version }; string path = AssetDatabase.GUIDToAssetPath(asset.ToString()); entry.Type = CacheEntry.EntryType.Asset; if (path.Equals(CommonStrings.UnityBuiltInExtraPath, StringComparison.OrdinalIgnoreCase) || path.Equals(CommonStrings.UnityDefaultResourcePath, StringComparison.OrdinalIgnoreCase)) entry.Hash = HashingMethods.Calculate(Application.unityVersion, path).ToHash128(); else { entry.Hash = AssetDatabase.GetAssetDependencyHash(path); if (!entry.Hash.isValid && File.Exists(path)) entry.Hash = HashingMethods.CalculateFile(path).ToHash128(); if (path.EndsWith(".unity", StringComparison.OrdinalIgnoreCase)) entry.Hash = HashingMethods.Calculate(entry.Hash, BuildInterfacesWrapper.SceneCallbackVersionHash, PlayerSettings.stripUnusedMeshComponents).ToHash128(); } if (entry.Hash.isValid) entry.Hash = HashingMethods.Calculate(entry.Hash, entry.Version).ToHash128(); entry.Inclusion = m_ExplicitAssets.Contains(asset) ? CacheEntry.InclusionType.Explicit : CacheEntry.InclusionType.Implicit; m_GuidToHash[key] = entry; return entry; } public static CacheEntry GetCacheEntry(string path, int version = 1) { CacheEntry entry; KeyValuePair key = new KeyValuePair(path, version); if (m_PathToHash.TryGetValue(key, out entry)) return entry; var guid = AssetDatabase.AssetPathToGUID(path); if (!string.IsNullOrEmpty(guid)) return GetCacheEntry(new GUID(guid), version); entry = new CacheEntry { File = path, Version = version }; entry.Guid = HashingMethods.Calculate("FileHash", entry.File).ToGUID(); if (File.Exists(entry.File)) entry.Hash = HashingMethods.Calculate(HashingMethods.CalculateFile(entry.File), entry.Version).ToHash128(); entry.Type = CacheEntry.EntryType.File; m_PathToHash[key] = entry; return entry; } public static CacheEntry GetCacheEntry(Type type, int version = 1) { CacheEntry entry; KeyValuePair key = new KeyValuePair(type, version); if (m_TypeToHash.TryGetValue(key, out entry)) return entry; entry = new CacheEntry { ScriptType = type.AssemblyQualifiedName, Version = version }; entry.Guid = HashingMethods.Calculate("TypeHash", entry.ScriptType).ToGUID(); #if ENABLE_TYPE_HASHING entry.Hash = ContentBuildInterface.CalculatePlayerSerializationHashForType(type, m_TypeDB); #else entry.Hash = m_TypeDB != null ? m_TypeDB.GetHash128() : m_UnityVersion; #endif entry.Type = CacheEntry.EntryType.ScriptType; m_TypeToHash[key] = entry; return entry; } static Type[] GetCachedTypesForObject(ObjectIdentifier objectId) { if (!m_ObjectToType.TryGetValue(objectId, out Type[] types)) { #if ENABLE_TYPE_HASHING types = ContentBuildInterface.GetTypesForObject(objectId); #else types = ContentBuildInterface.GetTypeForObjects(new[] { objectId }); #endif m_ObjectToType[objectId] = types; } return types; } public static Type GetMainTypeForObject(ObjectIdentifier objectId) { Type[] types = GetCachedTypesForObject(objectId); return types[0]; } public static Type[] GetMainTypeForObjects(IEnumerable objectIds) { List results = new List(); foreach (var objectId in objectIds) { Type[] types = GetCachedTypesForObject(objectId); results.Add(types[0]); } return results.ToArray(); } public static Type[] GetSortedUniqueTypesForObject(ObjectIdentifier objectId) { Type[] types = GetCachedTypesForObject(objectId); Array.Sort(types, (x, y) => x.AssemblyQualifiedName.CompareTo(y.AssemblyQualifiedName)); return types; } public static Type[] GetSortedUniqueTypesForObjects(IEnumerable objectIds) { Type[] types; HashSet results = new HashSet(); foreach (var objectId in objectIds) { types = GetCachedTypesForObject(objectId); results.UnionWith(types); } types = results.ToArray(); Array.Sort(types, (x, y) => x.AssemblyQualifiedName.CompareTo(y.AssemblyQualifiedName)); return types; } public static void SetTypeForObjects(IEnumerable pairs) { foreach (var pair in pairs) m_ObjectToType[pair.ObjectID] = pair.Types; } internal static void ClearCacheHashes() { m_GuidToHash.Clear(); m_PathToHash.Clear(); m_TypeToHash.Clear(); m_ObjectToType.Clear(); m_TypeDB = null; } public static void SetTypeDB(TypeDB typeDB) { if (m_TypeToHash.Count > 0) throw new InvalidOperationException("Changing Player TypeDB mid build is not supported at this time."); m_TypeDB = typeDB; } public static CacheEntry GetCacheEntry(ObjectIdentifier objectID, int version = 1) { if (objectID.guid.Empty()) return GetCacheEntry(objectID.filePath, version); return GetCacheEntry(objectID.guid, version); } }