initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,929 @@
|
|||
#if UNITY_2022_1_OR_NEWER
|
||||
#define UNLOAD_BUNDLE_ASYNC
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
internal class DownloadOnlyLocation : LocationWrapper
|
||||
{
|
||||
public DownloadOnlyLocation(IResourceLocation location) : base(location)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to indication how Assets are loaded from the AssetBundle on the first load request.
|
||||
/// </summary>
|
||||
public enum AssetLoadMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Only load the requested Asset and Dependencies
|
||||
/// </summary>
|
||||
RequestedAssetAndDependencies = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Load all assets inside the AssetBundle
|
||||
/// </summary>
|
||||
AllPackedAssetsAndDependencies,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for asset bundles.
|
||||
/// </summary>
|
||||
public interface IAssetBundleResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the asset bundle.
|
||||
/// </summary>
|
||||
/// <returns>Returns the asset bundle.</returns>
|
||||
AssetBundle GetAssetBundle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains cache information to be used by the AssetBundleProvider
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AssetBundleRequestOptions : ILocationSizeData
|
||||
{
|
||||
[FormerlySerializedAs("m_hash")]
|
||||
[SerializeField]
|
||||
string m_Hash = "";
|
||||
|
||||
/// <summary>
|
||||
/// Hash value of the asset bundle.
|
||||
/// </summary>
|
||||
public string Hash
|
||||
{
|
||||
get { return m_Hash; }
|
||||
set { m_Hash = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_crc")]
|
||||
[SerializeField]
|
||||
uint m_Crc;
|
||||
|
||||
/// <summary>
|
||||
/// CRC value of the bundle.
|
||||
/// </summary>
|
||||
public uint Crc
|
||||
{
|
||||
get { return m_Crc; }
|
||||
set { m_Crc = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_timeout")]
|
||||
[SerializeField]
|
||||
int m_Timeout;
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to abort after the number of seconds in timeout have passed, where the UnityWebRequest has received no data.
|
||||
/// </summary>
|
||||
public int Timeout
|
||||
{
|
||||
get { return m_Timeout; }
|
||||
set { m_Timeout = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_chunkedTransfer")]
|
||||
[SerializeField]
|
||||
bool m_ChunkedTransfer;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the UnityWebRequest system should employ the HTTP/1.1 chunked-transfer encoding method.
|
||||
/// </summary>
|
||||
public bool ChunkedTransfer
|
||||
{
|
||||
get { return m_ChunkedTransfer; }
|
||||
set { m_ChunkedTransfer = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_redirectLimit")]
|
||||
[SerializeField]
|
||||
int m_RedirectLimit = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the number of redirects which this UnityWebRequest will follow before halting with a “Redirect Limit Exceeded” system error.
|
||||
/// </summary>
|
||||
public int RedirectLimit
|
||||
{
|
||||
get { return m_RedirectLimit; }
|
||||
set { m_RedirectLimit = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_retryCount")]
|
||||
[SerializeField]
|
||||
int m_RetryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the number of times the request will be retried.
|
||||
/// </summary>
|
||||
public int RetryCount
|
||||
{
|
||||
get { return m_RetryCount; }
|
||||
set { m_RetryCount = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
string m_BundleName = null;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the original bundle. This does not contain the appended hash.
|
||||
/// </summary>
|
||||
public string BundleName
|
||||
{
|
||||
get { return m_BundleName; }
|
||||
set { m_BundleName = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
AssetLoadMode m_AssetLoadMode = AssetLoadMode.RequestedAssetAndDependencies;
|
||||
|
||||
/// <summary>
|
||||
/// Determines how Assets are loaded when accessed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requested Asset And Dependencies, will only load the requested Asset (Recommended).
|
||||
/// All Packed Assets And Dependencies, will load all Assets that are packed together. Best used when loading all Assets into memory is required.
|
||||
///</remarks>
|
||||
public AssetLoadMode AssetLoadMode
|
||||
{
|
||||
get { return m_AssetLoadMode; }
|
||||
set { m_AssetLoadMode = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
long m_BundleSize;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the bundle, in bytes.
|
||||
/// </summary>
|
||||
public long BundleSize
|
||||
{
|
||||
get { return m_BundleSize; }
|
||||
set { m_BundleSize = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
bool m_UseCrcForCachedBundles;
|
||||
|
||||
/// <summary>
|
||||
/// If false, the CRC will not be used when loading bundles from the cache.
|
||||
/// </summary>
|
||||
public bool UseCrcForCachedBundle
|
||||
{
|
||||
get { return m_UseCrcForCachedBundles; }
|
||||
set { m_UseCrcForCachedBundles = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
bool m_UseUWRForLocalBundles;
|
||||
|
||||
/// <summary>
|
||||
/// If true, UnityWebRequest will be used even if the bundle is stored locally.
|
||||
/// </summary>
|
||||
public bool UseUnityWebRequestForLocalBundles
|
||||
{
|
||||
get { return m_UseUWRForLocalBundles; }
|
||||
set { m_UseUWRForLocalBundles = value; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
bool m_ClearOtherCachedVersionsWhenLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// If false, the CRC will not be used when loading bundles from the cache.
|
||||
/// </summary>
|
||||
public bool ClearOtherCachedVersionsWhenLoaded
|
||||
{
|
||||
get { return m_ClearOtherCachedVersionsWhenLoaded; }
|
||||
set { m_ClearOtherCachedVersionsWhenLoaded = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the amount of data needed to be downloaded for this bundle.
|
||||
/// </summary>
|
||||
/// <param name="location">The location of the bundle.</param>
|
||||
/// <param name="resourceManager">The object that contains all the resource locations.</param>
|
||||
/// <returns>The size in bytes of the bundle that is needed to be downloaded. If the local cache contains the bundle or it is a local bundle, 0 will be returned.</returns>
|
||||
public virtual long ComputeSize(IResourceLocation location, ResourceManager resourceManager)
|
||||
{
|
||||
var id = resourceManager == null ? location.InternalId : resourceManager.TransformInternalId(location);
|
||||
if (!ResourceManagerConfig.IsPathRemote(id))
|
||||
return 0;
|
||||
var locHash = Hash128.Parse(Hash);
|
||||
#if ENABLE_CACHING
|
||||
if (locHash.isValid) //If we have a hash, ensure that our desired version is cached.
|
||||
{
|
||||
if (Caching.IsVersionCached(new CachedAssetBundle(BundleName, locHash)))
|
||||
return 0;
|
||||
return BundleSize;
|
||||
}
|
||||
#endif //ENABLE_CACHING
|
||||
return BundleSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for loading an AssetBundle from a local or remote location.
|
||||
/// </summary>
|
||||
public class AssetBundleResource : IAssetBundleResource, IUpdateReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for where an AssetBundle can be loaded from.
|
||||
/// </summary>
|
||||
public enum LoadType
|
||||
{
|
||||
/// <summary>
|
||||
/// Cannot determine where the AssetBundle is located.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Load the AssetBundle from a local file location.
|
||||
/// </summary>
|
||||
Local,
|
||||
|
||||
/// <summary>
|
||||
/// Download the AssetBundle from a web server.
|
||||
/// </summary>
|
||||
Web
|
||||
}
|
||||
|
||||
AssetBundle m_AssetBundle;
|
||||
AsyncOperation m_RequestOperation;
|
||||
WebRequestQueueOperation m_WebRequestQueueOperation;
|
||||
internal ProvideHandle m_ProvideHandle;
|
||||
internal AssetBundleRequestOptions m_Options;
|
||||
|
||||
[NonSerialized]
|
||||
bool m_RequestCompletedCallbackCalled = false;
|
||||
|
||||
int m_Retries;
|
||||
BundleSource m_Source = BundleSource.None;
|
||||
long m_BytesToDownload;
|
||||
long m_DownloadedBytes;
|
||||
bool m_Completed = false;
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
AssetBundleUnloadOperation m_UnloadOperation;
|
||||
#endif
|
||||
const int k_WaitForWebRequestMainThreadSleep = 1;
|
||||
string m_TransformedInternalId;
|
||||
AssetBundleRequest m_PreloadRequest;
|
||||
bool m_PreloadCompleted = false;
|
||||
ulong m_LastDownloadedByteCount = 0;
|
||||
float m_TimeoutTimer = 0;
|
||||
int m_TimeoutOverFrames = 0;
|
||||
|
||||
private bool HasTimedOut => m_TimeoutTimer >= m_Options.Timeout && m_TimeoutOverFrames > 5;
|
||||
|
||||
internal long BytesToDownload
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_BytesToDownload == -1)
|
||||
{
|
||||
if (m_Options != null)
|
||||
m_BytesToDownload = m_Options.ComputeSize(m_ProvideHandle.Location, m_ProvideHandle.ResourceManager);
|
||||
else
|
||||
m_BytesToDownload = 0;
|
||||
}
|
||||
|
||||
return m_BytesToDownload;
|
||||
}
|
||||
}
|
||||
|
||||
internal UnityWebRequest CreateWebRequest(IResourceLocation loc)
|
||||
{
|
||||
var url = m_ProvideHandle.ResourceManager.TransformInternalId(loc);
|
||||
return CreateWebRequest(url);
|
||||
}
|
||||
|
||||
internal UnityWebRequest CreateWebRequest(string url)
|
||||
{
|
||||
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
|
||||
Uri uri = new Uri(url.Replace(" ", "%20"));
|
||||
#else
|
||||
Uri uri = new Uri(Uri.EscapeUriString(url));
|
||||
#endif
|
||||
|
||||
if (m_Options == null)
|
||||
{
|
||||
m_Source = BundleSource.Download;
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(Profiling.ContentStatus.Downloading, m_Source);
|
||||
#endif
|
||||
return UnityWebRequestAssetBundle.GetAssetBundle(uri);
|
||||
}
|
||||
|
||||
UnityWebRequest webRequest;
|
||||
if (!string.IsNullOrEmpty(m_Options.Hash))
|
||||
{
|
||||
CachedAssetBundle cachedBundle = new CachedAssetBundle(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
|
||||
#if ENABLE_CACHING
|
||||
bool cached = Caching.IsVersionCached(cachedBundle);
|
||||
m_Source = cached ? BundleSource.Cache : BundleSource.Download;
|
||||
if (m_Options.UseCrcForCachedBundle || m_Source == BundleSource.Download)
|
||||
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle, m_Options.Crc);
|
||||
else
|
||||
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle);
|
||||
#else
|
||||
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle, m_Options.Crc);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Source = BundleSource.Download;
|
||||
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, m_Options.Crc);
|
||||
}
|
||||
|
||||
if (m_Options.RedirectLimit > 0)
|
||||
webRequest.redirectLimit = m_Options.RedirectLimit;
|
||||
if (m_ProvideHandle.ResourceManager.CertificateHandlerInstance != null)
|
||||
{
|
||||
webRequest.certificateHandler = m_ProvideHandle.ResourceManager.CertificateHandlerInstance;
|
||||
webRequest.disposeCertificateHandlerOnDispose = false;
|
||||
}
|
||||
|
||||
m_ProvideHandle.ResourceManager.WebRequestOverride?.Invoke(webRequest);
|
||||
return webRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a request for loading all assets from an AssetBundle.
|
||||
/// </summary>
|
||||
/// <returns>Returns the request.</returns>
|
||||
public AssetBundleRequest GetAssetPreloadRequest()
|
||||
{
|
||||
if (m_PreloadCompleted || GetAssetBundle() == null)
|
||||
return null;
|
||||
|
||||
if (m_Options.AssetLoadMode == AssetLoadMode.AllPackedAssetsAndDependencies)
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
{
|
||||
m_AssetBundle.LoadAllAssets();
|
||||
m_PreloadCompleted = true;
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
if (m_PreloadRequest == null)
|
||||
{
|
||||
m_PreloadRequest = m_AssetBundle.LoadAllAssetsAsync();
|
||||
m_PreloadRequest.completed += operation => m_PreloadCompleted = true;
|
||||
}
|
||||
|
||||
return m_PreloadRequest;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
float PercentComplete()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
|
||||
}
|
||||
|
||||
DownloadStatus GetDownloadStatus()
|
||||
{
|
||||
if (m_Options == null)
|
||||
return default;
|
||||
var status = new DownloadStatus() {TotalBytes = BytesToDownload, IsDone = PercentComplete() >= 1f};
|
||||
if (BytesToDownload > 0)
|
||||
{
|
||||
if (m_WebRequestQueueOperation != null && string.IsNullOrEmpty(m_WebRequestQueueOperation.m_WebRequest.error))
|
||||
m_DownloadedBytes = (long)(m_WebRequestQueueOperation.m_WebRequest.downloadedBytes);
|
||||
else if (m_RequestOperation != null && m_RequestOperation is UnityWebRequestAsyncOperation operation && string.IsNullOrEmpty(operation.webRequest.error))
|
||||
m_DownloadedBytes = (long)operation.webRequest.downloadedBytes;
|
||||
}
|
||||
|
||||
status.DownloadedBytes = m_DownloadedBytes;
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the asset bundle object managed by this resource. This call may force the bundle to load if not already loaded.
|
||||
/// </summary>
|
||||
/// <returns>The asset bundle.</returns>
|
||||
public AssetBundle GetAssetBundle()
|
||||
{
|
||||
return m_AssetBundle;
|
||||
}
|
||||
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
private void AddBundleToProfiler(Profiling.ContentStatus status, BundleSource source)
|
||||
{
|
||||
if (!Profiler.enabled)
|
||||
return;
|
||||
if (!m_ProvideHandle.IsValid)
|
||||
return;
|
||||
|
||||
if (status == Profiling.ContentStatus.Active && m_AssetBundle == null)
|
||||
Profiling.ProfilerRuntime.BundleReleased(m_Options.BundleName);
|
||||
else
|
||||
Profiling.ProfilerRuntime.AddBundleOperation(m_ProvideHandle, m_Options, status, source);
|
||||
}
|
||||
|
||||
private void RemoveBundleFromProfiler()
|
||||
{
|
||||
if (m_Options == null)
|
||||
return;
|
||||
Profiling.ProfilerRuntime.BundleReleased(m_Options.BundleName);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
void OnUnloadOperationComplete(AsyncOperation op)
|
||||
{
|
||||
m_UnloadOperation = null;
|
||||
BeginOperation();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
/// <summary>
|
||||
/// Stores AssetBundle loading information, starts loading the bundle.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">The container for AssetBundle loading information.</param>
|
||||
/// <param name="unloadOp">The async operation for unloading the AssetBundle.</param>
|
||||
public void Start(ProvideHandle provideHandle, AssetBundleUnloadOperation unloadOp)
|
||||
#else
|
||||
/// <summary>
|
||||
/// Stores AssetBundle loading information, starts loading the bundle.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">The container for information regarding loading the AssetBundle.</param>
|
||||
public void Start(ProvideHandle provideHandle)
|
||||
#endif
|
||||
{
|
||||
m_Retries = 0;
|
||||
m_AssetBundle = null;
|
||||
m_RequestOperation = null;
|
||||
m_RequestCompletedCallbackCalled = false;
|
||||
m_ProvideHandle = provideHandle;
|
||||
m_Options = m_ProvideHandle.Location.Data as AssetBundleRequestOptions;
|
||||
m_BytesToDownload = -1;
|
||||
m_ProvideHandle.SetProgressCallback(PercentComplete);
|
||||
m_ProvideHandle.SetDownloadProgressCallbacks(GetDownloadStatus);
|
||||
m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
m_UnloadOperation = unloadOp;
|
||||
if (m_UnloadOperation != null && !m_UnloadOperation.isDone)
|
||||
m_UnloadOperation.completed += OnUnloadOperationComplete;
|
||||
else
|
||||
#endif
|
||||
BeginOperation();
|
||||
}
|
||||
|
||||
private bool WaitForCompletionHandler()
|
||||
{
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
if (m_UnloadOperation != null && !m_UnloadOperation.isDone)
|
||||
{
|
||||
m_UnloadOperation.completed -= OnUnloadOperationComplete;
|
||||
m_UnloadOperation.WaitForCompletion();
|
||||
m_UnloadOperation = null;
|
||||
BeginOperation();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_RequestOperation == null)
|
||||
{
|
||||
if (m_WebRequestQueueOperation == null)
|
||||
return false;
|
||||
else
|
||||
WebRequestQueue.WaitForRequestToBeActive(m_WebRequestQueueOperation, k_WaitForWebRequestMainThreadSleep);
|
||||
}
|
||||
|
||||
//We don't want to wait for request op to complete if it's a LoadFromFileAsync. Only UWR will complete in a tight loop like this.
|
||||
if (m_RequestOperation is UnityWebRequestAsyncOperation op)
|
||||
{
|
||||
while (!UnityWebRequestUtilities.IsAssetBundleDownloaded(op))
|
||||
System.Threading.Thread.Sleep(k_WaitForWebRequestMainThreadSleep);
|
||||
#if ENABLE_ASYNC_ASSETBUNDLE_UWR
|
||||
if (m_Source == BundleSource.Cache)
|
||||
{
|
||||
var downloadHandler = (DownloadHandlerAssetBundle)op?.webRequest?.downloadHandler;
|
||||
if (downloadHandler.autoLoadAssetBundle)
|
||||
m_AssetBundle = downloadHandler.assetBundle;
|
||||
}
|
||||
#endif
|
||||
if (!m_RequestCompletedCallbackCalled)
|
||||
{
|
||||
m_RequestOperation.completed -= WebRequestOperationCompleted;
|
||||
WebRequestOperationCompleted(m_RequestOperation);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Completed && m_Source == BundleSource.Local) {
|
||||
|
||||
// we don't have to check for done with local files as calling
|
||||
// m_requestOperation.assetBundle is blocking and will wait for the file to load
|
||||
if (!m_RequestCompletedCallbackCalled)
|
||||
{
|
||||
m_RequestOperation.completed -= LocalRequestOperationCompleted;
|
||||
LocalRequestOperationCompleted(m_RequestOperation);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_Completed && m_RequestOperation.isDone)
|
||||
{
|
||||
m_ProvideHandle.Complete(this, m_AssetBundle != null, null);
|
||||
m_Completed = true;
|
||||
}
|
||||
|
||||
return m_Completed;
|
||||
}
|
||||
|
||||
void AddCallbackInvokeIfDone(AsyncOperation operation, Action<AsyncOperation> callback)
|
||||
{
|
||||
if (operation.isDone)
|
||||
callback(operation);
|
||||
else
|
||||
operation.completed += callback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines where an AssetBundle can be loaded from.
|
||||
/// </summary>
|
||||
/// <param name="handle">The container for AssetBundle loading information.</param>
|
||||
/// <param name="loadType">Specifies where an AssetBundle can be loaded from.</param>
|
||||
/// <param name="path">The file path or url where the AssetBundle is located.</param>
|
||||
public static void GetLoadInfo(ProvideHandle handle, out LoadType loadType, out string path)
|
||||
{
|
||||
GetLoadInfo(handle.Location, handle.ResourceManager, out loadType, out path);
|
||||
}
|
||||
|
||||
internal static void GetLoadInfo(IResourceLocation location, ResourceManager resourceManager, out LoadType loadType, out string path)
|
||||
{
|
||||
var options = location?.Data as AssetBundleRequestOptions;
|
||||
if (options == null)
|
||||
{
|
||||
loadType = LoadType.None;
|
||||
path = null;
|
||||
return;
|
||||
}
|
||||
|
||||
path = resourceManager.TransformInternalId(location);
|
||||
if (Application.platform == RuntimePlatform.Android && path.StartsWith("jar:", StringComparison.Ordinal))
|
||||
loadType = options.UseUnityWebRequestForLocalBundles ? LoadType.Web : LoadType.Local;
|
||||
else if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
|
||||
loadType = LoadType.Web;
|
||||
else if (options.UseUnityWebRequestForLocalBundles)
|
||||
{
|
||||
path = "file:///" + Path.GetFullPath(path);
|
||||
loadType = LoadType.Web;
|
||||
}
|
||||
else
|
||||
loadType = LoadType.Local;
|
||||
|
||||
if (loadType == LoadType.Web)
|
||||
path = path.Replace('\\', '/');
|
||||
}
|
||||
|
||||
private void BeginOperation()
|
||||
{
|
||||
m_DownloadedBytes = 0;
|
||||
GetLoadInfo(m_ProvideHandle, out LoadType loadType, out m_TransformedInternalId);
|
||||
|
||||
if (loadType == LoadType.Local)
|
||||
{
|
||||
m_Source = BundleSource.Local;
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
CompleteBundleLoad(AssetBundle.LoadFromFile(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc));
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_RequestOperation = AssetBundle.LoadFromFileAsync(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc);
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(Profiling.ContentStatus.Loading, m_Source);
|
||||
#endif
|
||||
AddCallbackInvokeIfDone(m_RequestOperation, LocalRequestOperationCompleted);
|
||||
}
|
||||
}
|
||||
else if (loadType == LoadType.Web)
|
||||
{
|
||||
var req = CreateWebRequest(m_TransformedInternalId);
|
||||
#if ENABLE_ASYNC_ASSETBUNDLE_UWR
|
||||
((DownloadHandlerAssetBundle)req.downloadHandler).autoLoadAssetBundle = !(m_ProvideHandle.Location is DownloadOnlyLocation);
|
||||
#endif
|
||||
req.disposeDownloadHandlerOnDispose = false;
|
||||
|
||||
m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
|
||||
if (m_WebRequestQueueOperation.IsDone)
|
||||
{
|
||||
BeginWebRequestOperation(m_WebRequestQueueOperation.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(Profiling.ContentStatus.Queue, m_Source);
|
||||
#endif
|
||||
m_WebRequestQueueOperation.OnComplete += asyncOp => BeginWebRequestOperation(asyncOp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Source = BundleSource.None;
|
||||
m_RequestOperation = null;
|
||||
m_ProvideHandle.Complete<AssetBundleResource>(null, false,
|
||||
new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location));
|
||||
m_Completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginWebRequestOperation(AsyncOperation asyncOp)
|
||||
{
|
||||
m_TimeoutTimer = 0;
|
||||
m_TimeoutOverFrames = 0;
|
||||
m_LastDownloadedByteCount = 0;
|
||||
m_RequestOperation = asyncOp;
|
||||
if (m_RequestOperation == null || m_RequestOperation.isDone)
|
||||
WebRequestOperationCompleted(m_RequestOperation);
|
||||
else
|
||||
{
|
||||
if (m_Options.Timeout > 0)
|
||||
m_ProvideHandle.ResourceManager.AddUpdateReceiver(this);
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(m_Source == BundleSource.Cache ? Profiling.ContentStatus.Loading : Profiling.ContentStatus.Downloading, m_Source );
|
||||
#endif
|
||||
m_RequestOperation.completed += WebRequestOperationCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(float unscaledDeltaTime)
|
||||
{
|
||||
if (m_RequestOperation != null && m_RequestOperation is UnityWebRequestAsyncOperation operation && !operation.isDone)
|
||||
{
|
||||
if (m_LastDownloadedByteCount != operation.webRequest.downloadedBytes)
|
||||
{
|
||||
m_TimeoutTimer = 0;
|
||||
m_TimeoutOverFrames = 0;
|
||||
m_LastDownloadedByteCount = operation.webRequest.downloadedBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TimeoutTimer += unscaledDeltaTime;
|
||||
if (HasTimedOut)
|
||||
operation.webRequest.Abort();
|
||||
m_TimeoutOverFrames++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LocalRequestOperationCompleted(AsyncOperation op)
|
||||
{
|
||||
if (m_RequestCompletedCallbackCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_RequestCompletedCallbackCalled = true;
|
||||
CompleteBundleLoad((op as AssetBundleCreateRequest).assetBundle);
|
||||
}
|
||||
|
||||
private void CompleteBundleLoad(AssetBundle bundle)
|
||||
{
|
||||
m_AssetBundle = bundle;
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source);
|
||||
#endif
|
||||
if (m_AssetBundle != null)
|
||||
m_ProvideHandle.Complete(this, true, null);
|
||||
else
|
||||
m_ProvideHandle.Complete<AssetBundleResource>(null, false,
|
||||
new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location));
|
||||
m_Completed = true;
|
||||
}
|
||||
|
||||
private void WebRequestOperationCompleted(AsyncOperation op)
|
||||
{
|
||||
if (m_RequestCompletedCallbackCalled)
|
||||
return;
|
||||
|
||||
m_RequestCompletedCallbackCalled = true;
|
||||
|
||||
if (m_Options.Timeout > 0)
|
||||
m_ProvideHandle.ResourceManager.RemoveUpdateReciever(this);
|
||||
|
||||
UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
|
||||
var webReq = remoteReq?.webRequest;
|
||||
var downloadHandler = webReq?.downloadHandler as DownloadHandlerAssetBundle;
|
||||
UnityWebRequestResult uwrResult = null;
|
||||
if (webReq != null && !UnityWebRequestUtilities.RequestHasErrors(webReq, out uwrResult))
|
||||
{
|
||||
if (!m_Completed)
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source);
|
||||
#endif
|
||||
m_AssetBundle = downloadHandler.assetBundle;
|
||||
downloadHandler.Dispose();
|
||||
downloadHandler = null;
|
||||
m_ProvideHandle.Complete(this, true, null);
|
||||
m_Completed = true;
|
||||
}
|
||||
#if ENABLE_CACHING
|
||||
if (!string.IsNullOrEmpty(m_Options.Hash) && m_Options.ClearOtherCachedVersionsWhenLoaded)
|
||||
Caching.ClearOtherCachedVersions(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasTimedOut)
|
||||
uwrResult.Error = "Request timeout";
|
||||
webReq = m_WebRequestQueueOperation.m_WebRequest;
|
||||
if (uwrResult == null)
|
||||
uwrResult = new UnityWebRequestResult(m_WebRequestQueueOperation.m_WebRequest);
|
||||
|
||||
downloadHandler = webReq.downloadHandler as DownloadHandlerAssetBundle;
|
||||
downloadHandler.Dispose();
|
||||
downloadHandler = null;
|
||||
bool forcedRetry = false;
|
||||
string message = $"Web request failed, retrying ({m_Retries}/{m_Options.RetryCount})...\n{uwrResult}";
|
||||
#if ENABLE_CACHING
|
||||
if (!string.IsNullOrEmpty(m_Options.Hash))
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
if (m_Source == BundleSource.Cache)
|
||||
#endif
|
||||
{
|
||||
message = $"Web request failed to load from cache. The cached AssetBundle will be cleared from the cache and re-downloaded. Retrying...\n{uwrResult}";
|
||||
Caching.ClearCachedVersion(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
|
||||
// When attempted to load from cache we always retry on first attempt and failed
|
||||
if (m_Retries == 0 && uwrResult.ShouldRetryDownloadError())
|
||||
{
|
||||
Debug.LogFormat(message);
|
||||
BeginOperation();
|
||||
m_Retries++; //Will prevent us from entering an infinite loop of retrying if retry count is 0
|
||||
forcedRetry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!forcedRetry)
|
||||
{
|
||||
if (m_Retries < m_Options.RetryCount && uwrResult.ShouldRetryDownloadError())
|
||||
{
|
||||
m_Retries++;
|
||||
Debug.LogFormat(message);
|
||||
BeginOperation();
|
||||
}
|
||||
else
|
||||
{
|
||||
var exception = new RemoteProviderException($"Unable to load asset bundle from : {webReq.url}", m_ProvideHandle.Location, uwrResult);
|
||||
m_ProvideHandle.Complete<AssetBundleResource>(null, false, exception);
|
||||
m_Completed = true;
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
RemoveBundleFromProfiler();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webReq.Dispose();
|
||||
}
|
||||
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
/// <summary>
|
||||
/// Starts an async operation that unloads all resources associated with the AssetBundle.
|
||||
/// </summary>
|
||||
/// <param name="unloadOp">The async operation.</param>
|
||||
/// <returns>Returns true if the async operation object is valid.</returns>
|
||||
public bool Unload(out AssetBundleUnloadOperation unloadOp)
|
||||
#else
|
||||
/// <summary>
|
||||
/// Unloads all resources associated with the AssetBundle.
|
||||
/// </summary>
|
||||
public void Unload()
|
||||
#endif
|
||||
{
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
unloadOp = null;
|
||||
if (m_AssetBundle != null)
|
||||
{
|
||||
unloadOp = m_AssetBundle.UnloadAsync(true);
|
||||
m_AssetBundle = null;
|
||||
}
|
||||
#else
|
||||
if (m_AssetBundle != null)
|
||||
{
|
||||
m_AssetBundle.Unload(true);
|
||||
m_AssetBundle = null;
|
||||
}
|
||||
#endif
|
||||
m_RequestOperation = null;
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
RemoveBundleFromProfiler();
|
||||
#endif
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
return unloadOp != null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IResourceProvider for asset bundles. Loads bundles via UnityWebRequestAssetBundle API if the internalId starts with "http". If not, it will load the bundle via AssetBundle.LoadFromFileAsync.
|
||||
/// </summary>
|
||||
[DisplayName("AssetBundle Provider")]
|
||||
public class AssetBundleProvider : ResourceProviderBase
|
||||
{
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
private static Dictionary<string, AssetBundleUnloadOperation> m_UnloadingBundles = new Dictionary<string, AssetBundleUnloadOperation>();
|
||||
/// <summary>
|
||||
/// Stores async operations that unload the requested AssetBundles.
|
||||
/// </summary>
|
||||
protected internal static Dictionary<string, AssetBundleUnloadOperation> UnloadingBundles
|
||||
{
|
||||
get { return m_UnloadingBundles; }
|
||||
internal set { m_UnloadingBundles = value; }
|
||||
}
|
||||
|
||||
internal static int UnloadingAssetBundleCount => m_UnloadingBundles.Count;
|
||||
internal static int AssetBundleCount => AssetBundle.GetAllLoadedAssetBundles().Count() - UnloadingAssetBundleCount;
|
||||
internal static void WaitForAllUnloadingBundlesToComplete()
|
||||
{
|
||||
if (UnloadingAssetBundleCount > 0)
|
||||
{
|
||||
var bundles = m_UnloadingBundles.Values.ToArray();
|
||||
foreach (var b in bundles)
|
||||
b.WaitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
internal static void WaitForAllUnloadingBundlesToComplete()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Provide(ProvideHandle providerInterface)
|
||||
{
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
if (m_UnloadingBundles.TryGetValue(providerInterface.Location.InternalId, out var unloadOp))
|
||||
{
|
||||
if (unloadOp.isDone)
|
||||
unloadOp = null;
|
||||
}
|
||||
new AssetBundleResource().Start(providerInterface, unloadOp);
|
||||
#else
|
||||
new AssetBundleResource().Start(providerInterface);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Type GetDefaultType(IResourceLocation location)
|
||||
{
|
||||
return typeof(IAssetBundleResource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the asset bundle via AssetBundle.Unload(true).
|
||||
/// </summary>
|
||||
/// <param name="location">The location of the asset to release</param>
|
||||
/// <param name="asset">The asset in question</param>
|
||||
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;
|
||||
}
|
||||
|
||||
var bundle = asset as AssetBundleResource;
|
||||
if (bundle != null)
|
||||
{
|
||||
#if UNLOAD_BUNDLE_ASYNC
|
||||
if (bundle.Unload(out var unloadOp))
|
||||
{
|
||||
m_UnloadingBundles.Add(location.InternalId, unloadOp);
|
||||
unloadOp.completed += op => m_UnloadingBundles.Remove(location.InternalId);
|
||||
}
|
||||
#else
|
||||
bundle.Unload();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 86a4f4dd6530757418ab820c2c781436
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,130 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEditor;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides assets loaded via the AssetDatabase API. This provider is only available in the editor and is used for fast iteration or to simulate asset bundles when in play mode.
|
||||
/// </summary>
|
||||
[DisplayName("Assets from AssetDatabase Provider")]
|
||||
public class AssetDatabaseProvider : ResourceProviderBase
|
||||
{
|
||||
float m_LoadDelay = .1f;
|
||||
|
||||
internal static Object LoadAssetSubObject(string assetPath, string subObjectName, Type type)
|
||||
{
|
||||
var objs = LoadAssetsWithSubAssets(assetPath);
|
||||
foreach (var o in objs)
|
||||
{
|
||||
if (o.name == subObjectName)
|
||||
{
|
||||
if (type.IsAssignableFrom(o.GetType()))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object LoadMainAssetAtPath(string assetPath)
|
||||
{
|
||||
return AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||||
}
|
||||
|
||||
internal static object LoadAssetAtPath(string assetPath, ProvideHandle provideHandle)
|
||||
{
|
||||
Object obj = AssetDatabase.LoadAssetAtPath(assetPath, provideHandle.Location.ResourceType);
|
||||
obj = obj != null && provideHandle.Type.IsAssignableFrom(obj.GetType()) ? obj : null;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public AssetDatabaseProvider()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows for a sepcified delay for all requests.
|
||||
/// </summary>
|
||||
/// <param name="delay">Time in seconds for each delay call.</param>
|
||||
public AssetDatabaseProvider(float delay = .25f)
|
||||
{
|
||||
m_LoadDelay = delay;
|
||||
}
|
||||
|
||||
internal static Object[] LoadAssetsWithSubAssets(string assetPath)
|
||||
{
|
||||
var subObjects = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
|
||||
var allObjects = new Object[subObjects.Length + 1];
|
||||
allObjects[0] = LoadMainAssetAtPath(assetPath);
|
||||
for (int i = 0; i < subObjects.Length; i++)
|
||||
allObjects[i + 1] = subObjects[i];
|
||||
return allObjects;
|
||||
}
|
||||
|
||||
class InternalOp
|
||||
{
|
||||
ProvideHandle m_ProvideHandle;
|
||||
bool m_Loaded;
|
||||
|
||||
public void Start(ProvideHandle provideHandle, float loadDelay)
|
||||
{
|
||||
m_Loaded = false;
|
||||
m_ProvideHandle = provideHandle;
|
||||
m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
if (loadDelay < 0)
|
||||
LoadImmediate();
|
||||
else
|
||||
DelayedActionManager.AddAction((Action)LoadImmediate, loadDelay);
|
||||
}
|
||||
|
||||
private bool WaitForCompletionHandler()
|
||||
{
|
||||
LoadImmediate();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadImmediate()
|
||||
{
|
||||
if (m_Loaded)
|
||||
return;
|
||||
m_Loaded = true;
|
||||
string assetPath = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
|
||||
object result = null;
|
||||
if (m_ProvideHandle.Type.IsArray)
|
||||
result = ResourceManagerConfig.CreateArrayResult(m_ProvideHandle.Type, LoadAssetsWithSubAssets(assetPath));
|
||||
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
|
||||
result = ResourceManagerConfig.CreateListResult(m_ProvideHandle.Type, LoadAssetsWithSubAssets(assetPath));
|
||||
else
|
||||
{
|
||||
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
|
||||
result = LoadAssetSubObject(mainPath, subKey, m_ProvideHandle.Type);
|
||||
else
|
||||
result = LoadAssetAtPath(assetPath, m_ProvideHandle);
|
||||
}
|
||||
|
||||
m_ProvideHandle.Complete(result, result != null,
|
||||
result == null ? new Exception($"Unable to load asset of type {m_ProvideHandle.Type} from location {m_ProvideHandle.Location}.") : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanProvide(Type t, IResourceLocation location)
|
||||
{
|
||||
return base.CanProvide(t, location);
|
||||
}
|
||||
|
||||
public override void Provide(ProvideHandle provideHandle)
|
||||
{
|
||||
new InternalOp().Start(provideHandle, m_LoadDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f0364baebb057874782f9bd6e4dcfa78
|
||||
timeCreated: 1502505318
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,30 @@
|
|||
using System.ComponentModel;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides sprites from atlases
|
||||
/// </summary>
|
||||
[DisplayName("Sprites from Atlases Provider")]
|
||||
public class AtlasSpriteProvider : ResourceProviderBase
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void Provide(ProvideHandle providerInterface)
|
||||
{
|
||||
var atlas = providerInterface.GetDependency<SpriteAtlas>(0);
|
||||
if (atlas == null)
|
||||
{
|
||||
providerInterface.Complete<Sprite>(null, false, new System.Exception($"Sprite atlas failed to load for location {providerInterface.Location.PrimaryKey}."));
|
||||
return;
|
||||
}
|
||||
|
||||
var key = providerInterface.ResourceManager.TransformInternalId(providerInterface.Location);
|
||||
ResourceManagerConfig.ExtractKeyAndSubKey(key, out string mainKey, out string subKey);
|
||||
string spriteKey = string.IsNullOrEmpty(subKey) ? mainKey : subKey;
|
||||
var sprite = atlas.GetSprite(spriteKey);
|
||||
providerInterface.Complete(sprite, sprite != null, sprite != null ? null : new System.Exception($"Sprite failed to load for location {providerInterface.Location.PrimaryKey}."));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82e826e5c9181f2449a34ca76c3837e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts ray binary data into the requested object.
|
||||
/// </summary>
|
||||
[DisplayName("Binary Asset Provider")]
|
||||
internal class BinaryAssetProvider<TAdapter> : BinaryDataProvider where TAdapter : BinaryStorageBuffer.ISerializationAdapter, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts raw bytes into requested object type via BinaryStorageBuffer.ISerializationAdapter
|
||||
/// </summary>
|
||||
/// <param name="type">The object type the text is converted to.</param>
|
||||
/// <param name="data">The data to convert.</param>
|
||||
/// <returns>Returns the converted object.</returns>
|
||||
public override object Convert(Type type, byte[] data)
|
||||
{
|
||||
return new BinaryStorageBuffer.Reader(data, 512, new TAdapter()).ReadObject(type, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3fe4baf3821dbe44ca354c8bbae559f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,203 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides raw text from a local or remote URL.
|
||||
/// </summary>
|
||||
[DisplayName("Binary Data Provider")]
|
||||
internal class BinaryDataProvider : ResourceProviderBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls whether errors are logged - this is disabled when trying to load from the local cache since failures are expected
|
||||
/// </summary>
|
||||
public bool IgnoreFailures { get; set; }
|
||||
|
||||
internal class InternalOp
|
||||
{
|
||||
BinaryDataProvider m_Provider;
|
||||
UnityWebRequestAsyncOperation m_RequestOperation;
|
||||
WebRequestQueueOperation m_RequestQueueOperation;
|
||||
ProvideHandle m_PI;
|
||||
bool m_IgnoreFailures;
|
||||
private bool m_Complete = false;
|
||||
private int m_Timeout = 0;
|
||||
|
||||
private float GetPercentComplete()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
|
||||
}
|
||||
|
||||
public void Start(ProvideHandle provideHandle, BinaryDataProvider rawProvider)
|
||||
{
|
||||
m_PI = provideHandle;
|
||||
m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
provideHandle.SetProgressCallback(GetPercentComplete);
|
||||
m_Provider = rawProvider;
|
||||
|
||||
// override input options with options from Location if included
|
||||
if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
|
||||
{
|
||||
m_IgnoreFailures = providerData.IgnoreFailures;
|
||||
m_Timeout = providerData.WebRequestTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IgnoreFailures = rawProvider.IgnoreFailures;
|
||||
m_Timeout = 0;
|
||||
}
|
||||
|
||||
var path = m_PI.ResourceManager.TransformInternalId(m_PI.Location);
|
||||
if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
|
||||
{
|
||||
SendWebRequest(path);
|
||||
}
|
||||
else if (File.Exists(path))
|
||||
{
|
||||
#if NET_4_6
|
||||
if (path.Length >= 260)
|
||||
path = @"\\?\" + path;
|
||||
#endif
|
||||
if (path.EndsWith(".json"))
|
||||
throw new Exception($"Trying to read non binary data at path '{path}'.");
|
||||
var data = File.ReadAllBytes(path);
|
||||
object result = ConvertBytes(data);
|
||||
m_PI.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {m_PI.Type} from location {m_PI.Location}.") : null);
|
||||
m_Complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception exception = null;
|
||||
//Don't log errors when loading from the persistentDataPath since these files are expected to not exist until created
|
||||
if (m_IgnoreFailures)
|
||||
{
|
||||
m_PI.Complete<object>(null, true, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = new Exception(string.Format("Invalid path in " + nameof(TextDataProvider) + " : '{0}'.", path));
|
||||
m_PI.Complete<object>(null, false, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForCompletionHandler()
|
||||
{
|
||||
if (m_Complete)
|
||||
return true;
|
||||
|
||||
if (m_RequestOperation != null)
|
||||
{
|
||||
if (m_RequestOperation.isDone && !m_Complete)
|
||||
RequestOperation_completed(m_RequestOperation);
|
||||
else if (!m_RequestOperation.isDone)
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_Complete;
|
||||
}
|
||||
|
||||
private void RequestOperation_completed(AsyncOperation op)
|
||||
{
|
||||
if (m_Complete)
|
||||
return;
|
||||
|
||||
var webOp = op as UnityWebRequestAsyncOperation;
|
||||
byte[] binaryResult = null;
|
||||
Exception exception = null;
|
||||
if (webOp != null)
|
||||
{
|
||||
var webReq = webOp.webRequest;
|
||||
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
|
||||
binaryResult = webReq.downloadHandler.data;
|
||||
else
|
||||
exception = new RemoteProviderException($"{nameof(TextDataProvider)} : unable to load from url : {webReq.url}", m_PI.Location, uwrResult);
|
||||
webReq.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = new RemoteProviderException(nameof(TextDataProvider) + " unable to load from unknown url", m_PI.Location);
|
||||
}
|
||||
|
||||
CompleteOperation(binaryResult, exception);
|
||||
}
|
||||
|
||||
protected void CompleteOperation(byte[] data, Exception exception)
|
||||
{
|
||||
object result = null;
|
||||
if (data != null && data.Length != 0)
|
||||
result = ConvertBytes(data);
|
||||
|
||||
m_PI.Complete(result, result != null || m_IgnoreFailures, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
|
||||
private object ConvertBytes(byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_Provider.Convert(m_PI.Type, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!m_IgnoreFailures)
|
||||
Debug.LogException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SendWebRequest(string path)
|
||||
{
|
||||
UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null);
|
||||
if (m_Timeout > 0)
|
||||
request.timeout = m_Timeout;
|
||||
|
||||
m_PI.ResourceManager.WebRequestOverride?.Invoke(request);
|
||||
m_RequestQueueOperation = WebRequestQueue.QueueRequest(request);
|
||||
if (m_RequestQueueOperation.IsDone)
|
||||
{
|
||||
m_RequestOperation = m_RequestQueueOperation.Result;
|
||||
if (m_RequestOperation.isDone)
|
||||
RequestOperation_completed(m_RequestOperation);
|
||||
else
|
||||
m_RequestOperation.completed += RequestOperation_completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RequestQueueOperation.OnComplete += asyncOperation =>
|
||||
{
|
||||
m_RequestOperation = asyncOperation;
|
||||
m_RequestOperation.completed += RequestOperation_completed;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to convert the text into the object type requested. Usually the text contains a JSON formatted serialized object.
|
||||
/// </summary>
|
||||
/// <param name="type">The object type the text is converted to.</param>
|
||||
/// <param name="data">The byte array to be converted.</param>
|
||||
/// <returns>The converted object.</returns>
|
||||
public virtual object Convert(Type type, byte[] data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides raw text data from the location.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">The data needed by the provider to perform the load.</param>
|
||||
public override void Provide(ProvideHandle provideHandle)
|
||||
{
|
||||
new InternalOp().Start(provideHandle, this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 000d7a662089d6b4090fb72037306f82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,243 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides assets stored in an asset bundle.
|
||||
/// </summary>
|
||||
[DisplayName("Assets from Bundles Provider")]
|
||||
public class BundledAssetProvider : ResourceProviderBase
|
||||
{
|
||||
internal class InternalOp
|
||||
{
|
||||
AssetBundle m_AssetBundle;
|
||||
AssetBundleRequest m_PreloadRequest;
|
||||
AssetBundleRequest m_RequestOperation;
|
||||
object m_Result;
|
||||
ProvideHandle m_ProvideHandle;
|
||||
string subObjectName = null;
|
||||
|
||||
internal static T LoadBundleFromDependecies<T>(IList<object> results) where T : class, IAssetBundleResource
|
||||
{
|
||||
if (results == null || results.Count == 0)
|
||||
return default(T);
|
||||
|
||||
IAssetBundleResource bundle = null;
|
||||
bool firstBundleWrapper = true;
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
var abWrapper = results[i] as IAssetBundleResource;
|
||||
if (abWrapper != null)
|
||||
{
|
||||
//only use the first asset bundle, even if it is invalid
|
||||
abWrapper.GetAssetBundle();
|
||||
if (firstBundleWrapper)
|
||||
bundle = abWrapper;
|
||||
firstBundleWrapper = false;
|
||||
}
|
||||
}
|
||||
|
||||
return bundle as T;
|
||||
}
|
||||
|
||||
public void Start(ProvideHandle provideHandle)
|
||||
{
|
||||
provideHandle.SetProgressCallback(ProgressCallback);
|
||||
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
subObjectName = null;
|
||||
m_ProvideHandle = provideHandle;
|
||||
m_RequestOperation = null;
|
||||
List<object> deps = new List<object>(); // TODO: garbage. need to pass actual count and reuse the list
|
||||
m_ProvideHandle.GetDependencies(deps);
|
||||
var bundleResource = LoadBundleFromDependecies<IAssetBundleResource>(deps);
|
||||
if (bundleResource == null)
|
||||
{
|
||||
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AssetBundle = bundleResource.GetAssetBundle();
|
||||
if (m_AssetBundle == null)
|
||||
{
|
||||
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
|
||||
return;
|
||||
}
|
||||
|
||||
var assetBundleResource = bundleResource as AssetBundleResource;
|
||||
if (assetBundleResource != null)
|
||||
m_PreloadRequest = assetBundleResource.GetAssetPreloadRequest();
|
||||
if (m_PreloadRequest == null || m_PreloadRequest.isDone)
|
||||
BeginAssetLoad();
|
||||
else
|
||||
m_PreloadRequest.completed += operation => BeginAssetLoad();
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginAssetLoad()
|
||||
{
|
||||
if (m_AssetBundle == null)
|
||||
{
|
||||
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
|
||||
}
|
||||
else
|
||||
{
|
||||
var assetPath = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
|
||||
if (m_ProvideHandle.Type.IsArray)
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
{
|
||||
GetArrayResult(m_AssetBundle.LoadAssetWithSubAssets(assetPath, m_ProvideHandle.Type.GetElementType()));
|
||||
CompleteOperation();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(assetPath, m_ProvideHandle.Type.GetElementType());
|
||||
}
|
||||
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
{
|
||||
GetListResult(m_AssetBundle.LoadAssetWithSubAssets(assetPath, m_ProvideHandle.Type.GetGenericArguments()[0]));
|
||||
CompleteOperation();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(assetPath, m_ProvideHandle.Type.GetGenericArguments()[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
|
||||
{
|
||||
subObjectName = subKey;
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
{
|
||||
GetAssetSubObjectResult(m_AssetBundle.LoadAssetWithSubAssets(mainPath, m_ProvideHandle.Type));
|
||||
CompleteOperation();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(mainPath, m_ProvideHandle.Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
if (AsyncOperationHandle.IsWaitingForCompletion)
|
||||
{
|
||||
GetAssetResult(m_AssetBundle.LoadAsset(assetPath, m_ProvideHandle.Type));
|
||||
CompleteOperation();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
m_RequestOperation = m_AssetBundle.LoadAssetAsync(assetPath, m_ProvideHandle.Type);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RequestOperation != null)
|
||||
{
|
||||
if (m_RequestOperation.isDone)
|
||||
ActionComplete(m_RequestOperation);
|
||||
else
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
if (UnityEngine.Profiling.Profiler.enabled && m_ProvideHandle.IsValid)
|
||||
Profiling.ProfilerRuntime.AddAssetOperation(m_ProvideHandle, Profiling.ContentStatus.Loading);
|
||||
#endif
|
||||
m_RequestOperation.completed += ActionComplete;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool WaitForCompletionHandler()
|
||||
{
|
||||
if (m_PreloadRequest != null && !m_PreloadRequest.isDone)
|
||||
return m_PreloadRequest.asset == null;
|
||||
if (m_Result != null)
|
||||
return true;
|
||||
if (m_RequestOperation == null)
|
||||
return false;
|
||||
if (m_RequestOperation.isDone)
|
||||
return true;
|
||||
return m_RequestOperation.asset != null;
|
||||
}
|
||||
|
||||
private void ActionComplete(AsyncOperation obj)
|
||||
{
|
||||
if (m_RequestOperation != null)
|
||||
{
|
||||
if (m_ProvideHandle.Type.IsArray)
|
||||
GetArrayResult(m_RequestOperation.allAssets);
|
||||
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
|
||||
GetListResult(m_RequestOperation.allAssets);
|
||||
else if (string.IsNullOrEmpty(subObjectName))
|
||||
GetAssetResult(m_RequestOperation.asset);
|
||||
else
|
||||
GetAssetSubObjectResult(m_RequestOperation.allAssets);
|
||||
}
|
||||
|
||||
CompleteOperation();
|
||||
}
|
||||
|
||||
private void GetArrayResult(Object[] allAssets)
|
||||
{
|
||||
m_Result = ResourceManagerConfig.CreateArrayResult(m_ProvideHandle.Type, allAssets);
|
||||
}
|
||||
|
||||
private void GetListResult(Object[] allAssets)
|
||||
{
|
||||
m_Result = ResourceManagerConfig.CreateListResult(m_ProvideHandle.Type, allAssets);
|
||||
}
|
||||
|
||||
private void GetAssetResult(Object asset)
|
||||
{
|
||||
m_Result = (asset != null && m_ProvideHandle.Type.IsAssignableFrom(asset.GetType())) ? asset : null;
|
||||
}
|
||||
|
||||
private void GetAssetSubObjectResult(Object[] allAssets)
|
||||
{
|
||||
foreach (var o in allAssets)
|
||||
{
|
||||
if (o.name == subObjectName)
|
||||
{
|
||||
if (m_ProvideHandle.Type.IsAssignableFrom(o.GetType()))
|
||||
{
|
||||
m_Result = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompleteOperation()
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
if (UnityEngine.Profiling.Profiler.enabled && m_Result != null && m_ProvideHandle.IsValid)
|
||||
Profiling.ProfilerRuntime.AddAssetOperation(m_ProvideHandle, Profiling.ContentStatus.Active);
|
||||
#endif
|
||||
|
||||
Exception e = m_Result == null
|
||||
? new Exception($"Unable to load asset of type {m_ProvideHandle.Type} from location {m_ProvideHandle.Location}.")
|
||||
: null;
|
||||
m_ProvideHandle.Complete(m_Result, m_Result != null, e);
|
||||
}
|
||||
|
||||
public float ProgressCallback()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Provide(ProvideHandle provideHandle)
|
||||
{
|
||||
new InternalOp().Start(provideHandle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a286e4526d8a69d4cb47c5d75f5acc83
|
||||
timeCreated: 1491321970
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that contains properties to apply to instantiated objects.
|
||||
/// </summary>
|
||||
public struct InstantiationParameters
|
||||
{
|
||||
Vector3 m_Position;
|
||||
Quaternion m_Rotation;
|
||||
Transform m_Parent;
|
||||
bool m_InstantiateInWorldPosition;
|
||||
bool m_SetPositionRotation;
|
||||
|
||||
/// <summary>
|
||||
/// Position in world space to instantiate object.
|
||||
/// </summary>
|
||||
public Vector3 Position
|
||||
{
|
||||
get { return m_Position; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotation in world space to instantiate object.
|
||||
/// </summary>
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get { return m_Rotation; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform to set as the parent of the instantiated object.
|
||||
/// </summary>
|
||||
public Transform Parent
|
||||
{
|
||||
get { return m_Parent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When setting the parent Transform, this sets whether to preserve instance transform relative to world space or relative to the parent.
|
||||
/// </summary>
|
||||
public bool InstantiateInWorldPosition
|
||||
{
|
||||
get { return m_InstantiateInWorldPosition; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag to tell the IInstanceProvider whether to set the position and rotation on new instances.
|
||||
/// </summary>
|
||||
public bool SetPositionRotation
|
||||
{
|
||||
get { return m_SetPositionRotation; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new InstantationParameters class that will set the parent transform and use the prefab transform.
|
||||
/// </summary>
|
||||
/// <param name="parent">Transform to set as the parent of the instantiated object.</param>
|
||||
/// <param name="instantiateInWorldSpace">Flag to tell the IInstanceProvider whether to set the position and rotation on new instances.</param>
|
||||
public InstantiationParameters(Transform parent, bool instantiateInWorldSpace)
|
||||
{
|
||||
m_Position = Vector3.zero;
|
||||
m_Rotation = Quaternion.identity;
|
||||
m_Parent = parent;
|
||||
m_InstantiateInWorldPosition = instantiateInWorldSpace;
|
||||
m_SetPositionRotation = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new InstantationParameters class that will set the position, rotation, and Transform parent of the instance.
|
||||
/// </summary>
|
||||
/// <param name="position">Position relative to the parent to set on the instance.</param>
|
||||
/// <param name="rotation">Rotation relative to the parent to set on the instance.</param>
|
||||
/// <param name="parent">Transform to set as the parent of the instantiated object.</param>
|
||||
public InstantiationParameters(Vector3 position, Quaternion rotation, Transform parent)
|
||||
{
|
||||
m_Position = position;
|
||||
m_Rotation = rotation;
|
||||
m_Parent = parent;
|
||||
m_InstantiateInWorldPosition = false;
|
||||
m_SetPositionRotation = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate an object with the parameters of this object.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">Object type. This type must be of type UnityEngine.Object.</typeparam>
|
||||
/// <param name="source">Object to instantiate.</param>
|
||||
/// <returns>Returns the instantiated object.</returns>
|
||||
public TObject Instantiate<TObject>(TObject source) where TObject : Object
|
||||
{
|
||||
TObject result;
|
||||
if (m_Parent == null)
|
||||
{
|
||||
if (m_SetPositionRotation)
|
||||
result = Object.Instantiate(source, m_Position, m_Rotation);
|
||||
else
|
||||
result = Object.Instantiate(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_SetPositionRotation)
|
||||
result = Object.Instantiate(source, m_Position, m_Rotation, m_Parent);
|
||||
else
|
||||
result = Object.Instantiate(source, m_Parent, m_InstantiateInWorldPosition);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface that provides instances of objects. This is used in ResourceManager.Instantiate* calls.
|
||||
/// </summary>
|
||||
public interface IInstanceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide an instance of the gameobject contained in the prefabHandle.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The object that contains all the resource locations.</param>
|
||||
/// <param name="prefabHandle">The operation handle for the prefab to instantiate.</param>
|
||||
/// <param name="instantiateParameters">The parameters to use for instantation.</param>
|
||||
/// <returns>The instantiated object.</returns>
|
||||
GameObject ProvideInstance(ResourceManager resourceManager, AsyncOperationHandle<GameObject> prefabHandle, InstantiationParameters instantiateParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Release an instance.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The object that contains all the resource locations.</param>
|
||||
/// <param name="instance">The instance to release.</param>
|
||||
void ReleaseInstance(ResourceManager resourceManager, GameObject instance);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f72ac6a7a32304f2baf84ce520e0a2b6
|
||||
timeCreated: 1504176125
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,195 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for resource provider behavior.
|
||||
/// </summary>
|
||||
public enum ProviderBehaviourFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the provider does not have extra specified behavior.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the provider will still fulfill requests even with failed dependencies.
|
||||
/// </summary>
|
||||
CanProvideWithFailedDependencies = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Container for all data need by providers to fulfill requests.
|
||||
/// </summary>
|
||||
public struct ProvideHandle
|
||||
{
|
||||
int m_Version;
|
||||
IGenericProviderOperation m_InternalOp;
|
||||
ResourceManager m_ResourceManager;
|
||||
|
||||
internal ProvideHandle(ResourceManager rm, IGenericProviderOperation op)
|
||||
{
|
||||
m_ResourceManager = rm;
|
||||
m_InternalOp = op;
|
||||
m_Version = op.ProvideHandleVersion;
|
||||
}
|
||||
|
||||
internal bool IsValid => m_InternalOp != null && m_InternalOp.ProvideHandleVersion == m_Version;
|
||||
|
||||
internal IGenericProviderOperation InternalOp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_InternalOp.ProvideHandleVersion != m_Version)
|
||||
{
|
||||
throw new Exception(ProviderOperation<object>.kInvalidHandleMsg);
|
||||
}
|
||||
|
||||
return m_InternalOp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ResourceManager used to create the operation.
|
||||
/// </summary>
|
||||
public ResourceManager ResourceManager
|
||||
{
|
||||
get { return m_ResourceManager; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The requested object type.
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get { return InternalOp.RequestedType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The location for the request.
|
||||
/// </summary>
|
||||
public IResourceLocation Location
|
||||
{
|
||||
get { return InternalOp.Location; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of dependencies.
|
||||
/// </summary>
|
||||
public int DependencyCount
|
||||
{
|
||||
get { return InternalOp.DependencyCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific dependency object.
|
||||
/// </summary>
|
||||
/// <typeparam name="TDepObject">The dependency type.</typeparam>
|
||||
/// <param name="index">The index of the dependency.</param>
|
||||
/// <returns>The dependency object.</returns>
|
||||
public TDepObject GetDependency<TDepObject>(int index)
|
||||
{
|
||||
return InternalOp.GetDependency<TDepObject>(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the depedency objects.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of dependecies to fill in.</param>
|
||||
public void GetDependencies(IList<object> list)
|
||||
{
|
||||
InternalOp.GetDependencies(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the func for handling progress requests.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback function.</param>
|
||||
public void SetProgressCallback(Func<float> callback)
|
||||
{
|
||||
InternalOp.SetProgressCallback(callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the func for handling download progress requests.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback function.</param>
|
||||
public void SetDownloadProgressCallbacks(Func<DownloadStatus> callback)
|
||||
{
|
||||
InternalOp.SetDownloadProgressCallback(callback);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the func for handling a request to wait for the completion of the operation
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback function.</param>
|
||||
public void SetWaitForCompletionCallback(Func<bool> callback)
|
||||
{
|
||||
InternalOp.SetWaitForCompletionCallback(callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to complete the operation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object requested.</typeparam>
|
||||
/// <param name="result">The result object.</param>
|
||||
/// <param name="status">True if the operation was successful, false otherwise.</param>
|
||||
/// <param name="exception">The exception if the operation failed.</param>
|
||||
public void Complete<T>(T result, bool status, Exception exception)
|
||||
{
|
||||
InternalOp.ProviderCompleted<T>(result, status, exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resoure Providers handle loading (Provide) and unloading (Release) of objects
|
||||
/// </summary>
|
||||
public interface IResourceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for this provider, used by Resource Locations to find a suitable Provider
|
||||
/// </summary>
|
||||
/// <value>The provider identifier.</value>
|
||||
string ProviderId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The default type of object that this provider can provide.
|
||||
/// </summary>
|
||||
/// <param name="location">The location that can be used to determine the type.</param>
|
||||
/// <returns>The type of object that can be provided.</returns>
|
||||
Type GetDefaultType(IResourceLocation location);
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this provider can provide the specified object type from the specified location.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of object.</param>
|
||||
/// <param name="location">The resource location of the object.</param>
|
||||
/// <returns>True if this provider can create the specified object.</returns>
|
||||
bool CanProvide(Type type, IResourceLocation location);
|
||||
|
||||
/// <summary>
|
||||
/// Tells the provide that it needs to provide a resource and report the results through the passed provideHandle. When this is called, all dependencies have completed and are available through the provideHandle.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">A handle used to update the operation.</param>
|
||||
void Provide(ProvideHandle provideHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Release and/or unload the given resource location and asset
|
||||
/// </summary>
|
||||
/// <param name="location">Location to release.</param>
|
||||
/// <param name="asset">Asset to unload.</param>
|
||||
void Release(IResourceLocation location, object asset);
|
||||
|
||||
/// <summary>
|
||||
/// Custom flags for the provider.
|
||||
/// </summary>
|
||||
ProviderBehaviourFlags BehaviourFlags { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d136ebd2e9594408296041252ad6d2a9
|
||||
timeCreated: 1503083765
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for scenes. This is used to allow access to the AsyncOperation and delayed activation.
|
||||
/// </summary>
|
||||
public struct SceneInstance
|
||||
{
|
||||
Scene m_Scene;
|
||||
internal AsyncOperation m_Operation;
|
||||
|
||||
/// <summary>
|
||||
/// The scene instance.
|
||||
/// </summary>
|
||||
public Scene Scene
|
||||
{
|
||||
get { return m_Scene; }
|
||||
internal set { m_Scene = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate the scene via the AsyncOperation.
|
||||
/// </summary>
|
||||
[Obsolete("Activate() has been deprecated. Please use ActivateAsync().")]
|
||||
public void Activate()
|
||||
{
|
||||
m_Operation.allowSceneActivation = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate the scene via the AsyncOperation. This is the scene loading AsyncOperation provided by the engine.
|
||||
/// The documentation for AsyncOperation can be found here: https://docs.unity3d.com/ScriptReference/AsyncOperation.html
|
||||
/// </summary>
|
||||
/// <returns>The scene load operation.</returns>
|
||||
public AsyncOperation ActivateAsync()
|
||||
{
|
||||
m_Operation.allowSceneActivation = true;
|
||||
return m_Operation;
|
||||
}
|
||||
|
||||
///<inheritdoc cref="Scene"/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Scene.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Scene"/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is SceneInstance))
|
||||
return false;
|
||||
return Scene.Equals(((SceneInstance)obj).Scene);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for scene providers.
|
||||
/// </summary>
|
||||
public interface ISceneProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Load a scene at a specified resource location.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
|
||||
/// <param name="location">The location of the scene.</param>
|
||||
/// <param name="loadMode">Load mode for the scene.</param>
|
||||
/// <param name="activateOnLoad">If true, the scene is activated as soon as it finished loading. Otherwise it needs to be activated via the returned SceneInstance object.</param>
|
||||
/// <param name="priority">The loading priority for the load.</param>
|
||||
/// <returns>An operation handle for the loading of the scene. The scene is wrapped in a SceneInstance object to support delayed activation.</returns>
|
||||
AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneMode loadMode, bool activateOnLoad, int priority);
|
||||
|
||||
/// <summary>
|
||||
/// Load a scene at a specified resource location.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
|
||||
/// <param name="location">The location of the scene.</param>
|
||||
/// <param name="loadSceneParameters">Load parameters for the scene.</param>
|
||||
/// <param name="activateOnLoad">If true, the scene is activated as soon as it finished loading. Otherwise it needs to be activated via the returned SceneInstance object.</param>
|
||||
/// <param name="priority">The loading priority for the load.</param>
|
||||
/// <returns>An operation handle for the loading of the scene. The scene is wrapped in a SceneInstance object to support delayed activation.</returns>
|
||||
AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority);
|
||||
|
||||
/// <summary>
|
||||
/// Release a scene.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
|
||||
/// <param name="sceneLoadHandle">The operation handle used to load the scene.</param>
|
||||
/// <returns>An operation handle for the unload.</returns>
|
||||
AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle);
|
||||
}
|
||||
|
||||
internal interface ISceneProvider2 : ISceneProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Release a scene.
|
||||
/// </summary>
|
||||
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
|
||||
/// <param name="sceneLoadHandle">The operation handle used to load the scene.</param>
|
||||
/// <returns>An operation handle for the unload.</returns>
|
||||
AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions unloadOptions);
|
||||
}
|
||||
|
||||
static internal class SceneProviderExtensions
|
||||
{
|
||||
public static AsyncOperationHandle<SceneInstance> ReleaseScene(this ISceneProvider provider, ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle,
|
||||
UnloadSceneOptions unloadOptions)
|
||||
{
|
||||
if (provider is ISceneProvider2)
|
||||
return ((ISceneProvider2)provider).ReleaseScene(resourceManager, sceneLoadHandle, unloadOptions);
|
||||
return provider.ReleaseScene(resourceManager, sceneLoadHandle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e135f99d5c3f044e4aba84ddbed0d30b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
namespace UnityEngine.ResourceManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Providers that implement this interface will received Update calls from the ResourceManager each frame
|
||||
/// </summary>
|
||||
public interface IUpdateReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// This will be called once per frame by the ResourceManager
|
||||
/// </summary>
|
||||
/// <param name="unscaledDeltaTime">The unscaled delta time since the last invocation of this function</param>
|
||||
void Update(float unscaledDeltaTime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c5f681e19be9ae4c845e9227f9b755a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic implementation of IInstanceProvider.
|
||||
/// </summary>
|
||||
public class InstanceProvider : IInstanceProvider
|
||||
{
|
||||
Dictionary<GameObject, AsyncOperationHandle<GameObject>> m_InstanceObjectToPrefabHandle = new Dictionary<GameObject, AsyncOperationHandle<GameObject>>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public GameObject ProvideInstance(ResourceManager resourceManager, AsyncOperationHandle<GameObject> prefabHandle, InstantiationParameters instantiateParameters)
|
||||
{
|
||||
GameObject result = instantiateParameters.Instantiate(prefabHandle.Result);
|
||||
m_InstanceObjectToPrefabHandle.Add(result, prefabHandle);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ReleaseInstance(ResourceManager resourceManager, GameObject instance)
|
||||
{
|
||||
AsyncOperationHandle<GameObject> resource;
|
||||
if (!m_InstanceObjectToPrefabHandle.TryGetValue(instance, out resource))
|
||||
{
|
||||
Debug.LogWarningFormat("Releasing unknown GameObject {0} to InstanceProvider.", instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceManager.Release(resource);
|
||||
m_InstanceObjectToPrefabHandle.Remove(instance);
|
||||
}
|
||||
|
||||
if (instance != null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
Object.Destroy(instance);
|
||||
else
|
||||
Object.DestroyImmediate(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 80a4732d85a576b4da4c5492957b9189
|
||||
timeCreated: 1491321970
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts JSON serialized text into the requested object.
|
||||
/// </summary>
|
||||
[DisplayName("JSON Asset Provider")]
|
||||
public class JsonAssetProvider : TextDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts raw text into requested object type via JSONUtility.FromJson.
|
||||
/// </summary>
|
||||
/// <param name="type">The object type the text is converted to.</param>
|
||||
/// <param name="text">The text to convert.</param>
|
||||
/// <returns>Returns the converted object.</returns>
|
||||
public override object Convert(Type type, string text)
|
||||
{
|
||||
return JsonUtility.FromJson(text, type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 22e49408c617c404480396d74c837024
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides assets loaded via Resources.LoadAsync API.
|
||||
/// </summary>
|
||||
[DisplayName("Assets from Legacy Resources")]
|
||||
public class LegacyResourcesProvider : ResourceProviderBase
|
||||
{
|
||||
internal class InternalOp
|
||||
{
|
||||
ResourceRequest m_RequestOperation;
|
||||
ProvideHandle m_ProvideHandle;
|
||||
|
||||
public void Start(ProvideHandle provideHandle)
|
||||
{
|
||||
m_ProvideHandle = provideHandle;
|
||||
|
||||
provideHandle.SetProgressCallback(PercentComplete);
|
||||
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
m_RequestOperation = Resources.LoadAsync(m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location), m_ProvideHandle.Type);
|
||||
m_RequestOperation.completed += AsyncOperationCompleted;
|
||||
}
|
||||
|
||||
private bool WaitForCompletionHandler()
|
||||
{
|
||||
if (!m_RequestOperation.isDone && Mathf.Approximately(m_RequestOperation.progress, 1f))
|
||||
{
|
||||
m_RequestOperation.completed -= AsyncOperationCompleted;
|
||||
AsyncOperationCompleted(m_RequestOperation);
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_RequestOperation != null && m_RequestOperation.isDone;
|
||||
}
|
||||
|
||||
private void AsyncOperationCompleted(AsyncOperation op)
|
||||
{
|
||||
var request = op as ResourceRequest;
|
||||
object result = request != null ? request.asset : null;
|
||||
result = result != null && m_ProvideHandle.Type.IsAssignableFrom(result.GetType()) ? result : null;
|
||||
m_ProvideHandle.Complete(result, result != null,
|
||||
result == null ? new Exception($"Unable to load asset of type {m_ProvideHandle.Type} from location {m_ProvideHandle.Location}.") : null);
|
||||
}
|
||||
|
||||
public float PercentComplete()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Provide(ProvideHandle pi)
|
||||
{
|
||||
Type t = pi.Type;
|
||||
bool isList = t.IsGenericType && typeof(IList<>) == t.GetGenericTypeDefinition();
|
||||
var internalId = pi.ResourceManager.TransformInternalId(pi.Location);
|
||||
if (t.IsArray || isList)
|
||||
{
|
||||
object result = null;
|
||||
if (t.IsArray)
|
||||
result = ResourceManagerConfig.CreateArrayResult(t, Resources.LoadAll(internalId, t.GetElementType()));
|
||||
else
|
||||
result = ResourceManagerConfig.CreateListResult(t, Resources.LoadAll(internalId, t.GetGenericArguments()[0]));
|
||||
|
||||
pi.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {pi.Type} from location {pi.Location}.") : null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ResourceManagerConfig.ExtractKeyAndSubKey(internalId, out string mainPath, out string subKey))
|
||||
{
|
||||
var objs = Resources.LoadAll(mainPath, pi.Type);
|
||||
object result = null;
|
||||
foreach (var o in objs)
|
||||
{
|
||||
if (o.name == subKey)
|
||||
{
|
||||
if (pi.Type.IsAssignableFrom(o.GetType()))
|
||||
{
|
||||
result = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pi.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {pi.Type} from location {pi.Location}.") : null);
|
||||
}
|
||||
else
|
||||
{
|
||||
new InternalOp().Start(pi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Release(IResourceLocation location, object asset)
|
||||
{
|
||||
if (location == null)
|
||||
throw new ArgumentNullException("location");
|
||||
var obj = asset as Object;
|
||||
//GameObjects cannot be resleased via Object.Destroy because they are considered an asset
|
||||
//but they can't be unloaded via Resources.UnloadAsset since they are NOT an asset?
|
||||
if (obj != null && !(obj is GameObject))
|
||||
Resources.UnloadAsset(obj);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c6cb8c4cac7a64d109ab5f65c0f85aca
|
||||
timeCreated: 1504834357
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,165 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for IResourceProvider.
|
||||
/// </summary>
|
||||
public abstract class ResourceProviderBase : IResourceProvider, IInitializableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique identifier of the provider.
|
||||
/// </summary>
|
||||
protected string m_ProviderId;
|
||||
|
||||
/// <summary>
|
||||
/// The extra behavior of the provider.
|
||||
/// </summary>
|
||||
protected ProviderBehaviourFlags m_BehaviourFlags = ProviderBehaviourFlags.None;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual string ProviderId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_ProviderId))
|
||||
m_ProviderId = GetType().FullName;
|
||||
|
||||
return m_ProviderId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool Initialize(string id, string data)
|
||||
{
|
||||
m_ProviderId = id;
|
||||
return !string.IsNullOrEmpty(m_ProviderId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool CanProvide(Type t, IResourceLocation location)
|
||||
{
|
||||
return GetDefaultType(location).IsAssignableFrom(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts information about the resource provider to a formatted string.
|
||||
/// </summary>
|
||||
/// <returns>Returns information about the resource provider.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ProviderId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the specified object that was created from the specified location.
|
||||
/// </summary>
|
||||
/// <param name="location">The location of the object</param>
|
||||
/// <param name="obj">The object to release.</param>
|
||||
public virtual void Release(IResourceLocation location, object obj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default type of object that this provider can provide.
|
||||
/// </summary>
|
||||
/// <param name="location"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Type GetDefaultType(IResourceLocation location)
|
||||
{
|
||||
return typeof(object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide the object specified in the provideHandle.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">Contains all data needed to provide the requested object.</param>
|
||||
public abstract void Provide(ProvideHandle provideHandle);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual AsyncOperationHandle<bool> InitializeAsync(ResourceManager rm, string id, string data)
|
||||
{
|
||||
BaseInitAsyncOp baseInitOp = new BaseInitAsyncOp();
|
||||
baseInitOp.Init(() => Initialize(id, data));
|
||||
return rm.StartOperation(baseInitOp, default);
|
||||
}
|
||||
|
||||
ProviderBehaviourFlags IResourceProvider.BehaviourFlags
|
||||
{
|
||||
get { return m_BehaviourFlags; }
|
||||
}
|
||||
|
||||
class BaseInitAsyncOp : AsyncOperationBase<bool>
|
||||
{
|
||||
private Func<bool> m_CallBack;
|
||||
|
||||
public void Init(Func<bool> callback)
|
||||
{
|
||||
m_CallBack = callback;
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
if (m_CallBack != null)
|
||||
Complete(m_CallBack(), true, "");
|
||||
else
|
||||
Complete(true, true, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains options used in Resource Provider load requests. ProviderLoadRequestOptions are used to specify
|
||||
/// parameters such as whether or not to ignore load failures and UnityWebRequest timeouts.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ProviderLoadRequestOptions
|
||||
{
|
||||
[SerializeField]
|
||||
private bool m_IgnoreFailures = false;
|
||||
|
||||
private int m_WebRequestTimeout = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a memberwise clone of a given ProviderLoadRequestOption.
|
||||
/// </summary>
|
||||
/// <returns>The newly created ProviderLoadRequestOption object</returns>
|
||||
public ProviderLoadRequestOptions Copy()
|
||||
{
|
||||
return (ProviderLoadRequestOptions)this.MemberwiseClone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IgnoreFailures for provider load requests
|
||||
/// </summary>
|
||||
public bool IgnoreFailures
|
||||
{
|
||||
get { return m_IgnoreFailures; }
|
||||
set { m_IgnoreFailures = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnityWebRequest Timeout
|
||||
/// </summary>
|
||||
public int WebRequestTimeout
|
||||
{
|
||||
get => m_WebRequestTimeout;
|
||||
set => m_WebRequestTimeout = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 353e24853179e47bf9ebb9de6c42e428
|
||||
timeCreated: 1503084871
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,308 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.Assertions.Must;
|
||||
using UnityEngine.ResourceManagement;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation if ISceneProvider
|
||||
/// </summary>
|
||||
public class SceneProvider : ISceneProvider2
|
||||
{
|
||||
class SceneOp : AsyncOperationBase<SceneInstance>, IUpdateReceiver
|
||||
{
|
||||
bool m_ActivateOnLoad;
|
||||
SceneInstance m_Inst;
|
||||
IResourceLocation m_Location;
|
||||
LoadSceneParameters m_LoadSceneParameters;
|
||||
int m_Priority;
|
||||
private AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
|
||||
ResourceManager m_ResourceManager;
|
||||
|
||||
public SceneOp(ResourceManager rm)
|
||||
{
|
||||
m_ResourceManager = rm;
|
||||
}
|
||||
|
||||
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
return m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : new DownloadStatus() { IsDone = IsDone };
|
||||
}
|
||||
|
||||
public void Init(IResourceLocation location, LoadSceneMode loadSceneMode, bool activateOnLoad, int priority, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
|
||||
{
|
||||
Init(location, new LoadSceneParameters(loadSceneMode), activateOnLoad, priority, depOp);
|
||||
}
|
||||
|
||||
public void Init(IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
|
||||
{
|
||||
m_DepOp = depOp;
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Acquire();
|
||||
|
||||
m_Location = location;
|
||||
m_LoadSceneParameters = loadSceneParameters;
|
||||
m_ActivateOnLoad = activateOnLoad;
|
||||
m_Priority = priority;
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
|
||||
m_DepOp.WaitForCompletion();
|
||||
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
|
||||
while (!IsDone)
|
||||
{
|
||||
((IUpdateReceiver)this).Update(Time.unscaledDeltaTime);
|
||||
//We need the operation to complete but it'll take a frame to activate the scene (post 0.9 progress).
|
||||
if (m_Inst.m_Operation.progress == 0 && timer.ElapsedMilliseconds > 5000)
|
||||
throw new Exception(
|
||||
"Infinite loop detected within LoadSceneAsync.WaitForCompletion. For more information see the notes under the Scenes section of the \"Synchronous Addressables\" page of the Addressables documentation, or consider using asynchronous scene loading code.");
|
||||
|
||||
if (m_Inst.m_Operation.allowSceneActivation && Mathf.Approximately(m_Inst.m_Operation.progress, .9f))
|
||||
{
|
||||
Result = m_Inst;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return IsDone;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
deps.Add(m_DepOp);
|
||||
}
|
||||
|
||||
protected override string DebugName
|
||||
{
|
||||
get { return string.Format("Scene({0})", m_Location == null ? "Invalid" : ShortenPath(m_ResourceManager.TransformInternalId(m_Location), false)); }
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
var loadingFromBundle = false;
|
||||
if (m_DepOp.IsValid())
|
||||
{
|
||||
foreach (var d in m_DepOp.Result)
|
||||
{
|
||||
var abResource = d.Result as IAssetBundleResource;
|
||||
if (abResource != null && abResource.GetAssetBundle() != null)
|
||||
loadingFromBundle = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_DepOp.IsValid() || m_DepOp.OperationException == null)
|
||||
{
|
||||
m_Inst = InternalLoadScene(m_Location, loadingFromBundle, m_LoadSceneParameters, m_ActivateOnLoad, m_Priority);
|
||||
((IUpdateReceiver)this).Update(0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Complete(m_Inst, false, m_DepOp.OperationException);
|
||||
}
|
||||
|
||||
HasExecuted = true;
|
||||
}
|
||||
|
||||
internal SceneInstance InternalLoadScene(IResourceLocation location, bool loadingFromBundle, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority)
|
||||
{
|
||||
var internalId = m_ResourceManager.TransformInternalId(location);
|
||||
var op = InternalLoad(internalId, loadingFromBundle, loadSceneParameters);
|
||||
op.allowSceneActivation = activateOnLoad;
|
||||
op.priority = priority;
|
||||
return new SceneInstance() { m_Operation = op, Scene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1) };
|
||||
}
|
||||
|
||||
AsyncOperation InternalLoad(string path, bool loadingFromBundle, LoadSceneParameters loadSceneParameters)
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Loading);
|
||||
#endif
|
||||
return SceneManager.LoadSceneAsync(path, loadSceneParameters);
|
||||
#else
|
||||
if (loadingFromBundle)
|
||||
{
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Loading);
|
||||
#endif
|
||||
return SceneManager.LoadSceneAsync(path, loadSceneParameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) && !path.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
|
||||
path = "Assets/" + path;
|
||||
if (path.LastIndexOf(".unity", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
path += ".unity";
|
||||
|
||||
return UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(path, loadSceneParameters);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
//the scene will be unloaded via the UnloadSceneOp as it waits correctlyf or the unload to complete before releasing the load op
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
base.Destroy();
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
float depOpWeight = 0.9f;
|
||||
float loadOpWeight = 0.1f;
|
||||
float progress = 0f;
|
||||
|
||||
//We will always have an instance operation but this will be null until the dependant operation is completed.
|
||||
if (m_Inst.m_Operation != null)
|
||||
progress += m_Inst.m_Operation.progress * loadOpWeight;
|
||||
|
||||
if (!m_DepOp.IsDone)
|
||||
progress += m_DepOp.PercentComplete * depOpWeight;
|
||||
else
|
||||
progress += depOpWeight;
|
||||
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
void IUpdateReceiver.Update(float unscaledDeltaTime)
|
||||
{
|
||||
if (m_Inst.m_Operation != null)
|
||||
{
|
||||
if (m_Inst.m_Operation.isDone || (!m_Inst.m_Operation.allowSceneActivation && Mathf.Approximately(m_Inst.m_Operation.progress, .9f)))
|
||||
{
|
||||
m_ResourceManager.RemoveUpdateReciever(this);
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Active);
|
||||
#endif
|
||||
Complete(m_Inst, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UnloadSceneOp : AsyncOperationBase<SceneInstance>
|
||||
{
|
||||
SceneInstance m_Instance;
|
||||
AsyncOperationHandle<SceneInstance> m_sceneLoadHandle;
|
||||
UnloadSceneOptions m_UnloadOptions;
|
||||
|
||||
public void Init(AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions options)
|
||||
{
|
||||
if (sceneLoadHandle.ReferenceCount > 0)
|
||||
{
|
||||
m_sceneLoadHandle = sceneLoadHandle;
|
||||
m_Instance = m_sceneLoadHandle.Result;
|
||||
}
|
||||
|
||||
m_UnloadOptions = options;
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
if (m_sceneLoadHandle.IsValid() && m_Instance.Scene.isLoaded)
|
||||
{
|
||||
var unloadOp = SceneManager.UnloadSceneAsync(m_Instance.Scene, m_UnloadOptions);
|
||||
if (unloadOp == null)
|
||||
UnloadSceneCompletedNoRelease(null);
|
||||
else
|
||||
unloadOp.completed += UnloadSceneCompletedNoRelease;
|
||||
}
|
||||
else
|
||||
UnloadSceneCompleted(null);
|
||||
|
||||
HasExecuted = true;
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
Debug.LogWarning("Cannot unload a Scene with WaitForCompletion. Scenes must be unloaded asynchronously.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UnloadSceneCompleted(AsyncOperation obj)
|
||||
{
|
||||
Complete(m_Instance, true, "");
|
||||
if (m_sceneLoadHandle.IsValid())
|
||||
m_sceneLoadHandle.Release();
|
||||
}
|
||||
|
||||
private void UnloadSceneCompletedNoRelease(AsyncOperation obj)
|
||||
{
|
||||
Complete(m_Instance, true, "");
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get { return m_sceneLoadHandle.PercentComplete; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneMode loadSceneMode, bool activateOnLoad, int priority)
|
||||
{
|
||||
return ProvideScene(resourceManager, location, new LoadSceneParameters(loadSceneMode), activateOnLoad, priority);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority)
|
||||
{
|
||||
AsyncOperationHandle<IList<AsyncOperationHandle>> depOp = default(AsyncOperationHandle<IList<AsyncOperationHandle>>);
|
||||
if (location.HasDependencies)
|
||||
depOp = resourceManager.ProvideResourceGroupCached(location.Dependencies, location.DependencyHashCode, typeof(IAssetBundleResource), null);
|
||||
|
||||
SceneOp op = new SceneOp(resourceManager);
|
||||
op.Init(location, loadSceneParameters, activateOnLoad, priority, depOp);
|
||||
|
||||
var handle = resourceManager.StartOperation<SceneInstance>(op, depOp);
|
||||
|
||||
if (depOp.IsValid())
|
||||
depOp.Release();
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle)
|
||||
{
|
||||
return ((ISceneProvider2)(this)).ReleaseScene(resourceManager, sceneLoadHandle, UnloadSceneOptions.None);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
AsyncOperationHandle<SceneInstance> ISceneProvider2.ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions unloadOptions)
|
||||
{
|
||||
var unloadOp = new UnloadSceneOp();
|
||||
unloadOp.Init(sceneLoadHandle, unloadOptions);
|
||||
#if ENABLE_ADDRESSABLE_PROFILER
|
||||
Profiling.ProfilerRuntime.SceneReleased(sceneLoadHandle);
|
||||
#endif
|
||||
return resourceManager.StartOperation(unloadOp, sceneLoadHandle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 722d9658f4892ba47a7a9db5fdf152d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a1eb7ad3cb0ad104b9cf9b6018e9d578
|
||||
folderAsset: yes
|
||||
timeCreated: 1498526414
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,611 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
|
||||
{
|
||||
abstract class VBAsyncOperation
|
||||
{
|
||||
public abstract DownloadStatus GetDownloadStatus();
|
||||
public abstract bool WaitForCompletion();
|
||||
}
|
||||
|
||||
class VBAsyncOperation<TObject> : VBAsyncOperation
|
||||
{
|
||||
protected TObject m_Result;
|
||||
protected AsyncOperationStatus m_Status;
|
||||
protected Exception m_Error;
|
||||
protected object m_Context;
|
||||
|
||||
DelegateList<VBAsyncOperation<TObject>> m_CompletedAction;
|
||||
Action<VBAsyncOperation<TObject>> m_OnDestroyAction;
|
||||
|
||||
public override DownloadStatus GetDownloadStatus() => default;
|
||||
public override bool WaitForCompletion() => true;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var instId = "";
|
||||
var or = m_Result as Object;
|
||||
if (or != null)
|
||||
instId = "(" + or.GetInstanceID() + ")";
|
||||
return string.Format("{0}, result='{1}', status='{2}', location={3}.", base.ToString(), (m_Result + instId), m_Status, m_Context);
|
||||
}
|
||||
|
||||
public event Action<VBAsyncOperation<TObject>> Completed
|
||||
{
|
||||
add
|
||||
{
|
||||
if (IsDone)
|
||||
{
|
||||
DelayedActionManager.AddAction(value, 0, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_CompletedAction == null)
|
||||
m_CompletedAction = DelegateList<VBAsyncOperation<TObject>>.CreateWithGlobalCache();
|
||||
m_CompletedAction.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
remove { m_CompletedAction.Remove(value); }
|
||||
}
|
||||
|
||||
public AsyncOperationStatus Status
|
||||
{
|
||||
get { return m_Status; }
|
||||
protected set { m_Status = value; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Exception OperationException
|
||||
{
|
||||
get { return m_Error; }
|
||||
protected set
|
||||
{
|
||||
m_Error = value;
|
||||
if (m_Error != null && ResourceManager.ExceptionHandler != null)
|
||||
ResourceManager.ExceptionHandler(new AsyncOperationHandle(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
public TObject Result
|
||||
{
|
||||
get { return m_Result; }
|
||||
}
|
||||
|
||||
public virtual bool IsDone
|
||||
{
|
||||
get { return Status == AsyncOperationStatus.Failed || Status == AsyncOperationStatus.Succeeded; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual float PercentComplete
|
||||
{
|
||||
get { return IsDone ? 1f : 0f; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Context
|
||||
{
|
||||
get { return m_Context; }
|
||||
set { m_Context = value; }
|
||||
}
|
||||
|
||||
public void InvokeCompletionEvent()
|
||||
{
|
||||
if (m_CompletedAction != null)
|
||||
{
|
||||
m_CompletedAction.Invoke(this);
|
||||
m_CompletedAction.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetResult(TObject result)
|
||||
{
|
||||
m_Result = result;
|
||||
m_Status = (m_Result == null) ? AsyncOperationStatus.Failed : AsyncOperationStatus.Succeeded;
|
||||
}
|
||||
|
||||
public VBAsyncOperation<TObject> StartCompleted(object context, object key, TObject val, Exception error = null)
|
||||
{
|
||||
Context = context;
|
||||
OperationException = error;
|
||||
m_Result = val;
|
||||
m_Status = (m_Result == null) ? AsyncOperationStatus.Failed : AsyncOperationStatus.Succeeded;
|
||||
DelayedActionManager.AddAction((Action)InvokeCompletionEvent);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains data needed to simulate a bundled asset
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VirtualAssetBundleEntry
|
||||
{
|
||||
[FormerlySerializedAs("m_name")]
|
||||
[SerializeField]
|
||||
string m_Name;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the asset.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_size")]
|
||||
[SerializeField]
|
||||
long m_Size;
|
||||
|
||||
/// <summary>
|
||||
/// The file size of the asset, in bytes.
|
||||
/// </summary>
|
||||
public long Size
|
||||
{
|
||||
get { return m_Size; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
internal string m_AssetPath;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundleEntry
|
||||
/// </summary>
|
||||
public VirtualAssetBundleEntry()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundleEntry
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the asset.</param>
|
||||
/// <param name="size">The size of the asset, in bytes.</param>
|
||||
public VirtualAssetBundleEntry(string name, long size)
|
||||
{
|
||||
m_Name = name;
|
||||
m_Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains data need to simulate an asset bundle.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VirtualAssetBundle : ISerializationCallbackReceiver, IAssetBundleResource
|
||||
{
|
||||
[FormerlySerializedAs("m_name")]
|
||||
[SerializeField]
|
||||
string m_Name;
|
||||
|
||||
[FormerlySerializedAs("m_isLocal")]
|
||||
[SerializeField]
|
||||
bool m_IsLocal;
|
||||
|
||||
[FormerlySerializedAs("m_dataSize")]
|
||||
[SerializeField]
|
||||
long m_DataSize;
|
||||
|
||||
[FormerlySerializedAs("m_headerSize")]
|
||||
[SerializeField]
|
||||
long m_HeaderSize;
|
||||
|
||||
[FormerlySerializedAs("m_latency")]
|
||||
[SerializeField]
|
||||
float m_Latency;
|
||||
|
||||
[SerializeField]
|
||||
uint m_Crc;
|
||||
|
||||
[SerializeField]
|
||||
string m_Hash;
|
||||
|
||||
[FormerlySerializedAs("m_serializedAssets")]
|
||||
[SerializeField]
|
||||
List<VirtualAssetBundleEntry> m_SerializedAssets = new List<VirtualAssetBundleEntry>();
|
||||
|
||||
long m_HeaderBytesLoaded;
|
||||
long m_DataBytesLoaded;
|
||||
|
||||
LoadAssetBundleOp m_BundleLoadOperation;
|
||||
List<IVirtualLoadable> m_AssetLoadOperations = new List<IVirtualLoadable>();
|
||||
Dictionary<string, VirtualAssetBundleEntry> m_AssetMap;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the bundle.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assets contained in the bundle.
|
||||
/// </summary>
|
||||
public List<VirtualAssetBundleEntry> Assets
|
||||
{
|
||||
get { return m_SerializedAssets; }
|
||||
}
|
||||
|
||||
const long k_SynchronousBytesPerSecond = (long)1024 * 1024 * 1024 * 10; // 10 Gb/s
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundle object.
|
||||
/// </summary>
|
||||
public VirtualAssetBundle()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The percent of data that has been loaded.
|
||||
/// </summary>
|
||||
public float PercentComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_HeaderSize + m_DataSize <= 0)
|
||||
return 1;
|
||||
|
||||
return (float)(m_HeaderBytesLoaded + m_DataBytesLoaded) / (m_HeaderSize + m_DataSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundle
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the bundle.</param>
|
||||
/// <param name="local">Is the bundle local or remote. This is used to determine which bandwidth value to use when simulating loading.</param>
|
||||
public VirtualAssetBundle(string name, bool local, uint crc, string hash)
|
||||
{
|
||||
m_Latency = .1f;
|
||||
m_Name = name;
|
||||
m_IsLocal = local;
|
||||
m_HeaderBytesLoaded = 0;
|
||||
m_DataBytesLoaded = 0;
|
||||
m_Crc = crc;
|
||||
m_Hash = hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the size of the bundle.
|
||||
/// </summary>
|
||||
/// <param name="dataSize">The size of the data.</param>
|
||||
/// <param name="headerSize">The size of the header.</param>
|
||||
public void SetSize(long dataSize, long headerSize)
|
||||
{
|
||||
m_HeaderSize = headerSize;
|
||||
m_DataSize = dataSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not used
|
||||
/// </summary>
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load serialized data into runtime structures.
|
||||
/// </summary>
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
m_AssetMap = new Dictionary<string, VirtualAssetBundleEntry>();
|
||||
foreach (var a in m_SerializedAssets)
|
||||
m_AssetMap.Add(a.Name, a);
|
||||
}
|
||||
|
||||
class LoadAssetBundleOp : VBAsyncOperation<VirtualAssetBundle>
|
||||
{
|
||||
VirtualAssetBundle m_Bundle;
|
||||
float m_TimeInLoadingState;
|
||||
bool m_crcHashValidated;
|
||||
|
||||
public LoadAssetBundleOp(IResourceLocation location, VirtualAssetBundle bundle)
|
||||
{
|
||||
Context = location;
|
||||
m_Bundle = bundle;
|
||||
m_TimeInLoadingState = 0.0f;
|
||||
}
|
||||
|
||||
public override bool WaitForCompletion()
|
||||
{
|
||||
SetResult(m_Bundle);
|
||||
InvokeCompletionEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override DownloadStatus GetDownloadStatus()
|
||||
{
|
||||
if (m_Bundle.m_IsLocal)
|
||||
return new DownloadStatus() {IsDone = IsDone};
|
||||
return new DownloadStatus() {DownloadedBytes = m_Bundle.m_DataBytesLoaded, TotalBytes = m_Bundle.m_DataSize, IsDone = IsDone};
|
||||
}
|
||||
|
||||
public override float PercentComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsDone)
|
||||
return 1f;
|
||||
return m_Bundle.PercentComplete;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
|
||||
{
|
||||
if (m_Result != null)
|
||||
return;
|
||||
|
||||
if (!m_crcHashValidated)
|
||||
{
|
||||
var location = Context as IResourceLocation;
|
||||
var reqOptions = location.Data as AssetBundleRequestOptions;
|
||||
if (reqOptions != null)
|
||||
{
|
||||
if (reqOptions.Crc != 0 && m_Bundle.m_Crc != reqOptions.Crc)
|
||||
{
|
||||
var err = string.Format("Error while downloading Asset Bundle: CRC Mismatch. Provided {0}, calculated {1} from data. Will not load Asset Bundle.", reqOptions.Crc,
|
||||
m_Bundle.m_Crc);
|
||||
SetResult(null);
|
||||
OperationException = new Exception(err);
|
||||
InvokeCompletionEvent();
|
||||
}
|
||||
|
||||
if (!m_Bundle.m_IsLocal)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(reqOptions.Hash))
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_Bundle.m_Hash) || m_Bundle.m_Hash != reqOptions.Hash)
|
||||
{
|
||||
Debug.LogWarningFormat("Mismatched hash in bundle {0}.", m_Bundle.Name);
|
||||
}
|
||||
//TODO: implement virtual cache that would persist between runs.
|
||||
//if(vCache.hashBundle(m_Bundle.Name, reqOptions.Hash))
|
||||
// m_m_Bundle.IsLocal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_crcHashValidated = true;
|
||||
}
|
||||
|
||||
m_TimeInLoadingState += unscaledDeltaTime;
|
||||
if (m_TimeInLoadingState > m_Bundle.m_Latency)
|
||||
{
|
||||
long localBytes = (long)Math.Ceiling(unscaledDeltaTime * localBandwidth);
|
||||
long remoteBytes = (long)Math.Ceiling(unscaledDeltaTime * remoteBandwidth);
|
||||
|
||||
if (m_Bundle.LoadData(localBytes, remoteBytes))
|
||||
{
|
||||
SetResult(m_Bundle);
|
||||
InvokeCompletionEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadData(long localBytes, long remoteBytes)
|
||||
{
|
||||
if (m_IsLocal)
|
||||
{
|
||||
m_HeaderBytesLoaded += localBytes;
|
||||
if (m_HeaderBytesLoaded < m_HeaderSize)
|
||||
return false;
|
||||
m_HeaderBytesLoaded = m_HeaderSize;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_DataBytesLoaded < m_DataSize)
|
||||
{
|
||||
m_DataBytesLoaded += remoteBytes;
|
||||
if (m_DataBytesLoaded < m_DataSize)
|
||||
return false;
|
||||
m_DataBytesLoaded = m_DataSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_HeaderBytesLoaded += localBytes;
|
||||
if (m_HeaderBytesLoaded < m_HeaderSize)
|
||||
return false;
|
||||
m_HeaderBytesLoaded = m_HeaderSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Unload()
|
||||
{
|
||||
if (m_BundleLoadOperation == null)
|
||||
Debug.LogWarningFormat("Simulated assetbundle {0} is already unloaded.", m_Name);
|
||||
m_HeaderBytesLoaded = 0;
|
||||
m_BundleLoadOperation = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal VBAsyncOperation<VirtualAssetBundle> StartLoad(IResourceLocation location)
|
||||
{
|
||||
if (m_BundleLoadOperation != null)
|
||||
{
|
||||
if (m_BundleLoadOperation.IsDone)
|
||||
Debug.LogWarningFormat("Simulated assetbundle {0} is already loaded.", m_Name);
|
||||
else
|
||||
Debug.LogWarningFormat("Simulated assetbundle {0} is already loading.", m_Name);
|
||||
return m_BundleLoadOperation;
|
||||
}
|
||||
|
||||
m_HeaderBytesLoaded = 0;
|
||||
return (m_BundleLoadOperation = new LoadAssetBundleOp(location, this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an asset via its location. The asset will actually be loaded via the AssetDatabase API.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject"></typeparam>
|
||||
/// <param name="location"></param>
|
||||
/// <returns></returns>
|
||||
internal VBAsyncOperation<object> LoadAssetAsync(ProvideHandle provideHandle, IResourceLocation location)
|
||||
{
|
||||
if (location == null)
|
||||
throw new ArgumentException("IResourceLocation location cannot be null.");
|
||||
if (m_BundleLoadOperation == null)
|
||||
return new VBAsyncOperation<object>().StartCompleted(location, location, null, new ResourceManagerException("LoadAssetAsync called on unloaded bundle " + m_Name));
|
||||
|
||||
if (!m_BundleLoadOperation.IsDone)
|
||||
return new VBAsyncOperation<object>().StartCompleted(location, location, null, new ResourceManagerException("LoadAssetAsync called on loading bundle " + m_Name));
|
||||
VirtualAssetBundleEntry assetInfo;
|
||||
var assetPath = location.InternalId;
|
||||
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
|
||||
assetPath = mainPath;
|
||||
|
||||
//this needs to use the non translated internal id since that was how the table was built.
|
||||
if (!m_AssetMap.TryGetValue(assetPath, out assetInfo))
|
||||
return new VBAsyncOperation<object>().StartCompleted(location, location, null,
|
||||
new ResourceManagerException(string.Format("Unable to load asset {0} from simulated bundle {1}.", location.InternalId, Name)));
|
||||
|
||||
var op = new LoadAssetOp(location, assetInfo, provideHandle);
|
||||
m_AssetLoadOperations.Add(op);
|
||||
return op;
|
||||
}
|
||||
|
||||
internal void CountBandwidthUsage(ref long localCount, ref long remoteCount)
|
||||
{
|
||||
if (m_BundleLoadOperation != null && m_BundleLoadOperation.IsDone)
|
||||
{
|
||||
localCount += m_AssetLoadOperations.Count;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_IsLocal)
|
||||
{
|
||||
localCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_DataBytesLoaded < m_DataSize)
|
||||
remoteCount++;
|
||||
else
|
||||
localCount++;
|
||||
}
|
||||
}
|
||||
|
||||
interface IVirtualLoadable
|
||||
{
|
||||
bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime);
|
||||
}
|
||||
|
||||
// TODO: This is only needed internally. We can change this to not derive off of AsyncOperationBase and simplify the code
|
||||
class LoadAssetOp : VBAsyncOperation<object>, IVirtualLoadable
|
||||
{
|
||||
long m_BytesLoaded;
|
||||
float m_LastUpdateTime;
|
||||
VirtualAssetBundleEntry m_AssetInfo;
|
||||
ProvideHandle m_provideHandle;
|
||||
|
||||
public LoadAssetOp(IResourceLocation location, VirtualAssetBundleEntry assetInfo, ProvideHandle ph)
|
||||
{
|
||||
m_provideHandle = ph;
|
||||
Context = location;
|
||||
m_AssetInfo = assetInfo;
|
||||
m_LastUpdateTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
public override bool WaitForCompletion()
|
||||
{
|
||||
//TODO: this needs to just wait on the resourcemanager update loop to ensure proper loading order
|
||||
while (!IsDone)
|
||||
{
|
||||
Load(k_SynchronousBytesPerSecond, k_SynchronousBytesPerSecond, .1f);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override float PercentComplete
|
||||
{
|
||||
get { return Mathf.Clamp01(m_BytesLoaded / (float)m_AssetInfo.Size); }
|
||||
}
|
||||
|
||||
public bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
|
||||
{
|
||||
if (IsDone)
|
||||
return false;
|
||||
var now = m_LastUpdateTime + unscaledDeltaTime;
|
||||
if (now > m_LastUpdateTime)
|
||||
{
|
||||
m_BytesLoaded += (long)Math.Ceiling((now - m_LastUpdateTime) * localBandwidth);
|
||||
m_LastUpdateTime = now;
|
||||
}
|
||||
|
||||
if (m_BytesLoaded < m_AssetInfo.Size)
|
||||
return true;
|
||||
if (!(Context is IResourceLocation))
|
||||
return false;
|
||||
var location = Context as IResourceLocation;
|
||||
var assetPath = m_AssetInfo.m_AssetPath;
|
||||
object result = null;
|
||||
|
||||
var pt = m_provideHandle.Type;
|
||||
if (pt.IsArray)
|
||||
result = ResourceManagerConfig.CreateArrayResult(pt, AssetDatabaseProvider.LoadAssetsWithSubAssets(assetPath));
|
||||
else if (pt.IsGenericType && typeof(IList<>) == pt.GetGenericTypeDefinition())
|
||||
result = ResourceManagerConfig.CreateListResult(pt, AssetDatabaseProvider.LoadAssetsWithSubAssets(assetPath));
|
||||
else
|
||||
{
|
||||
if (ResourceManagerConfig.ExtractKeyAndSubKey(location.InternalId, out string mainPath, out string subKey))
|
||||
result = AssetDatabaseProvider.LoadAssetSubObject(assetPath, subKey, pt);
|
||||
else
|
||||
result = AssetDatabaseProvider.LoadAssetAtPath(assetPath, m_provideHandle);
|
||||
}
|
||||
|
||||
SetResult(result);
|
||||
InvokeCompletionEvent();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//return true until complete
|
||||
internal bool UpdateAsyncOperations(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
|
||||
{
|
||||
if (m_BundleLoadOperation == null)
|
||||
return false;
|
||||
|
||||
if (!m_BundleLoadOperation.IsDone)
|
||||
{
|
||||
m_BundleLoadOperation.Update(localBandwidth, remoteBandwidth, unscaledDeltaTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var o in m_AssetLoadOperations)
|
||||
{
|
||||
if (!o.Load(localBandwidth, remoteBandwidth, unscaledDeltaTime))
|
||||
{
|
||||
m_AssetLoadOperations.Remove(o);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m_AssetLoadOperations.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IAssetBundleResource API
|
||||
/// </summary>
|
||||
/// <returns>Always returns null.</returns>
|
||||
public AssetBundle GetAssetBundle()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d9b4067bd3135d46a8f5e265bee07cb
|
||||
timeCreated: 1498587435
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,198 @@
|
|||
#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
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulates the loading behavior of an asset bundle. Internally it uses the AssetDatabase API. This provider will only work in the editor.
|
||||
/// </summary>
|
||||
[DisplayName("Virtual AssetBundle Provider")]
|
||||
public class VirtualAssetBundleProvider : ResourceProviderBase, IUpdateReceiver
|
||||
{
|
||||
VirtualAssetBundleRuntimeData m_BundleData;
|
||||
|
||||
private VirtualAssetBundleProvider()
|
||||
{
|
||||
m_ProviderId = typeof(AssetBundleProvider).FullName;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Type GetDefaultType(IResourceLocation location)
|
||||
{
|
||||
return typeof(IAssetBundleResource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundleProvider object;
|
||||
/// <param name="data">Contains information on virtual bundle layout</param>
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initilization data is passed when when constructed from serialized data
|
||||
/// </summary>
|
||||
/// <param name="id">The provider id</param>
|
||||
/// <param name="data">The data string, this is assumed to be the virtual bundle data path</param>
|
||||
/// <returns>true if the data is as expected</returns>
|
||||
public override bool Initialize(string id, string data)
|
||||
{
|
||||
VirtualAssetBundleRuntimeData bundleData = JsonUtility.FromJson<VirtualAssetBundleRuntimeData>(data);
|
||||
return InitializeInternal(id, bundleData);
|
||||
}
|
||||
|
||||
class InternalOp
|
||||
{
|
||||
VBAsyncOperation<VirtualAssetBundle> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<string, VirtualAssetBundle> m_PendingOperations = new Dictionary<string, VirtualAssetBundle>();
|
||||
|
||||
Dictionary<string, VirtualAssetBundle> m_AllBundles = new Dictionary<string, VirtualAssetBundle>();
|
||||
Dictionary<string, VirtualAssetBundle> m_ActiveBundles = new Dictionary<string, VirtualAssetBundle>();
|
||||
|
||||
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<VirtualAssetBundle> 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<VirtualAssetBundle>().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
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0619aca0c5e2b4dc38311d8bdc157e99
|
||||
timeCreated: 1504209361
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,70 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialized data containing the asset bundle layout.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VirtualAssetBundleRuntimeData
|
||||
{
|
||||
[FormerlySerializedAs("m_simulatedAssetBundles")]
|
||||
[SerializeField]
|
||||
List<VirtualAssetBundle> m_SimulatedAssetBundles = new List<VirtualAssetBundle>();
|
||||
|
||||
[FormerlySerializedAs("m_remoteLoadSpeed")]
|
||||
[SerializeField]
|
||||
long m_RemoteLoadSpeed = 1234567890123456; //once we expose this to the user a good default value would be 1024 * 100;
|
||||
|
||||
[FormerlySerializedAs("m_localLoadSpeed")]
|
||||
[SerializeField]
|
||||
long m_LocalLoadSpeed = 1234567890123456; //once we expose this to the user a good default value would be 1024 * 1024 * 10;
|
||||
|
||||
/// <summary>
|
||||
/// The list of asset bundles to simulate.
|
||||
/// </summary>
|
||||
public List<VirtualAssetBundle> AssetBundles
|
||||
{
|
||||
get { return m_SimulatedAssetBundles; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bandwidth value (in bytes per second) to simulate loading from a remote location.
|
||||
/// </summary>
|
||||
public long RemoteLoadSpeed
|
||||
{
|
||||
get { return m_RemoteLoadSpeed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bandwidth value (in bytes per second) to simulate loading from a local location.
|
||||
/// </summary>
|
||||
public long LocalLoadSpeed
|
||||
{
|
||||
get { return m_LocalLoadSpeed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundleRuntimeData object.
|
||||
/// </summary>
|
||||
public VirtualAssetBundleRuntimeData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new VirtualAssetBundleRuntimeData object.
|
||||
/// </summary>
|
||||
/// <param name="localSpeed">Bandwidth value (in bytes per second) to simulate loading from a local location.</param>
|
||||
/// <param name="remoteSpeed">Bandwidth value (in bytes per second) to simulate loading from a remote location.</param>
|
||||
public VirtualAssetBundleRuntimeData(long localSpeed, long remoteSpeed)
|
||||
{
|
||||
m_LocalLoadSpeed = localSpeed;
|
||||
m_RemoteLoadSpeed = remoteSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9d68e463588743e48c547b980d041f7
|
||||
timeCreated: 1504807697
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,88 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom version of AssetBundleRequestOptions used to compute needed download size while bypassing the cache. In the future a virtual cache may be implemented.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VirtualAssetBundleRequestOptions : AssetBundleRequestOptions
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override long ComputeSize(IResourceLocation location, ResourceManager resourceManager)
|
||||
{
|
||||
var id = resourceManager == null ? location.InternalId : resourceManager.TransformInternalId(location);
|
||||
if (!ResourceManagerConfig.IsPathRemote(id))
|
||||
return 0;
|
||||
return BundleSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides assets from virtual asset bundles. Actual loads are done through the AssetDatabase API.
|
||||
/// </summary>
|
||||
[DisplayName("Assets from Virtual Bundles")]
|
||||
public class VirtualBundledAssetProvider : ResourceProviderBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Default copnstructor.
|
||||
/// </summary>
|
||||
public VirtualBundledAssetProvider()
|
||||
{
|
||||
m_ProviderId = typeof(BundledAssetProvider).FullName;
|
||||
}
|
||||
|
||||
class InternalOp
|
||||
{
|
||||
VBAsyncOperation<object> m_RequestOperation;
|
||||
ProvideHandle m_PI;
|
||||
|
||||
public void Start(ProvideHandle provideHandle, VirtualAssetBundle bundle)
|
||||
{
|
||||
m_PI = provideHandle;
|
||||
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
m_RequestOperation = bundle.LoadAssetAsync(m_PI, m_PI.Location);
|
||||
m_RequestOperation.Completed += RequestOperation_Completed;
|
||||
}
|
||||
|
||||
private bool WaitForCompletionHandler()
|
||||
{
|
||||
return m_RequestOperation.WaitForCompletion();
|
||||
}
|
||||
|
||||
private void RequestOperation_Completed(VBAsyncOperation<object> obj)
|
||||
{
|
||||
bool success = (obj.Result != null && m_PI.Type.IsAssignableFrom(obj.Result.GetType())) && obj.OperationException == null;
|
||||
m_PI.Complete(obj.Result, success, obj.OperationException);
|
||||
}
|
||||
|
||||
public float GetPercentComplete()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.PercentComplete : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Provide(ProvideHandle provideHandle)
|
||||
{
|
||||
List<object> deps = new List<object>(); // TODO: garbage. need to pass actual count and reuse the list
|
||||
provideHandle.GetDependencies(deps);
|
||||
VirtualAssetBundle bundle = deps[0] as VirtualAssetBundle;
|
||||
if (bundle == null)
|
||||
{
|
||||
provideHandle.Complete<object>(null, false, new Exception($"Unable to load asset of type {provideHandle.Type} from location {provideHandle.Location}."));
|
||||
}
|
||||
else
|
||||
{
|
||||
new InternalOp().Start(provideHandle, bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e4b11d3ae39f459483b122f50af55ee
|
||||
timeCreated: 1504647703
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.ResourceProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides raw text from a local or remote URL.
|
||||
/// </summary>
|
||||
[DisplayName("Text Data Provider")]
|
||||
public class TextDataProvider : ResourceProviderBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls whether errors are logged - this is disabled when trying to load from the local cache since failures are expected
|
||||
/// </summary>
|
||||
public bool IgnoreFailures { get; set; }
|
||||
|
||||
internal class InternalOp
|
||||
{
|
||||
TextDataProvider m_Provider;
|
||||
UnityWebRequestAsyncOperation m_RequestOperation;
|
||||
WebRequestQueueOperation m_RequestQueueOperation;
|
||||
ProvideHandle m_PI;
|
||||
bool m_IgnoreFailures;
|
||||
private bool m_Complete = false;
|
||||
private int m_Timeout = 0;
|
||||
|
||||
private float GetPercentComplete()
|
||||
{
|
||||
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
|
||||
}
|
||||
|
||||
public void Start(ProvideHandle provideHandle, TextDataProvider rawProvider)
|
||||
{
|
||||
m_PI = provideHandle;
|
||||
m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
provideHandle.SetProgressCallback(GetPercentComplete);
|
||||
m_Provider = rawProvider;
|
||||
|
||||
// override input options with options from Location if included
|
||||
if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
|
||||
{
|
||||
m_IgnoreFailures = providerData.IgnoreFailures;
|
||||
m_Timeout = providerData.WebRequestTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IgnoreFailures = rawProvider.IgnoreFailures;
|
||||
m_Timeout = 0;
|
||||
}
|
||||
|
||||
var path = m_PI.ResourceManager.TransformInternalId(m_PI.Location);
|
||||
if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
|
||||
{
|
||||
SendWebRequest(path);
|
||||
}
|
||||
else if (File.Exists(path))
|
||||
{
|
||||
#if NET_4_6
|
||||
if (path.Length >= 260)
|
||||
path = @"\\?\" + path;
|
||||
#endif
|
||||
var text = File.ReadAllText(path);
|
||||
object result = ConvertText(text);
|
||||
m_PI.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {m_PI.Type} from location {m_PI.Location}.") : null);
|
||||
m_Complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception exception = null;
|
||||
//Don't log errors when loading from the persistentDataPath since these files are expected to not exist until created
|
||||
if (m_IgnoreFailures)
|
||||
{
|
||||
m_PI.Complete<object>(null, true, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = new Exception(string.Format("Invalid path in " + nameof(TextDataProvider) + " : '{0}'.", path));
|
||||
m_PI.Complete<object>(null, false, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForCompletionHandler()
|
||||
{
|
||||
if (m_Complete)
|
||||
return true;
|
||||
|
||||
if (m_RequestOperation != null)
|
||||
{
|
||||
if (m_RequestOperation.isDone && !m_Complete)
|
||||
RequestOperation_completed(m_RequestOperation);
|
||||
else if (!m_RequestOperation.isDone)
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_Complete;
|
||||
}
|
||||
|
||||
private void RequestOperation_completed(AsyncOperation op)
|
||||
{
|
||||
if (m_Complete)
|
||||
return;
|
||||
|
||||
var webOp = op as UnityWebRequestAsyncOperation;
|
||||
string textResult = null;
|
||||
Exception exception = null;
|
||||
if (webOp != null)
|
||||
{
|
||||
var webReq = webOp.webRequest;
|
||||
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
|
||||
textResult = webReq.downloadHandler.text;
|
||||
else
|
||||
exception = new RemoteProviderException($"{nameof(TextDataProvider)} : unable to load from url : {webReq.url}", m_PI.Location, uwrResult);
|
||||
webReq.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = new RemoteProviderException(nameof(TextDataProvider) + " unable to load from unknown url", m_PI.Location);
|
||||
}
|
||||
|
||||
CompleteOperation(textResult, exception);
|
||||
}
|
||||
|
||||
protected void CompleteOperation(string text, Exception exception)
|
||||
{
|
||||
object result = null;
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
result = ConvertText(text);
|
||||
|
||||
m_PI.Complete(result, result != null || m_IgnoreFailures, exception);
|
||||
m_Complete = true;
|
||||
}
|
||||
|
||||
private object ConvertText(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_Provider.Convert(m_PI.Type, text);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!m_IgnoreFailures)
|
||||
Debug.LogException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SendWebRequest(string path)
|
||||
{
|
||||
path = path.Replace('\\', '/');
|
||||
UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null);
|
||||
if (m_Timeout > 0)
|
||||
request.timeout = m_Timeout;
|
||||
|
||||
m_PI.ResourceManager.WebRequestOverride?.Invoke(request);
|
||||
m_RequestQueueOperation = WebRequestQueue.QueueRequest(request);
|
||||
if (m_RequestQueueOperation.IsDone)
|
||||
{
|
||||
m_RequestOperation = m_RequestQueueOperation.Result;
|
||||
if (m_RequestOperation.isDone)
|
||||
RequestOperation_completed(m_RequestOperation);
|
||||
else
|
||||
m_RequestOperation.completed += RequestOperation_completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RequestQueueOperation.OnComplete += asyncOperation =>
|
||||
{
|
||||
m_RequestOperation = asyncOperation;
|
||||
m_RequestOperation.completed += RequestOperation_completed;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to convert the text into the object type requested. Usually the text contains a JSON formatted serialized object.
|
||||
/// </summary>
|
||||
/// <param name="type">The object type the text is converted to.</param>
|
||||
/// <param name="text">The text to be converted.</param>
|
||||
/// <returns>The converted object.</returns>
|
||||
public virtual object Convert(Type type, string text)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides raw text data from the location.
|
||||
/// </summary>
|
||||
/// <param name="provideHandle">The data needed by the provider to perform the load.</param>
|
||||
public override void Provide(ProvideHandle provideHandle)
|
||||
{
|
||||
new InternalOp().Start(provideHandle, this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 15f7962ec30713a4992a23b060cdf71c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,168 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a web request stored in the <seealso cref="WebRequestQueue"/>.
|
||||
/// </summary>
|
||||
public class WebRequestQueueOperation
|
||||
{
|
||||
private bool m_Completed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the async operation object returned from sending the web request.
|
||||
/// </summary>
|
||||
public UnityWebRequestAsyncOperation Result;
|
||||
|
||||
/// <summary>
|
||||
/// Event that is invoked when the async operation is complete.
|
||||
/// </summary>
|
||||
public Action<UnityWebRequestAsyncOperation> OnComplete;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the async operation is complete.
|
||||
/// </summary>
|
||||
public bool IsDone
|
||||
{
|
||||
get { return m_Completed || Result != null; }
|
||||
}
|
||||
|
||||
internal UnityWebRequest m_WebRequest;
|
||||
|
||||
/// <summary>
|
||||
/// The web request.
|
||||
/// </summary>
|
||||
public UnityWebRequest WebRequest
|
||||
{
|
||||
get { return m_WebRequest; }
|
||||
internal set { m_WebRequest = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of WebRequestQueueOperation.
|
||||
/// </summary>
|
||||
/// <param name="request">The web request associated with this object.</param>
|
||||
public WebRequestQueueOperation(UnityWebRequest request)
|
||||
{
|
||||
m_WebRequest = request;
|
||||
}
|
||||
|
||||
internal void Complete(UnityWebRequestAsyncOperation asyncOp)
|
||||
{
|
||||
m_Completed = true;
|
||||
Result = asyncOp;
|
||||
OnComplete?.Invoke(Result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a queue of web requests. Completed requests are removed from the queue.
|
||||
/// </summary>
|
||||
public static class WebRequestQueue
|
||||
{
|
||||
internal static int s_MaxRequest = 3;
|
||||
internal static Queue<WebRequestQueueOperation> s_QueuedOperations = new Queue<WebRequestQueueOperation>();
|
||||
internal static List<UnityWebRequestAsyncOperation> s_ActiveRequests = new List<UnityWebRequestAsyncOperation>();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the max number of web requests running at the same time.
|
||||
/// </summary>
|
||||
/// <param name="maxRequests">The max number of web requests.</param>
|
||||
public static void SetMaxConcurrentRequests(int maxRequests)
|
||||
{
|
||||
if (maxRequests < 1)
|
||||
throw new ArgumentException("MaxRequests must be 1 or greater.", "maxRequests");
|
||||
s_MaxRequest = maxRequests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a web request to the queue.
|
||||
/// </summary>
|
||||
/// <param name="request">The web request.</param>
|
||||
/// <returns>Returns an object representing the web request.</returns>
|
||||
public static WebRequestQueueOperation QueueRequest(UnityWebRequest request)
|
||||
{
|
||||
WebRequestQueueOperation queueOperation = new WebRequestQueueOperation(request);
|
||||
if (s_ActiveRequests.Count < s_MaxRequest)
|
||||
BeginWebRequest(queueOperation);
|
||||
else
|
||||
s_QueuedOperations.Enqueue(queueOperation);
|
||||
|
||||
return queueOperation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously waits for a particular web request to be completed.
|
||||
/// </summary>
|
||||
/// <param name="request">The web request.</param>
|
||||
/// <param name="millisecondsTimeout">The amount of time in milliseconds spent waiting per iteration before checking if the request is complete.</param>
|
||||
public static void WaitForRequestToBeActive(WebRequestQueueOperation request, int millisecondsTimeout)
|
||||
{
|
||||
var completedRequests = new List<UnityWebRequestAsyncOperation>();
|
||||
while (s_QueuedOperations.Contains(request))
|
||||
{
|
||||
completedRequests.Clear();
|
||||
foreach (UnityWebRequestAsyncOperation webRequestAsyncOp in s_ActiveRequests)
|
||||
{
|
||||
if (UnityWebRequestUtilities.IsAssetBundleDownloaded(webRequestAsyncOp))
|
||||
completedRequests.Add(webRequestAsyncOp);
|
||||
}
|
||||
|
||||
foreach (UnityWebRequestAsyncOperation webRequestAsyncOp in completedRequests)
|
||||
{
|
||||
bool requestIsActive = s_QueuedOperations.Peek() == request;
|
||||
webRequestAsyncOp.completed -= OnWebAsyncOpComplete;
|
||||
OnWebAsyncOpComplete(webRequestAsyncOp);
|
||||
if (requestIsActive)
|
||||
return;
|
||||
}
|
||||
|
||||
System.Threading.Thread.Sleep(millisecondsTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnWebAsyncOpComplete(AsyncOperation operation)
|
||||
{
|
||||
s_ActiveRequests.Remove(operation as UnityWebRequestAsyncOperation);
|
||||
|
||||
if (s_QueuedOperations.Count > 0)
|
||||
{
|
||||
var nextQueuedOperation = s_QueuedOperations.Dequeue();
|
||||
BeginWebRequest(nextQueuedOperation);
|
||||
}
|
||||
}
|
||||
|
||||
static void BeginWebRequest(WebRequestQueueOperation queueOperation)
|
||||
{
|
||||
var request = queueOperation.m_WebRequest;
|
||||
UnityWebRequestAsyncOperation webRequestAsyncOp = null;
|
||||
try
|
||||
{
|
||||
webRequestAsyncOp = request.SendWebRequest();
|
||||
if (webRequestAsyncOp != null)
|
||||
{
|
||||
s_ActiveRequests.Add(webRequestAsyncOp);
|
||||
|
||||
if (webRequestAsyncOp.isDone)
|
||||
OnWebAsyncOpComplete(webRequestAsyncOp);
|
||||
else
|
||||
webRequestAsyncOp.completed += OnWebAsyncOpComplete;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnWebAsyncOpComplete(null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
|
||||
queueOperation.Complete(webRequestAsyncOp);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f928cb80657c364680d0c25895df60f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue