using System; using System.Collections; using UnityEngine; #if !UniRxLibrary using ObservableUnity = UniRx.Observable; #endif #if UNITY_2018_3_OR_NEWER #pragma warning disable CS0618 #endif namespace UniRx { using System.Threading; #if !(UNITY_METRO || UNITY_WP8) && (UNITY_4_4 || UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5 || UNITY_3_4 || UNITY_3_3 || UNITY_3_2 || UNITY_3_1 || UNITY_3_0_0 || UNITY_3_0 || UNITY_2_6_1 || UNITY_2_6) // Fallback for Unity versions below 4.5 using Hash = System.Collections.Hashtable; using HashEntry = System.Collections.DictionaryEntry; #else // Unity 4.5 release notes: // WWW: deprecated 'WWW(string url, byte[] postData, Hashtable headers)', // use 'public WWW(string url, byte[] postData, Dictionary headers)' instead. using Hash = System.Collections.Generic.Dictionary; using HashEntry = System.Collections.Generic.KeyValuePair; #endif #if UNITY_2018_3_OR_NEWER [Obsolete("Use UnityWebRequest, a fully featured replacement which is more efficient and has additional features")] #endif public static partial class ObservableWWW { public static IObservable Get(string url, Hash headers = null, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchText(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation)); } public static IObservable GetAndGetBytes(string url, Hash headers = null, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchBytes(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation)); } public static IObservable GetWWW(string url, Hash headers = null, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => Fetch(new WWW(url, null, (headers ?? new Hash())), observer, progress, cancellation)); } public static IObservable Post(string url, byte[] postData, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchText(new WWW(url, postData), observer, progress, cancellation)); } public static IObservable Post(string url, byte[] postData, Hash headers, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchText(new WWW(url, postData, headers), observer, progress, cancellation)); } public static IObservable Post(string url, WWWForm content, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchText(new WWW(url, content), observer, progress, cancellation)); } public static IObservable Post(string url, WWWForm content, Hash headers, IProgress progress = null) { var contentHeaders = content.headers; return ObservableUnity.FromCoroutine((observer, cancellation) => FetchText(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation)); } public static IObservable PostAndGetBytes(string url, byte[] postData, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchBytes(new WWW(url, postData), observer, progress, cancellation)); } public static IObservable PostAndGetBytes(string url, byte[] postData, Hash headers, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchBytes(new WWW(url, postData, headers), observer, progress, cancellation)); } public static IObservable PostAndGetBytes(string url, WWWForm content, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchBytes(new WWW(url, content), observer, progress, cancellation)); } public static IObservable PostAndGetBytes(string url, WWWForm content, Hash headers, IProgress progress = null) { var contentHeaders = content.headers; return ObservableUnity.FromCoroutine((observer, cancellation) => FetchBytes(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation)); } public static IObservable PostWWW(string url, byte[] postData, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => Fetch(new WWW(url, postData), observer, progress, cancellation)); } public static IObservable PostWWW(string url, byte[] postData, Hash headers, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => Fetch(new WWW(url, postData, headers), observer, progress, cancellation)); } public static IObservable PostWWW(string url, WWWForm content, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => Fetch(new WWW(url, content), observer, progress, cancellation)); } public static IObservable PostWWW(string url, WWWForm content, Hash headers, IProgress progress = null) { var contentHeaders = content.headers; return ObservableUnity.FromCoroutine((observer, cancellation) => Fetch(new WWW(url, content.data, MergeHash(contentHeaders, headers)), observer, progress, cancellation)); } public static IObservable LoadFromCacheOrDownload(string url, int version, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, version), observer, progress, cancellation)); } public static IObservable LoadFromCacheOrDownload(string url, int version, uint crc, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, version, crc), observer, progress, cancellation)); } // over Unity5 supports Hash128 #if !(UNITY_4_7 || UNITY_4_6 || UNITY_4_5 || UNITY_4_4 || UNITY_4_3 || UNITY_4_2 || UNITY_4_1 || UNITY_4_0_1 || UNITY_4_0 || UNITY_3_5 || UNITY_3_4 || UNITY_3_3 || UNITY_3_2 || UNITY_3_1 || UNITY_3_0_0 || UNITY_3_0 || UNITY_2_6_1 || UNITY_2_6) public static IObservable LoadFromCacheOrDownload(string url, Hash128 hash128, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, hash128), observer, progress, cancellation)); } public static IObservable LoadFromCacheOrDownload(string url, Hash128 hash128, uint crc, IProgress progress = null) { return ObservableUnity.FromCoroutine((observer, cancellation) => FetchAssetBundle(WWW.LoadFromCacheOrDownload(url, hash128, crc), observer, progress, cancellation)); } #endif // over 4.5, Hash define is Dictionary. // below Unity 4.5, WWW only supports Hashtable. // Unity 4.5, 4.6 WWW supports Dictionary and [Obsolete]Hashtable but WWWForm.content is Hashtable. // Unity 5.0 WWW only supports Dictionary and WWWForm.content is also Dictionary. #if !(UNITY_METRO || UNITY_WP8) && (UNITY_4_5 || UNITY_4_6 || UNITY_4_7) static Hash MergeHash(Hashtable wwwFormHeaders, Hash externalHeaders) { var newHeaders = new Hash(); foreach (DictionaryEntry item in wwwFormHeaders) { newHeaders[item.Key.ToString()] = item.Value.ToString(); } foreach (HashEntry item in externalHeaders) { newHeaders[item.Key] = item.Value; } return newHeaders; } #else static Hash MergeHash(Hash wwwFormHeaders, Hash externalHeaders) { foreach (HashEntry item in externalHeaders) { wwwFormHeaders[item.Key] = item.Value; } return wwwFormHeaders; } #endif static IEnumerator Fetch(WWW www, IObserver observer, IProgress reportProgress, CancellationToken cancel) { using (www) { if (reportProgress != null) { while (!www.isDone && !cancel.IsCancellationRequested) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } yield return null; } } else { if (!www.isDone) { yield return www; } } if (cancel.IsCancellationRequested) { yield break; } if (reportProgress != null) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } } if (!string.IsNullOrEmpty(www.error)) { observer.OnError(new WWWErrorException(www, www.text)); } else { observer.OnNext(www); observer.OnCompleted(); } } } static IEnumerator FetchText(WWW www, IObserver observer, IProgress reportProgress, CancellationToken cancel) { using (www) { if (reportProgress != null) { while (!www.isDone && !cancel.IsCancellationRequested) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } yield return null; } } else { if (!www.isDone) { yield return www; } } if (cancel.IsCancellationRequested) { yield break; } if (reportProgress != null) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } } if (!string.IsNullOrEmpty(www.error)) { observer.OnError(new WWWErrorException(www, www.text)); } else { observer.OnNext(www.text); observer.OnCompleted(); } } } static IEnumerator FetchBytes(WWW www, IObserver observer, IProgress reportProgress, CancellationToken cancel) { using (www) { if (reportProgress != null) { while (!www.isDone && !cancel.IsCancellationRequested) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } yield return null; } } else { if (!www.isDone) { yield return www; } } if (cancel.IsCancellationRequested) { yield break; } if (reportProgress != null) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } } if (!string.IsNullOrEmpty(www.error)) { observer.OnError(new WWWErrorException(www, www.text)); } else { observer.OnNext(www.bytes); observer.OnCompleted(); } } } static IEnumerator FetchAssetBundle(WWW www, IObserver observer, IProgress reportProgress, CancellationToken cancel) { using (www) { if (reportProgress != null) { while (!www.isDone && !cancel.IsCancellationRequested) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } yield return null; } } else { if (!www.isDone) { yield return www; } } if (cancel.IsCancellationRequested) { yield break; } if (reportProgress != null) { try { reportProgress.Report(www.progress); } catch (Exception ex) { observer.OnError(ex); yield break; } } if (!string.IsNullOrEmpty(www.error)) { observer.OnError(new WWWErrorException(www, "")); } else { observer.OnNext(www.assetBundle); observer.OnCompleted(); } } } } public class WWWErrorException : Exception { public string RawErrorMessage { get; private set; } public bool HasResponse { get; private set; } public string Text { get; private set; } public System.Net.HttpStatusCode StatusCode { get; private set; } public System.Collections.Generic.Dictionary ResponseHeaders { get; private set; } public WWW WWW { get; private set; } // cache the text because if www was disposed, can't access it. public WWWErrorException(WWW www, string text) { this.WWW = www; this.RawErrorMessage = www.error; this.ResponseHeaders = www.responseHeaders; this.HasResponse = false; this.Text = text; var splitted = RawErrorMessage.Split(' ', ':'); if (splitted.Length != 0) { int statusCode; if (int.TryParse(splitted[0], out statusCode)) { this.HasResponse = true; this.StatusCode = (System.Net.HttpStatusCode)statusCode; } } } public override string ToString() { var text = this.Text; if (string.IsNullOrEmpty(text)) { return RawErrorMessage; } else { return RawErrorMessage + " " + text; } } } } #if UNITY_2018_3_OR_NEWER #pragma warning restore CS0618 #endif