薯塔、bugly、Firebase、In App Purchasing
关闭 HybridClr 关闭热更
webgl的addressable的bundle是url来的 都用下载的方式,修改BeginOperation
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using UnityEngine.AddressableAssets;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
#region ================> extent from addressable WebRequestQueue.cs
internal class WebRequestQueueOperation
{
public UnityWebRequestAsyncOperation Result;
public Action<UnityWebRequestAsyncOperation> OnComplete;
public bool IsDone
{
get { return Result != null; }
}
internal UnityWebRequest m_WebRequest;
public WebRequestQueueOperation(UnityWebRequest request)
{
m_WebRequest = request;
}
internal void Complete(UnityWebRequestAsyncOperation asyncOp)
{
Result = asyncOp;
OnComplete?.Invoke(Result);
}
}
internal static class WebRequestQueue
{
internal static int s_MaxRequest = 500;
internal static Queue<WebRequestQueueOperation> s_QueuedOperations = new Queue<WebRequestQueueOperation>();
internal static List<UnityWebRequestAsyncOperation> s_ActiveRequests = new List<UnityWebRequestAsyncOperation>();
public static void SetMaxConcurrentRequests(int maxRequests)
{
if (maxRequests < 1)
throw new ArgumentException("MaxRequests must be 1 or greater.", "maxRequests");
s_MaxRequest = maxRequests;
}
public static WebRequestQueueOperation QueueRequest(UnityWebRequest request)
{
WebRequestQueueOperation queueOperation = new WebRequestQueueOperation(request);
if (s_ActiveRequests.Count < s_MaxRequest)
{
var webRequestAsyncOp = request.SendWebRequest();
s_ActiveRequests.Add(webRequestAsyncOp);
if (webRequestAsyncOp.isDone)
OnWebAsyncOpComplete(webRequestAsyncOp);
else
webRequestAsyncOp.completed += OnWebAsyncOpComplete;
queueOperation.Complete(webRequestAsyncOp);
}
else
s_QueuedOperations.Enqueue(queueOperation);
return queueOperation;
}
private static void OnWebAsyncOpComplete(AsyncOperation operation)
{
s_ActiveRequests.Remove((operation as UnityWebRequestAsyncOperation));
if (s_QueuedOperations.Count > 0)
{
var nextQueuedOperation = s_QueuedOperations.Dequeue();
var webRequestAsyncOp = nextQueuedOperation.m_WebRequest.SendWebRequest();
webRequestAsyncOp.completed += OnWebAsyncOpComplete;
s_ActiveRequests.Add(webRequestAsyncOp);
nextQueuedOperation.Complete(webRequestAsyncOp);
}
}
}
#endregion
/// <summary>
/// Contains cache information to be used by the AssetBundleProvider
/// </summary>
[Serializable]
public class MyAssetBundleRequestOptions : 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>
/// Sets UnityWebRequest to attempt to abort after the number of seconds in timeout have passed.
/// </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]
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;
}
}
class CustomAssetBundleResource : IAssetBundleResource
{
private static string KEY = AESTool.GenAssetBundleSecretKey();
AssetBundle m_AssetBundle;
DownloadHandler m_downloadHandler;
AsyncOperation m_RequestOperation;
WebRequestQueueOperation m_WebRequestQueueOperation;
internal ProvideHandle m_ProvideHandle;
internal AssetBundleRequestOptions m_Options;
int m_Retries;
long m_BytesToDownload;
long m_DownloadedBytes;
bool m_Completed = false;
internal UnityWebRequest CreateWebRequest(IResourceLocation loc)
{
var url = m_ProvideHandle.ResourceManager.TransformInternalId(loc);
return CreateWebRequest(url);
}
internal UnityWebRequest CreateWebRequest(string url)
{
UnityWebRequest webRequest = null;
webRequest = new UnityWebRequest(url);
DownloadHandlerBuffer dH = new DownloadHandlerBuffer();
webRequest.downloadHandler = dH;
if (webRequest == null)
return webRequest;
if (m_Options != null)
{
if (m_Options.Timeout > 0)
webRequest.timeout = m_Options.Timeout;
if (m_Options.RedirectLimit > 0)
webRequest.redirectLimit = m_Options.RedirectLimit;
#if !UNITY_2019_3_OR_NEWER
webRequest.chunkedTransfer = m_Options.ChunkedTransfer;
#endif
}
if (m_ProvideHandle.ResourceManager.CertificateHandlerInstance != null)
{
webRequest.certificateHandler = m_ProvideHandle.ResourceManager.CertificateHandlerInstance;
webRequest.disposeCertificateHandlerOnDispose = false;
}
return webRequest;
}
float PercentComplete() { return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f; }
DownloadStatus GetDownloadStatus()
{
if (m_Options == null)
return default;
var status = new DownloadStatus() { TotalBytes = m_BytesToDownload, IsDone = PercentComplete() >= 1f };
if (m_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()
{
if (m_AssetBundle == null)
{
if (m_downloadHandler != null)
{
m_AssetBundle = AssetBundle.LoadFromMemory(m_downloadHandler.data);
m_downloadHandler.Dispose();
m_downloadHandler = null;
}
else if (m_RequestOperation is AssetBundleCreateRequest)
{
m_AssetBundle = (m_RequestOperation as AssetBundleCreateRequest).assetBundle;
}
}
return m_AssetBundle;
}
internal void Start(ProvideHandle provideHandle)
{
m_Retries = 0;
m_AssetBundle = null;
m_downloadHandler = null;
m_RequestOperation = null;
m_ProvideHandle = provideHandle;
m_Options = m_ProvideHandle.Location.Data as AssetBundleRequestOptions;
if (m_Options != null)
m_BytesToDownload = m_Options.ComputeSize(m_ProvideHandle.Location, m_ProvideHandle.ResourceManager);
m_ProvideHandle.SetProgressCallback(PercentComplete);
m_ProvideHandle.SetDownloadProgressCallbacks(GetDownloadStatus);
m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
BeginOperation();
}
private bool WaitForCompletionHandler()
{
if (m_RequestOperation == null)
return false;
//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 AssetBundleCreateRequest))
while (!m_RequestOperation.isDone) { }
var assetBundle = GetAssetBundle();
if (!m_Completed && assetBundle != null)
{
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;
}
private void BeginOperation()
{
string path = m_ProvideHandle.Location.InternalId;
var url = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
string bundleName = Path.GetFileName(url);
// if a path starts with jar:file, it is an android embeded resource. The resource is a local file but cannot be accessed by
// FileStream(called in LoadWithDataProc) directly
// Need to use webrequest's async call to get the content.
if (Application.platform == RuntimePlatform.WeixinMiniGamePlayer || Application.platform == RuntimePlatform.WebGLPlayer)
{
string streamPath = UnityEngine.AddressableAssets.Addressables.RuntimePath + "/" + PlatformMappingService.GetPlatformPathSubFolder() + "/" + bundleName;
Debug.Log("LoadZero:" + streamPath);
var req = CreateWebRequest(streamPath);
req.disposeDownloadHandlerOnDispose = false;
m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
if (m_WebRequestQueueOperation.IsDone)
{
m_RequestOperation = m_WebRequestQueueOperation.Result;
m_RequestOperation.completed += WebRequestOperationCompleted;
}
else
{
m_WebRequestQueueOperation.OnComplete += asyncOp =>
{
m_RequestOperation = asyncOp;
m_RequestOperation.completed += WebRequestOperationCompleted;
};
}
}
else if (BundleMgr.Instance.CheckIsBuildInBundle(bundleName))//本地资源
{
string streamPath = UnityEngine.AddressableAssets.Addressables.RuntimePath + "/" + PlatformMappingService.GetPlatformPathSubFolder() + "/" + bundleName;
//#if UNITY_IPHONE && !UNITY_EDITOR
// streamPath ="file://" + streamPath;
//#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
// streamPath = "file://" + streamPath;
//#endif
// Debug.Log("LoadOne:" + streamPath);
// var crc = m_Options == null ? 0 : m_Options.Crc;
// var req = CreateWebRequest(streamPath);
// req.disposeDownloadHandlerOnDispose = false;
// m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
// if (m_WebRequestQueueOperation.IsDone)
// {
// m_RequestOperation = m_WebRequestQueueOperation.Result;
// m_RequestOperation.completed += StreamWebRequestOperationCompleted;
// }
// else
// {
// m_WebRequestQueueOperation.OnComplete += asyncOp =>
// {
// m_RequestOperation = asyncOp;
// m_RequestOperation.completed += StreamWebRequestOperationCompleted;
// };
// }
Debug.Log("LoadOne:" + streamPath);
CompleteBundleLoad(AssetBundle.LoadFromFile(streamPath));
}
else if (BundleMgr.Instance.IsBundleCache(bundleName))
{
string cachePath = Path.Combine(BundleMgr.Instance.GetBundleCachePath(), bundleName);
Debug.Log("LoadTwo:" + cachePath);
var crc = m_Options == null ? 0 : m_Options.Crc;
//AssetBundle.LoadFromMemoryAsync(AESTool.DecryptBytes(File.ReadAllBytes(cachePath), AESTool.GenAssetBundleSecretKey()));
//m_RequestOperation.completed += LocalRequestOperationCompleted;
CompleteBundleLoad(AssetBundle.LoadFromFile(cachePath));
}
else if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
{
Debug.Log("DownloadThree:" + url);
var req = CreateWebRequest(m_ProvideHandle.Location);
req.disposeDownloadHandlerOnDispose = false;
m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
if (m_WebRequestQueueOperation.IsDone)
{
m_RequestOperation = m_WebRequestQueueOperation.Result;
m_RequestOperation.completed += WebRequestOperationCompleted;
}
else
{
m_WebRequestQueueOperation.OnComplete += asyncOp =>
{
m_RequestOperation = asyncOp;
m_RequestOperation.completed += WebRequestOperationCompleted;
};
}
}
else
{
m_RequestOperation = null;
m_ProvideHandle.Complete<CustomAssetBundleResource>(null, false, new Exception(string.Format("Invalid path in AssetBundleProvider: '{0}'.", path)));
}
}
private void LocalRequestOperationCompleted(AsyncOperation op)
{
CompleteBundleLoad((op as AssetBundleCreateRequest).assetBundle);
}
private void CompleteBundleLoad(AssetBundle bundle)
{
m_AssetBundle = bundle;
m_ProvideHandle.Complete(this, m_AssetBundle != null, null);
m_Completed = true;
}
private void StreamWebRequestOperationCompleted(AsyncOperation op)
{
UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
var webReq = remoteReq.webRequest;
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
{
m_downloadHandler = webReq.downloadHandler;
//CompleteBundleLoad(AssetBundle.LoadFromMemory(AESTool.DecryptBytes(m_downloadHandler.data, KEY)));
//CompleteBundleLoad(AssetBundle.LoadFromMemory(m_downloadHandler.data));
if (!m_Completed)
{
m_ProvideHandle.Complete(this, true, null);
m_Completed = true;
}
}
webReq.Dispose();
}
private void WebRequestOperationCompleted(AsyncOperation op)
{
UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
var webReq = remoteReq.webRequest;
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
{
m_downloadHandler = webReq.downloadHandler;
string path = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
string bundleName = Path.GetFileName(path);
BundleMgr.Instance.CacheBundle(bundleName, m_downloadHandler.data);
if (!m_Completed)
{
m_ProvideHandle.Complete(this, true, null);
m_Completed = true;
}
}
else
{
m_downloadHandler = webReq.downloadHandler;
m_downloadHandler.Dispose();
m_downloadHandler = null;
string message = string.Format("Web request {0} failed with error '{1}', retrying ({2}/{3})...", webReq.url, webReq.error, m_Retries, m_Options.RetryCount);
if (m_Retries < m_Options.RetryCount)
{
Debug.LogFormat(message);
BeginOperation();
m_Retries++;
}
else
{
var exception = new Exception(string.Format(
"RemoteAssetBundleProvider unable to load from url {0}, result='{1}'.", webReq.url,
webReq.error));
m_ProvideHandle.Complete<CustomAssetBundleResource>(null, false, exception);
}
}
webReq.Dispose();
}
/// <summary>
/// Unloads all resources associated with this asset bundle.
/// </summary>
public void Unload()
{
if (m_AssetBundle != null)
{
m_AssetBundle.Unload(true);
m_AssetBundle = null;
}
if (m_downloadHandler != null)
{
m_downloadHandler.Dispose();
m_downloadHandler = null;
}
m_RequestOperation = null;
}
}
/// <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("My AssetBundle Provider")]
public class CustomAssetBundleProvider : ResourceProviderBase
{
/// <inheritdoc/>
public override void Provide(ProvideHandle providerInterface)
{
new CustomAssetBundleResource().Start(providerInterface);
}
/// <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 CustomAssetBundleResource;
if (bundle != null)
{
bundle.Unload();
return;
}
}
}
}