#if UNITY_EDITOR using System; using System.Collections.Generic; using System.ComponentModel; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.Exceptions; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.Util; namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation { /// /// Simulates the loading behavior of an asset bundle. Internally it uses the AssetDatabase API. This provider will only work in the editor. /// [DisplayName("Virtual AssetBundle Provider")] public class VirtualAssetBundleProvider : ResourceProviderBase, IUpdateReceiver { VirtualAssetBundleRuntimeData m_BundleData; private VirtualAssetBundleProvider() { m_ProviderId = typeof(AssetBundleProvider).FullName; } /// public override Type GetDefaultType(IResourceLocation location) { return typeof(IAssetBundleResource); } /// /// Construct a new VirtualAssetBundleProvider object; /// Contains information on virtual bundle layout /// public VirtualAssetBundleProvider(VirtualAssetBundleRuntimeData data) { InitializeInternal(typeof(AssetBundleProvider).FullName, data); } private bool InitializeInternal(string providerId, VirtualAssetBundleRuntimeData data) { m_ProviderId = providerId; m_BundleData = data; foreach (var b in m_BundleData.AssetBundles) m_AllBundles.Add(b.Name, b); return !string.IsNullOrEmpty(m_ProviderId); } /// /// Initilization data is passed when when constructed from serialized data /// /// The provider id /// The data string, this is assumed to be the virtual bundle data path /// true if the data is as expected public override bool Initialize(string id, string data) { VirtualAssetBundleRuntimeData bundleData = JsonUtility.FromJson(data); return InitializeInternal(id, bundleData); } class InternalOp { VBAsyncOperation m_RequestOperation; VirtualAssetBundleProvider m_Provider; ProvideHandle m_PI; public float GetPercentComplete() { return m_RequestOperation != null ? m_RequestOperation.PercentComplete : 0.0f; } DownloadStatus GetDownloadStatus() { return m_RequestOperation != null ? m_RequestOperation.GetDownloadStatus() : new DownloadStatus() { IsDone = GetPercentComplete() >= 1f }; } public void Start(ProvideHandle provideHandle, VirtualAssetBundleProvider provider) { m_Provider = provider; m_PI = provideHandle; m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler); m_RequestOperation = m_Provider.LoadAsync(m_PI.Location); m_PI.SetProgressCallback(GetPercentComplete); m_PI.SetDownloadProgressCallbacks(GetDownloadStatus); m_RequestOperation.Completed += bundleOp => { object result = (bundleOp.Result != null && m_PI.Type.IsAssignableFrom(bundleOp.Result.GetType())) ? bundleOp.Result : null; m_PI.Complete(result, (result != null && bundleOp.OperationException == null), bundleOp.OperationException); }; } private bool WaitForCompletionHandler() { return m_RequestOperation.WaitForCompletion(); } } public override void Provide(ProvideHandle provideHandle) { new InternalOp().Start(provideHandle, this); } /// public override void Release(IResourceLocation location, object asset) { if (location == null) throw new ArgumentNullException("location"); if (asset == null) { Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location); return; } Unload(location); } bool m_UpdatingActiveBundles; Dictionary m_PendingOperations = new Dictionary(); Dictionary m_AllBundles = new Dictionary(); Dictionary m_ActiveBundles = new Dictionary(); internal bool Unload(IResourceLocation location) { if (location == null) throw new ArgumentException("IResourceLocation location cannot be null."); VirtualAssetBundle bundle; if (!m_AllBundles.TryGetValue(location.InternalId, out bundle)) { Debug.LogWarningFormat("Unable to unload virtual bundle {0}.", location); return false; } if (m_UpdatingActiveBundles) m_PendingOperations.Add(location.InternalId, null); else m_ActiveBundles.Remove(location.InternalId); return bundle.Unload(); } internal VBAsyncOperation LoadAsync(IResourceLocation location) { if (location == null) throw new ArgumentException("IResourceLocation location cannot be null."); VirtualAssetBundle bundle; if (!m_AllBundles.TryGetValue(location.InternalId, out bundle)) return new VBAsyncOperation().StartCompleted(location, location, default(VirtualAssetBundle), new ResourceManagerException(string.Format("Unable to unload virtual bundle {0}.", location))); try { if (m_UpdatingActiveBundles) m_PendingOperations.Add(location.InternalId, bundle); else m_ActiveBundles.Add(location.InternalId, bundle); } catch (Exception ex) { Debug.LogException(ex); } return bundle.StartLoad(location); } internal void Update(float unscaledDeltaTime) { long localCount = 0; long remoteCount = 0; foreach (VirtualAssetBundle bundle in m_ActiveBundles.Values) bundle.CountBandwidthUsage(ref localCount, ref remoteCount); long localBw = localCount > 1 ? (m_BundleData.LocalLoadSpeed / localCount) : m_BundleData.LocalLoadSpeed; long remoteBw = remoteCount > 1 ? (m_BundleData.RemoteLoadSpeed / remoteCount) : m_BundleData.RemoteLoadSpeed; m_UpdatingActiveBundles = true; foreach (VirtualAssetBundle bundle in m_ActiveBundles.Values) bundle.UpdateAsyncOperations(localBw, remoteBw, unscaledDeltaTime); m_UpdatingActiveBundles = false; if (m_PendingOperations.Count > 0) { foreach (var o in m_PendingOperations) { if (o.Value == null) m_ActiveBundles.Remove(o.Key); else m_ActiveBundles.Add(o.Key, o.Value); } m_PendingOperations.Clear(); } } void IUpdateReceiver.Update(float unscaledDeltaTime) { Update(unscaledDeltaTime); } } } #endif