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 { /// /// Provides raw text from a local or remote URL. /// [DisplayName("Text Data Provider")] public class TextDataProvider : ResourceProviderBase { /// /// Controls whether errors are logged - this is disabled when trying to load from the local cache since failures are expected /// 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(null, true, exception); m_Complete = true; } else { exception = new Exception(string.Format("Invalid path in " + nameof(TextDataProvider) + " : '{0}'.", path)); m_PI.Complete(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); } 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) { 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; }; } } } /// /// Method to convert the text into the object type requested. Usually the text contains a JSON formatted serialized object. /// /// The object type the text is converted to. /// The text to be converted. /// The converted object. public virtual object Convert(Type type, string text) { return text; } /// /// Provides raw text data from the location. /// /// The data needed by the provider to perform the load. public override void Provide(ProvideHandle provideHandle) { new InternalOp().Start(provideHandle, this); } } }