using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine.AddressableAssets.Initialization; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.Util; using UnityEngine.SceneManagement; #if UNITY_EDITOR [assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")] #endif [assembly: InternalsVisibleTo("Unity.Addressables.Tests")] namespace UnityEngine.AddressableAssets { internal class AddressablesImpl { ResourceManager m_ResourceManager; IInstanceProvider m_InstanceProvider; public IInstanceProvider InstanceProvider { get { return m_InstanceProvider; } set { m_InstanceProvider = value; var rec = m_InstanceProvider as IUpdateReceiver; if (rec != null) m_ResourceManager.AddUpdateReceiver(rec); } } public ISceneProvider SceneProvider; public ResourceManager ResourceManager { get { if (m_ResourceManager == null) m_ResourceManager = new ResourceManager(new DefaultAllocationStrategy()); return m_ResourceManager; } } List m_ResourceLocators = new List(); AsyncOperationHandle m_InitializationOperation; Action m_OnHandleCompleteAction; Action> m_OnSceneHandleCompleteAction; Action m_OnHandleDestroyedAction; Dictionary m_resultToHandle = new Dictionary(); HashSet> m_SceneInstances = new HashSet>(); internal int SceneOperationCount { get { return m_SceneInstances.Count; } } internal int TrackedHandleCount { get { return m_resultToHandle.Count; } } public AddressablesImpl(IAllocationStrategy alloc) { m_ResourceManager = new ResourceManager(alloc); SceneManager.sceneUnloaded += OnSceneUnloaded; } private void OnSceneUnloaded(Scene scene) { foreach (var s in m_SceneInstances) { if (!s.IsValid()) { m_SceneInstances.Remove(s); break; } if (s.Result.Scene == scene) { m_SceneInstances.Remove(s); m_resultToHandle.Remove(s.Result); m_ResourceManager.ReleaseScene(SceneProvider, s); break; } } m_ResourceManager.CleanupSceneInstances(scene); } public string StreamingAssetsSubFolder { get { return "aa"; } } public string BuildPath { get { return "Library/com.unity.addressables/StreamingAssetsCopy/" + StreamingAssetsSubFolder + "/" + PlatformMappingService.GetPlatform(); } } public string PlayerBuildDataPath { get { return Application.streamingAssetsPath + "/" + StreamingAssetsSubFolder + "/" + PlatformMappingService.GetPlatform(); } } public string RuntimePath { get { #if UNITY_EDITOR return BuildPath; #else return PlayerBuildDataPath; #endif } } public void Log(string msg) { Debug.Log(msg); } public void LogFormat(string format, params object[] args) { Debug.LogFormat(format, args); } public void LogWarning(string msg) { Debug.LogWarning(msg); } public void LogWarningFormat(string format, params object[] args) { Debug.LogWarningFormat(format, args); } public void LogError(string msg) { Debug.LogError(msg); } public void LogException(AsyncOperationHandle op, Exception ex) { Debug.LogErrorFormat("{0} encountered in operation {1}: {2}", ex.GetType().Name, op.DebugName, ex.Message); } public void LogErrorFormat(string format, params object[] args) { Debug.LogErrorFormat(format, args); } public string ResolveInternalId(string id) { var path = AddressablesRuntimeProperties.EvaluateString(id); #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_XBOXONE if (path.Length >= 260 && path.StartsWith(Application.dataPath)) path = path.Substring(Application.dataPath.Length + 1); #endif return path; } public IList ResourceLocators { get { return m_ResourceLocators; } } internal bool GetResourceLocations(object key, Type type, out IList locations) { key = EvaluateKey(key); locations = null; HashSet current = null; foreach (var l in m_ResourceLocators) { IList locs; if (l.Locate(key, type, out locs)) { if (locations == null) { //simple, common case, no allocations locations = locs; } else { //less common, need to merge... if (current == null) { current = new HashSet(); foreach (var loc in locations) current.Add(loc); } current.UnionWith(locs); } } } if (current == null) return locations != null; locations = new List(current); return true; } internal bool GetResourceLocations(IEnumerable keys, Type type, Addressables.MergeMode merge, out IList locations) { locations = null; HashSet current = null; foreach (var key in keys) { IList locs; if (GetResourceLocations(key, type, out locs)) { if (locations == null) { locations = locs; if (merge == Addressables.MergeMode.UseFirst) return true; } else { if (current == null) { current = new HashSet(); foreach (var loc in locations) current.Add(loc); } if (merge == Addressables.MergeMode.Intersection) current.IntersectWith(locs); else if (merge == Addressables.MergeMode.Union) current.UnionWith(locs); } } else { //if entries for a key are not found, the intersection is empty if (merge == Addressables.MergeMode.Intersection) { locations = null; return false; } } } if (current == null) return locations != null; if (current.Count == 0) { locations = null; return false; } locations = new List(current); return true; } public AsyncOperationHandle InitializeAsync(string runtimeDataPath, string providerSuffix = null) { //these need to be referenced in order to prevent stripping on IL2CPP platforms. if (string.IsNullOrEmpty(Application.streamingAssetsPath)) Debug.LogWarning("Application.streamingAssetsPath has been stripped!"); #if !UNITY_SWITCH if (string.IsNullOrEmpty(Application.persistentDataPath)) Debug.LogWarning("Application.persistentDataPath has been stripped!"); #endif if (string.IsNullOrEmpty(runtimeDataPath)) return ResourceManager.CreateCompletedOperation(null, string.Format("Invalid Key: {0}", runtimeDataPath)); m_OnHandleCompleteAction = OnHandleCompleted; m_OnSceneHandleCompleteAction = OnSceneHandleCompleted; m_OnHandleDestroyedAction = OnHandleDestroyed; m_InitializationOperation = Initialization.InitializationOperation.CreateInitializationOperation(this, runtimeDataPath, providerSuffix); m_InitializationOperation.Completed += (x) => ResourceManager.ExceptionHandler = LogException; return m_InitializationOperation; } public AsyncOperationHandle InitializeAsync() { if (!m_InitializationOperation.IsValid()) return InitializeAsync(ResolveInternalId(PlayerPrefs.GetString(Addressables.kAddressablesRuntimeDataPath, RuntimePath + "/settings.json"))); return m_InitializationOperation; } public AsyncOperationHandle LoadContentCatalogAsync(string catalogPath, string providerSuffix = null) { var catalogLoc = new ResourceLocationBase(catalogPath, catalogPath, typeof(JsonAssetProvider).FullName, typeof(IResourceLocator)); if (!InitializationOperation.IsDone) return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadContentCatalogAsync(catalogPath, providerSuffix)); return Initialization.InitializationOperation.LoadContentCatalog(this, catalogLoc, providerSuffix); } public AsyncOperationHandle InitializationOperation { get { if (!m_InitializationOperation.IsValid()) InitializeAsync(); return m_InitializationOperation; } } AsyncOperationHandle TrackHandle(AsyncOperationHandle handle) { handle.Completed += m_OnSceneHandleCompleteAction; return handle; } AsyncOperationHandle TrackHandle(AsyncOperationHandle handle) { handle.CompletedTypeless += m_OnHandleCompleteAction; return handle; } AsyncOperationHandle TrackHandle(AsyncOperationHandle handle) { handle.Completed += m_OnHandleCompleteAction; return handle; } internal void ClearTrackHandles() { m_resultToHandle.Clear(); } public AsyncOperationHandle LoadAssetAsync(IResourceLocation location) { return TrackHandle(ResourceManager.ProvideResource(location)); } AsyncOperationHandle LoadAssetWithChain(object key) { return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadAssetAsync(key)); } public AsyncOperationHandle LoadAssetAsync(object key) { if (!InitializationOperation.IsDone) return LoadAssetWithChain(key); key = EvaluateKey(key); IList locs; var t = typeof(TObject); if (t.IsArray) t = t.GetElementType(); else if (t.IsGenericType && typeof(IList<>) == t.GetGenericTypeDefinition()) t = t.GetGenericArguments()[0]; for (int i = 0; i < m_ResourceLocators.Count; i++) { if (m_ResourceLocators[i].Locate(key, t, out locs)) { foreach (var loc in locs) { var provider = ResourceManager.GetResourceProvider(typeof(TObject), loc); if (provider != null) return TrackHandle(ResourceManager.ProvideResource(loc)); } } } return ResourceManager.CreateCompletedOperation(default(TObject), new InvalidKeyException(key).Message); } class LoadResourceLocationKeyOp : AsyncOperationBase> { object m_Key; IList m_locations; AddressablesImpl m_Addressables; Type m_ResourceType; protected override string DebugName { get { return m_Key.ToString(); } } public void Init(AddressablesImpl aa, Type t, object key) { m_Key = key; m_ResourceType = t; m_Addressables = aa; } protected override void Execute() { m_Addressables.GetResourceLocations(m_Key, m_ResourceType, out m_locations); if (m_locations == null) m_locations = new List(); Complete(m_locations, true, string.Empty); } } class LoadResourceLocationKeysOp : AsyncOperationBase> { IList m_Key; Addressables.MergeMode m_MergeMode; IList m_locations; AddressablesImpl m_Addressables; Type m_ResourceType; protected override string DebugName { get { return "LoadResourceLocationKeysOp"; } } public void Init(AddressablesImpl aa, Type t, IList key, Addressables.MergeMode mergeMode) { m_Key = key; m_ResourceType = t; m_MergeMode = mergeMode; m_Addressables = aa; } protected override void Execute() { m_Addressables.GetResourceLocations(m_Key, m_ResourceType, m_MergeMode, out m_locations); if (m_locations == null) m_locations = new List(); Complete(m_locations, true, string.Empty); } } public AsyncOperationHandle> LoadResourceLocationsAsync(IList keys, Addressables.MergeMode mode, Type type = null) { var op = new LoadResourceLocationKeysOp(); op.Init(this, type, keys, mode); return TrackHandle(ResourceManager.StartOperation(op, InitializationOperation)); } public AsyncOperationHandle> LoadResourceLocationsAsync(object key, Type type = null) { var op = new LoadResourceLocationKeyOp(); op.Init(this, type, key); return TrackHandle(ResourceManager.StartOperation(op, InitializationOperation)); } public AsyncOperationHandle> LoadAssetsAsync(IList locations, Action callback) { return TrackHandle(ResourceManager.ProvideResources(locations, callback)); } AsyncOperationHandle> LoadAssetsWithChain(IList keys, Action callback, Addressables.MergeMode mode) { return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadAssetsAsync(keys, callback, mode)); } public AsyncOperationHandle> LoadAssetsAsync(IList keys, Action callback, Addressables.MergeMode mode) { if (!InitializationOperation.IsDone) return LoadAssetsWithChain(keys, callback, mode); IList locations; if (!GetResourceLocations(keys, typeof(TObject), mode, out locations)) return ResourceManager.CreateCompletedOperation>(null, new InvalidKeyException(keys).Message); return LoadAssetsAsync(locations, callback); } AsyncOperationHandle> LoadAssetsWithChain(object key, Action callback) { return ResourceManager.CreateChainOperation(InitializationOperation, op2 => LoadAssetsAsync(key, callback)); } public AsyncOperationHandle> LoadAssetsAsync(object key, Action callback) { if (!InitializationOperation.IsDone) return LoadAssetsWithChain(key, callback); IList locations; if (!GetResourceLocations(key, typeof(TObject), out locations)) return ResourceManager.CreateCompletedOperation>(null, new InvalidKeyException(key).Message); return LoadAssetsAsync(locations, callback); } void OnHandleDestroyed(AsyncOperationHandle handle) { if (handle.Status == AsyncOperationStatus.Succeeded) { m_resultToHandle.Remove(handle.Result); } } void OnSceneHandleCompleted(AsyncOperationHandle handle) { if (handle.Status == AsyncOperationStatus.Succeeded) { m_SceneInstances.Add(handle); if (!m_resultToHandle.ContainsKey(handle.Result)) { handle.Destroyed += m_OnHandleDestroyedAction; m_resultToHandle.Add(handle.Result, handle); } } } void OnHandleCompleted(AsyncOperationHandle handle) { if (handle.Status == AsyncOperationStatus.Succeeded) { if (!m_resultToHandle.ContainsKey(handle.Result)) { handle.Destroyed += m_OnHandleDestroyedAction; m_resultToHandle.Add(handle.Result, handle); } } } public void Release(TObject obj) { if (obj == null) { LogWarning("Addressables.Release() - trying to release null object."); return; } AsyncOperationHandle handle; if (m_resultToHandle.TryGetValue(obj, out handle)) Release(handle); else { LogError("Addressables.Release was called on an object that Addressables was not previously aware of. Thus nothing is being released"); } } public void Release(AsyncOperationHandle handle) { m_ResourceManager.Release(handle); } public void Release(AsyncOperationHandle handle) { m_ResourceManager.Release(handle); } AsyncOperationHandle GetDownloadSizeWithChain(object key) { return ResourceManager.CreateChainOperation(InitializationOperation, op => GetDownloadSizeAsync(key)); } public AsyncOperationHandle GetDownloadSizeAsync(object key) { if (!InitializationOperation.IsDone) return GetDownloadSizeWithChain(key); IList locations; if (typeof(IList).IsAssignableFrom(key.GetType())) locations = key as IList; else if (typeof(IResourceLocation).IsAssignableFrom(key.GetType())) { locations = new List(1); locations.Add(key as IResourceLocation); } else { if (!GetResourceLocations(key, typeof(object), out locations)) return ResourceManager.CreateCompletedOperation(0, new InvalidKeyException(key).Message); } var locHash = new HashSet(); foreach (var loc in locations) { if (loc.HasDependencies) { foreach (var dep in loc.Dependencies) locHash.Add(dep); } } long size = 0; foreach (var d in locHash) { var sizeData = d.Data as ILocationSizeData; if (sizeData != null) size += sizeData.ComputeSize(d); } return ResourceManager.CreateCompletedOperation(size, string.Empty); } public AsyncOperationHandle DownloadDependenciesAsync(object key) { if (!InitializationOperation.IsDone) return ResourceManager.CreateChainOperation>(InitializationOperation, op => DownloadDependenciesAsync(key).Convert>()); IList locations; if (!GetResourceLocations(key, typeof(object), out locations)) return ResourceManager.CreateCompletedOperation>(null, new InvalidKeyException(key).Message); var locHash = new HashSet(); foreach (var loc in locations) { if (loc.HasDependencies) { foreach (var dep in loc.Dependencies) locHash.Add(dep); } } return LoadAssetsAsync(new List(locHash), null); } public AsyncOperationHandle DownloadDependenciesAsync(IList locations) { if (!InitializationOperation.IsDone) return ResourceManager.CreateChainOperation>(InitializationOperation, op => DownloadDependenciesAsync(locations).Convert>()); var locHash = new HashSet(); foreach (var loc in locations) { if (loc.HasDependencies) { foreach (var dep in loc.Dependencies) locHash.Add(dep); } } return LoadAssetsAsync(new List(locHash), null); } public AsyncOperationHandle DownloadDependenciesAsync(IList keys, Addressables.MergeMode mode) { if (!InitializationOperation.IsDone) return ResourceManager.CreateChainOperation>(InitializationOperation, op => DownloadDependenciesAsync(keys, mode).Convert>()); IList locations; if (!GetResourceLocations(keys, typeof(object), mode, out locations)) return ResourceManager.CreateCompletedOperation>(null, new InvalidKeyException(keys).Message); var locHash = new HashSet(); foreach (var loc in locations) { if (loc.HasDependencies) { foreach (var dep in loc.Dependencies) locHash.Add(dep); } } return LoadAssetsAsync(new List(locHash), null); } public AsyncOperationHandle InstantiateAsync(IResourceLocation location, Transform parent = null, bool instantiateInWorldSpace = false, bool trackHandle = true) { return InstantiateAsync(location, new InstantiationParameters(parent, instantiateInWorldSpace), trackHandle); } public AsyncOperationHandle InstantiateAsync(IResourceLocation location, Vector3 position, Quaternion rotation, Transform parent = null, bool trackHandle = true) { return InstantiateAsync(location, new InstantiationParameters(position, rotation, parent), trackHandle); } public AsyncOperationHandle InstantiateAsync(object key, Transform parent = null, bool instantiateInWorldSpace = false, bool trackHandle = true) { return InstantiateAsync(key, new InstantiationParameters(parent, instantiateInWorldSpace), trackHandle); } public AsyncOperationHandle InstantiateAsync(object key, Vector3 position, Quaternion rotation, Transform parent = null, bool trackHandle = true) { return InstantiateAsync(key, new InstantiationParameters(position, rotation, parent), trackHandle); } AsyncOperationHandle InstantiateWithChain(object key, InstantiationParameters instantiateParameters, bool trackHandle = true) { return ResourceManager.CreateChainOperation(InitializationOperation, op => InstantiateAsync(key, instantiateParameters, trackHandle)); } public AsyncOperationHandle InstantiateAsync(object key, InstantiationParameters instantiateParameters, bool trackHandle = true) { if (!InitializationOperation.IsDone) return InstantiateWithChain(key, instantiateParameters, trackHandle); key = EvaluateKey(key); IList locs; for (int i = 0; i < m_ResourceLocators.Count; i++) { if (m_ResourceLocators[i].Locate(key, typeof(GameObject), out locs)) return InstantiateAsync(locs[0], instantiateParameters, trackHandle); } return ResourceManager.CreateCompletedOperation(null, new InvalidKeyException(key).Message); } public AsyncOperationHandle InstantiateAsync(IResourceLocation location, InstantiationParameters instantiateParameters, bool trackHandle = true) { var opHandle = ResourceManager.ProvideInstance(InstanceProvider, location, instantiateParameters); if (!trackHandle) return opHandle; opHandle.CompletedTypeless += m_OnHandleCompleteAction; return opHandle; } public bool ReleaseInstance(GameObject instance) { if (instance == null) { LogWarning("Addressables.ReleaseInstance() - trying to release null object."); return false; } AsyncOperationHandle handle; if (m_resultToHandle.TryGetValue(instance, out handle)) Release(handle); else return false; return true; } AsyncOperationHandle LoadSceneWithChain(object key, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) { return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadSceneAsync(key, loadMode, activateOnLoad, priority)); } public AsyncOperationHandle LoadSceneAsync(object key, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) { if (!InitializationOperation.IsDone) return LoadSceneWithChain(key, loadMode, activateOnLoad, priority); IList locations; if (!GetResourceLocations(key, typeof(SceneInstance), out locations)) return ResourceManager.CreateCompletedOperation(default(SceneInstance), new InvalidKeyException(key).Message); return LoadSceneAsync(locations[0], loadMode, activateOnLoad, priority); } public AsyncOperationHandle LoadSceneAsync(IResourceLocation location, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) { return TrackHandle(ResourceManager.ProvideScene(SceneProvider, location, loadMode, activateOnLoad, priority)); } public AsyncOperationHandle UnloadSceneAsync(SceneInstance scene, bool autoReleaseHandle = true) { AsyncOperationHandle handle; if (!m_resultToHandle.TryGetValue(scene, out handle)) { var msg = string.Format("Addressables.UnloadSceneAsync() - Cannot find handle for scene {0}", scene); LogWarning(msg); return ResourceManager.CreateCompletedOperation(scene, msg); } return UnloadSceneAsync(handle, autoReleaseHandle); } public AsyncOperationHandle UnloadSceneAsync(AsyncOperationHandle handle, bool autoReleaseHandle = true) { return UnloadSceneAsync(handle.Convert(), autoReleaseHandle); } public AsyncOperationHandle UnloadSceneAsync(AsyncOperationHandle handle, bool autoReleaseHandle = true) { var relOp = ResourceManager.ReleaseScene(SceneProvider, handle); if (autoReleaseHandle) relOp.Completed += op => Release(op); return relOp; } private object EvaluateKey(object obj) { if (obj is IKeyEvaluator) return (obj as IKeyEvaluator).RuntimeKey; return obj; } } }