UNITASK_WEBREQUEST_SUPPORT

master
neuecc 2021-02-08 19:09:02 +09:00
parent 9a3f10d4bf
commit a35e5f929d
9 changed files with 524 additions and 518 deletions

View File

@ -20,8 +20,9 @@ public static class EditorRunnerChecker
{ {
Debug.Log("Start"); Debug.Log("Start");
var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask(); //var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
Debug.Log(r.downloadHandler.text.Substring(0, 100)); //Debug.Log(r.downloadHandler.text.Substring(0, 100));
await UniTask.Yield();
Debug.Log("End"); Debug.Log("End");
} }

View File

@ -7,7 +7,7 @@ using UnityEngine.Networking;
namespace Cysharp.Threading.Tasks.Internal namespace Cysharp.Threading.Tasks.Internal
{ {
#if ENABLE_UNITYWEBREQUEST && UNITASK_WEBREQUEST_SUPPORT #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
internal static class UnityWebRequestResultExtensions internal static class UnityWebRequestResultExtensions
{ {

View File

@ -1,7 +1,7 @@
{ {
"name": "UniTask", "name": "UniTask",
"references": [ "rootNamespace": "",
], "references": [],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
@ -13,7 +13,7 @@
{ {
"name": "com.unity.modules.assetbundle", "name": "com.unity.modules.assetbundle",
"expression": "", "expression": "",
"define": "NITASK_ASSETBUNDLE_SUPPORT" "define": "UNITASK_ASSETBUNDLE_SUPPORT"
}, },
{ {
"name": "com.unity.modules.physics", "name": "com.unity.modules.physics",
@ -34,6 +34,11 @@
"name": "com.unity.ugui", "name": "com.unity.ugui",
"expression": "", "expression": "",
"define": "UNITASK_UGUI_SUPPORT" "define": "UNITASK_UGUI_SUPPORT"
},
{
"name": "com.unity.modules.unitywebrequestwww",
"expression": "",
"define": "UNITASK_WEBREQUEST_SUPPORT"
} }
], ],
"noEngineReferences": false "noEngineReferences": false

View File

@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Internal;
#if !UNITY_2019_1_OR_NEWER && (ENABLE_UNITYWEBREQUEST && UNITASK_WEBREQUEST_SUPPORT) #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using UnityEngine.Networking; using UnityEngine.Networking;
#endif #endif
@ -1221,7 +1221,7 @@ namespace Cysharp.Threading.Tasks
#endregion #endregion
#endif #endif
#if ENABLE_UNITYWEBREQUEST && UNITASK_WEBREQUEST_SUPPORT #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
#region UnityWebRequestAsyncOperation #region UnityWebRequestAsyncOperation
public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation) public static UnityWebRequestAsyncOperationAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOperation)

View File

@ -1,4 +1,4 @@
#if ENABLE_UNITYWEBREQUEST #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,471 +1,471 @@
using Cysharp.Threading.Tasks; //using Cysharp.Threading.Tasks;
using System; //using System;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Diagnostics; //using System.Diagnostics;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using System.Threading; //using System.Threading;
using System.Threading.Tasks; //using System.Threading.Tasks;
using UnityEngine; //using UnityEngine;
using UnityEngine.Networking; //using UnityEngine.Networking;
using UnityEngine.SceneManagement; //using UnityEngine.SceneManagement;
using UnityEngine.UI; //using UnityEngine.UI;
namespace Cysharp.Threading.Tasks.Sample //namespace Cysharp.Threading.Tasks.Sample
{ //{
//public class Sample2 // //public class Sample2
//{ // //{
// public Sample2() // // public Sample2()
// { // // {
// // デコレーターの詰まったClientを生成これは一度作ったらフィールドに保存可 // // // デコレーターの詰まったClientを生成これは一度作ったらフィールドに保存可
// var client = new NetworkClient("http://localhost", TimeSpan.FromSeconds(10), // // var client = new NetworkClient("http://localhost", TimeSpan.FromSeconds(10),
// new QueueRequestDecorator(), // // new QueueRequestDecorator(),
// new LoggingDecorator(), // // new LoggingDecorator(),
// new AppendTokenDecorator(), // // new AppendTokenDecorator(),
// new SetupHeaderDecorator()); // // new SetupHeaderDecorator());
// await client.PostAsync("/User/Register", new { Id = 100 }); // // await client.PostAsync("/User/Register", new { Id = 100 });
// } // // }
//} // //}
public class ReturnToTitleDecorator : IAsyncDecorator // public class ReturnToTitleDecorator : IAsyncDecorator
{ // {
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{ // {
try // try
{ // {
return await next(context, cancellationToken); // return await next(context, cancellationToken);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
if (ex is OperationCanceledException) // if (ex is OperationCanceledException)
{ // {
// キャンセルはきっと想定されている処理なのでそのまんまスルー呼び出し側でOperationCanceledExceptionとして飛んでいく) // // キャンセルはきっと想定されている処理なのでそのまんまスルー呼び出し側でOperationCanceledExceptionとして飛んでいく)
throw; // throw;
} // }
if (ex is UnityWebRequestException uwe) // if (ex is UnityWebRequestException uwe)
{ // {
// ステータスコードを使って、タイトルに戻す例外です、とかリトライさせる例外です、とかハンドリングさせると便利 // // ステータスコードを使って、タイトルに戻す例外です、とかリトライさせる例外です、とかハンドリングさせると便利
// if (uwe.ResponseCode) { }... // // if (uwe.ResponseCode) { }...
} // }
// サーバー例外のMessageを直接出すなんて乱暴なことはデバッグ時だけですよ勿論。 // // サーバー例外のMessageを直接出すなんて乱暴なことはデバッグ時だけですよ勿論。
var result = await MessageDialog.ShowAsync(ex.Message); // var result = await MessageDialog.ShowAsync(ex.Message);
// OK か Cancelかで分岐するなら。今回はボタン一個、OKのみの想定なので無視 // // OK か Cancelかで分岐するなら。今回はボタン一個、OKのみの想定なので無視
// if (result == DialogResult.Ok) { }... // // if (result == DialogResult.Ok) { }...
// シーン呼び出しはawaitしないことawaitして正常終了しちゃうと、この通信の呼び出し元に処理が戻って続行してしまいます // // シーン呼び出しはawaitしないことawaitして正常終了しちゃうと、この通信の呼び出し元に処理が戻って続行してしまいます
// のでForget。 // // のでForget。
SceneManager.LoadSceneAsync("TitleScene").ToUniTask().Forget(); // SceneManager.LoadSceneAsync("TitleScene").ToUniTask().Forget();
// そしてOperationCanceledExceptionを投げて、この通信の呼び出し元の処理はキャンセル扱いにして終了させる // // そしてOperationCanceledExceptionを投げて、この通信の呼び出し元の処理はキャンセル扱いにして終了させる
throw new OperationCanceledException(); // throw new OperationCanceledException();
} // }
} // }
} // }
public enum DialogResult // public enum DialogResult
{ // {
Ok, // Ok,
Cancel // Cancel
} // }
public static class MessageDialog // public static class MessageDialog
{ // {
public static async UniTask<DialogResult> ShowAsync(string message) // public static async UniTask<DialogResult> ShowAsync(string message)
{ // {
// (例えば)Prefabで作っておいたダイアログを生成する // // (例えば)Prefabで作っておいたダイアログを生成する
var view = await Resources.LoadAsync("Prefabs/Dialog"); // var view = await Resources.LoadAsync("Prefabs/Dialog");
// Ok, Cancelボタンのどちらかが押されるのを待機 // // Ok, Cancelボタンのどちらかが押されるのを待機
return await (view as GameObject).GetComponent<MessageDialogView>().ClickResult; // return await (view as GameObject).GetComponent<MessageDialogView>().ClickResult;
} // }
} // }
public class MessageDialogView : MonoBehaviour // public class MessageDialogView : MonoBehaviour
{ // {
[SerializeField] Button okButton = default; // [SerializeField] Button okButton = default;
[SerializeField] Button closeButton = default; // [SerializeField] Button closeButton = default;
UniTaskCompletionSource<DialogResult> taskCompletion; // UniTaskCompletionSource<DialogResult> taskCompletion;
// これでどちらかが押されるまで無限に待つを表現 // // これでどちらかが押されるまで無限に待つを表現
public UniTask<DialogResult> ClickResult => taskCompletion.Task; // public UniTask<DialogResult> ClickResult => taskCompletion.Task;
private void Start() // private void Start()
{ // {
taskCompletion = new UniTaskCompletionSource<DialogResult>(); // taskCompletion = new UniTaskCompletionSource<DialogResult>();
okButton.onClick.AddListener(() => // okButton.onClick.AddListener(() =>
{ // {
taskCompletion.TrySetResult(DialogResult.Ok); // taskCompletion.TrySetResult(DialogResult.Ok);
}); // });
closeButton.onClick.AddListener(() => // closeButton.onClick.AddListener(() =>
{ // {
taskCompletion.TrySetResult(DialogResult.Cancel); // taskCompletion.TrySetResult(DialogResult.Cancel);
}); // });
} // }
// もしボタンが押されずに消滅した場合にネンノタメ。 // // もしボタンが押されずに消滅した場合にネンノタメ。
private void OnDestroy() // private void OnDestroy()
{ // {
taskCompletion.TrySetResult(DialogResult.Cancel); // taskCompletion.TrySetResult(DialogResult.Cancel);
} // }
} // }
public class MockDecorator : IAsyncDecorator // public class MockDecorator : IAsyncDecorator
{ // {
Dictionary<string, object> mock; // Dictionary<string, object> mock;
// Pathと型を1:1にして事前定義したオブジェクトを返す辞書を渡す // // Pathと型を1:1にして事前定義したオブジェクトを返す辞書を渡す
public MockDecorator(Dictionary<string, object> mock) // public MockDecorator(Dictionary<string, object> mock)
{ // {
this.mock = mock; // this.mock = mock;
} // }
public UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // public UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{ // {
if (mock.TryGetValue(context.Path, out var value)) // if (mock.TryGetValue(context.Path, out var value))
{ // {
// 一致したものがあればそれを返す(実際の通信は行わない) // // 一致したものがあればそれを返す(実際の通信は行わない)
return new UniTask<ResponseContext>(new ResponseContext(value)); // return new UniTask<ResponseContext>(new ResponseContext(value));
} // }
else // else
{ // {
return next(context, cancellationToken); // return next(context, cancellationToken);
} // }
} // }
} // }
//public class LoggingDecorator : IAsyncDecorator // //public class LoggingDecorator : IAsyncDecorator
//{ // //{
// public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // // public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
// { // // {
// var sw = Stopwatch.StartNew(); // // var sw = Stopwatch.StartNew();
// try // // try
// { // // {
// UnityEngine.Debug.Log("Start Network Request:" + context.Path); // // UnityEngine.Debug.Log("Start Network Request:" + context.Path);
// var response = await next(context, cancellationToken); // // var response = await next(context, cancellationToken);
// UnityEngine.Debug.Log($"Complete Network Request: {context.Path} , Elapsed: {sw.Elapsed}, Size: {response.GetRawData().Length}"); // // UnityEngine.Debug.Log($"Complete Network Request: {context.Path} , Elapsed: {sw.Elapsed}, Size: {response.GetRawData().Length}");
// return response; // // return response;
// } // // }
// catch (Exception ex) // // catch (Exception ex)
// { // // {
// if (ex is OperationCanceledException) // // if (ex is OperationCanceledException)
// { // // {
// UnityEngine.Debug.Log("Request Canceled:" + context.Path); // // UnityEngine.Debug.Log("Request Canceled:" + context.Path);
// } // // }
// else if (ex is TimeoutException) // // else if (ex is TimeoutException)
// { // // {
// UnityEngine.Debug.Log("Request Timeout:" + context.Path); // // UnityEngine.Debug.Log("Request Timeout:" + context.Path);
// } // // }
// else if (ex is UnityWebRequestException webex) // // else if (ex is UnityWebRequestException webex)
// { // // {
// if (webex.IsHttpError) // // if (webex.IsHttpError)
// { // // {
// UnityEngine.Debug.Log($"Request HttpError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}"); // // UnityEngine.Debug.Log($"Request HttpError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}");
// } // // }
// else if (webex.IsNetworkError) // // else if (webex.IsNetworkError)
// { // // {
// UnityEngine.Debug.Log($"Request NetworkError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}"); // // UnityEngine.Debug.Log($"Request NetworkError: {context.Path} Code:{webex.ResponseCode} Message:{webex.Message}");
// } // // }
// } // // }
// throw; // // throw;
// } // // }
// finally // // finally
// { // // {
// /* log other */ // // /* log other */
// } // // }
// } // // }
//} // //}
public class SetupHeaderDecorator : IAsyncDecorator // public class SetupHeaderDecorator : IAsyncDecorator
{ // {
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{ // {
context.RequestHeaders["x-app-timestamp"] = context.Timestamp.ToString(); // context.RequestHeaders["x-app-timestamp"] = context.Timestamp.ToString();
context.RequestHeaders["x-user-id"] = "132141411"; // どこかから持ってくる // context.RequestHeaders["x-user-id"] = "132141411"; // どこかから持ってくる
context.RequestHeaders["x-access-token"] = "fafafawfafewaea"; // どこかから持ってくる2 // context.RequestHeaders["x-access-token"] = "fafafawfafewaea"; // どこかから持ってくる2
var respsonse = await next(context, cancellationToken); // var respsonse = await next(context, cancellationToken);
var nextToken = respsonse.ResponseHeaders["token"]; // var nextToken = respsonse.ResponseHeaders["token"];
// UserProfile.Token = nextToken; // どこかにセットするということにする // // UserProfile.Token = nextToken; // どこかにセットするということにする
return respsonse; // return respsonse;
} // }
} // }
public class AppendTokenDecorator : IAsyncDecorator // public class AppendTokenDecorator : IAsyncDecorator
{ // {
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{ // {
string token = "token"; // どっかから取ってくるということにする // string token = "token"; // どっかから取ってくるということにする
RETRY: // RETRY:
try // try
{ // {
context.RequestHeaders["x-accesss-token"] = token; // context.RequestHeaders["x-accesss-token"] = token;
return await next(context, cancellationToken); // return await next(context, cancellationToken);
} // }
catch (UnityWebRequestException ex) // catch (UnityWebRequestException ex)
{ // {
// 例えば700はTokenを再取得してください的な意味だったとする // // 例えば700はTokenを再取得してください的な意味だったとする
if (ex.ResponseCode == 700) // if (ex.ResponseCode == 700)
{ // {
// 別口でTokenを取得します的な処理 // // 別口でTokenを取得します的な処理
var newToken = await new NetworkClient(context.BasePath, context.Timeout).PostAsync<string>("/Auth/GetToken", "access_token", cancellationToken); // var newToken = await new NetworkClient(context.BasePath, context.Timeout).PostAsync<string>("/Auth/GetToken", "access_token", cancellationToken);
context.Reset(this); // context.Reset(this);
goto RETRY; // goto RETRY;
} // }
goto RETRY; // goto RETRY;
} // }
} // }
} // }
public class QueueRequestDecorator : IAsyncDecorator // public class QueueRequestDecorator : IAsyncDecorator
{ // {
readonly Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)> q = new Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)>(); // readonly Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)> q = new Queue<(UniTaskCompletionSource<ResponseContext>, RequestContext, CancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>>)>();
bool running; // bool running;
public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next) // public async UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next)
{ // {
if (q.Count == 0) // if (q.Count == 0)
{ // {
return await next(context, cancellationToken); // return await next(context, cancellationToken);
} // }
else // else
{ // {
var completionSource = new UniTaskCompletionSource<ResponseContext>(); // var completionSource = new UniTaskCompletionSource<ResponseContext>();
q.Enqueue((completionSource, context, cancellationToken, next)); // q.Enqueue((completionSource, context, cancellationToken, next));
if (!running) // if (!running)
{ // {
Run().Forget(); // Run().Forget();
} // }
return await completionSource.Task; // return await completionSource.Task;
} // }
} // }
async UniTaskVoid Run() // async UniTaskVoid Run()
{ // {
running = true; // running = true;
try // try
{ // {
while (q.Count != 0) // while (q.Count != 0)
{ // {
var (tcs, context, cancellationToken, next) = q.Dequeue(); // var (tcs, context, cancellationToken, next) = q.Dequeue();
try // try
{ // {
var response = await next(context, cancellationToken); // var response = await next(context, cancellationToken);
tcs.TrySetResult(response); // tcs.TrySetResult(response);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
tcs.TrySetException(ex); // tcs.TrySetException(ex);
} // }
} // }
} // }
finally // finally
{ // {
running = false; // running = false;
} // }
} // }
} // }
public class RequestContext // public class RequestContext
{ // {
int decoratorIndex; // int decoratorIndex;
readonly IAsyncDecorator[] decorators; // readonly IAsyncDecorator[] decorators;
Dictionary<string, string> headers; // Dictionary<string, string> headers;
public string BasePath { get; } // public string BasePath { get; }
public string Path { get; } // public string Path { get; }
public object Value { get; } // public object Value { get; }
public TimeSpan Timeout { get; } // public TimeSpan Timeout { get; }
public DateTimeOffset Timestamp { get; private set; } // public DateTimeOffset Timestamp { get; private set; }
public IDictionary<string, string> RequestHeaders // public IDictionary<string, string> RequestHeaders
{ // {
get // get
{ // {
if (headers == null) // if (headers == null)
{ // {
headers = new Dictionary<string, string>(); // headers = new Dictionary<string, string>();
} // }
return headers; // return headers;
} // }
} // }
public RequestContext(string basePath, string path, object value, TimeSpan timeout, IAsyncDecorator[] filters) // public RequestContext(string basePath, string path, object value, TimeSpan timeout, IAsyncDecorator[] filters)
{ // {
this.decoratorIndex = -1; // this.decoratorIndex = -1;
this.decorators = filters; // this.decorators = filters;
this.BasePath = basePath; // this.BasePath = basePath;
this.Path = path; // this.Path = path;
this.Value = value; // this.Value = value;
this.Timeout = timeout; // this.Timeout = timeout;
this.Timestamp = DateTimeOffset.UtcNow; // this.Timestamp = DateTimeOffset.UtcNow;
} // }
internal Dictionary<string, string> GetRawHeaders() => headers; // internal Dictionary<string, string> GetRawHeaders() => headers;
internal IAsyncDecorator GetNextDecorator() => decorators[++decoratorIndex]; // internal IAsyncDecorator GetNextDecorator() => decorators[++decoratorIndex];
public void Reset(IAsyncDecorator currentFilter) // public void Reset(IAsyncDecorator currentFilter)
{ // {
decoratorIndex = Array.IndexOf(decorators, currentFilter); // decoratorIndex = Array.IndexOf(decorators, currentFilter);
if (headers != null) // if (headers != null)
{ // {
headers.Clear(); // headers.Clear();
} // }
Timestamp = DateTimeOffset.UtcNow; // Timestamp = DateTimeOffset.UtcNow;
} // }
} // }
public class ResponseContext // public class ResponseContext
{ // {
bool hasValue; // bool hasValue;
object value; // object value;
readonly byte[] bytes; // readonly byte[] bytes;
public long StatusCode { get; } // public long StatusCode { get; }
public Dictionary<string, string> ResponseHeaders { get; } // public Dictionary<string, string> ResponseHeaders { get; }
public ResponseContext(object value, Dictionary<string, string> header = null) // public ResponseContext(object value, Dictionary<string, string> header = null)
{ // {
this.hasValue = true; // this.hasValue = true;
this.value = value; // this.value = value;
this.StatusCode = 200; // this.StatusCode = 200;
this.ResponseHeaders = (header ?? new Dictionary<string, string>()); // this.ResponseHeaders = (header ?? new Dictionary<string, string>());
} // }
public ResponseContext(byte[] bytes, long statusCode, Dictionary<string, string> responseHeaders) // public ResponseContext(byte[] bytes, long statusCode, Dictionary<string, string> responseHeaders)
{ // {
this.hasValue = false; // this.hasValue = false;
this.bytes = bytes; // this.bytes = bytes;
this.StatusCode = statusCode; // this.StatusCode = statusCode;
this.ResponseHeaders = responseHeaders; // this.ResponseHeaders = responseHeaders;
} // }
public byte[] GetRawData() => bytes; // public byte[] GetRawData() => bytes;
public T GetResponseAs<T>() // public T GetResponseAs<T>()
{ // {
if (hasValue) // if (hasValue)
{ // {
return (T)value; // return (T)value;
} // }
value = JsonUtility.FromJson<T>(Encoding.UTF8.GetString(bytes)); // value = JsonUtility.FromJson<T>(Encoding.UTF8.GetString(bytes));
hasValue = true; // hasValue = true;
return (T)value; // return (T)value;
} // }
} // }
public interface IAsyncDecorator // public interface IAsyncDecorator
{ // {
UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next); // UniTask<ResponseContext> SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next);
} // }
public class NetworkClient : IAsyncDecorator // public class NetworkClient : IAsyncDecorator
{ // {
readonly Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next; // readonly Func<RequestContext, CancellationToken, UniTask<ResponseContext>> next;
readonly IAsyncDecorator[] decorators; // readonly IAsyncDecorator[] decorators;
readonly TimeSpan timeout; // readonly TimeSpan timeout;
readonly IProgress<float> progress; // readonly IProgress<float> progress;
readonly string basePath; // readonly string basePath;
public NetworkClient(string basePath, TimeSpan timeout, params IAsyncDecorator[] decorators) // public NetworkClient(string basePath, TimeSpan timeout, params IAsyncDecorator[] decorators)
: this(basePath, timeout, null, decorators) // : this(basePath, timeout, null, decorators)
{ // {
} // }
public NetworkClient(string basePath, TimeSpan timeout, IProgress<float> progress, params IAsyncDecorator[] decorators) // public NetworkClient(string basePath, TimeSpan timeout, IProgress<float> progress, params IAsyncDecorator[] decorators)
{ // {
this.next = InvokeRecursive; // setup delegate // this.next = InvokeRecursive; // setup delegate
this.basePath = basePath; // this.basePath = basePath;
this.timeout = timeout; // this.timeout = timeout;
this.progress = progress; // this.progress = progress;
this.decorators = new IAsyncDecorator[decorators.Length + 1]; // this.decorators = new IAsyncDecorator[decorators.Length + 1];
Array.Copy(decorators, this.decorators, decorators.Length); // Array.Copy(decorators, this.decorators, decorators.Length);
this.decorators[this.decorators.Length - 1] = this; // this.decorators[this.decorators.Length - 1] = this;
} // }
public async UniTask<T> PostAsync<T>(string path, T value, CancellationToken cancellationToken = default) // public async UniTask<T> PostAsync<T>(string path, T value, CancellationToken cancellationToken = default)
{ // {
var request = new RequestContext(basePath, path, value, timeout, decorators); // var request = new RequestContext(basePath, path, value, timeout, decorators);
var response = await InvokeRecursive(request, cancellationToken); // var response = await InvokeRecursive(request, cancellationToken);
return response.GetResponseAs<T>(); // return response.GetResponseAs<T>();
} // }
UniTask<ResponseContext> InvokeRecursive(RequestContext context, CancellationToken cancellationToken) // UniTask<ResponseContext> InvokeRecursive(RequestContext context, CancellationToken cancellationToken)
{ // {
return context.GetNextDecorator().SendAsync(context, cancellationToken, next); // マジカル再帰処理 // return context.GetNextDecorator().SendAsync(context, cancellationToken, next); // マジカル再帰処理
} // }
async UniTask<ResponseContext> IAsyncDecorator.SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> _) // async UniTask<ResponseContext> IAsyncDecorator.SendAsync(RequestContext context, CancellationToken cancellationToken, Func<RequestContext, CancellationToken, UniTask<ResponseContext>> _)
{ // {
// Postしか興味ないからPostにしかしないよ // // Postしか興味ないからPostにしかしないよ
// パフォーマンスを最大限にしたい場合はuploadHandler, downloadHandlerをカスタマイズすること // // パフォーマンスを最大限にしたい場合はuploadHandler, downloadHandlerをカスタマイズすること
// JSONでbodyに送るというパラメータで送るという雑設定。 // // JSONでbodyに送るというパラメータで送るという雑設定。
var data = JsonUtility.ToJson(context.Value); // var data = JsonUtility.ToJson(context.Value);
var formData = new Dictionary<string, string> { { "body", data } }; // var formData = new Dictionary<string, string> { { "body", data } };
using (var req = UnityWebRequest.Post(basePath + context.Path, formData)) // using (var req = UnityWebRequest.Post(basePath + context.Path, formData))
{ // {
var header = context.GetRawHeaders(); // var header = context.GetRawHeaders();
if (header != null) // if (header != null)
{ // {
foreach (var item in header) // foreach (var item in header)
{ // {
req.SetRequestHeader(item.Key, item.Value); // req.SetRequestHeader(item.Key, item.Value);
} // }
} // }
// Timeout処理はCancellationTokenSourceのCancelAfterSlim(UniTask拡張)を使ってサクッと処理 // // Timeout処理はCancellationTokenSourceのCancelAfterSlim(UniTask拡張)を使ってサクッと処理
var linkToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // var linkToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
linkToken.CancelAfterSlim(timeout); // linkToken.CancelAfterSlim(timeout);
try // try
{ // {
// 完了待ちや終了処理はUniTaskの拡張自体に丸投げ // // 完了待ちや終了処理はUniTaskの拡張自体に丸投げ
await req.SendWebRequest().ToUniTask(progress: progress, cancellationToken: linkToken.Token); // await req.SendWebRequest().ToUniTask(progress: progress, cancellationToken: linkToken.Token);
} // }
catch (OperationCanceledException) // catch (OperationCanceledException)
{ // {
// 元キャンセレーションソースがキャンセルしてなければTimeoutによるものと判定 // // 元キャンセレーションソースがキャンセルしてなければTimeoutによるものと判定
if (!cancellationToken.IsCancellationRequested) // if (!cancellationToken.IsCancellationRequested)
{ // {
throw new TimeoutException(); // throw new TimeoutException();
} // }
} // }
finally // finally
{ // {
// Timeoutに引っかからなかった場合にてるのでCancelAfterSlimの裏で回ってるループをこれで終わらせとく // // Timeoutに引っかからなかった場合にてるのでCancelAfterSlimの裏で回ってるループをこれで終わらせとく
if (!linkToken.IsCancellationRequested) // if (!linkToken.IsCancellationRequested)
{ // {
linkToken.Cancel(); // linkToken.Cancel();
} // }
} // }
// UnityWebRequestを先にDisposeしちゃうので先に必要なものを取得しておく性能的には無駄なのでパフォーマンスを最大限にしたい場合は更に一工夫を // // UnityWebRequestを先にDisposeしちゃうので先に必要なものを取得しておく性能的には無駄なのでパフォーマンスを最大限にしたい場合は更に一工夫を
return new ResponseContext(req.downloadHandler.data, req.responseCode, req.GetResponseHeaders()); // return new ResponseContext(req.downloadHandler.data, req.responseCode, req.GetResponseHeaders());
} // }
} // }
} // }
} //}

View File

@ -18,7 +18,6 @@ using UnityEngine.SceneManagement;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using System.IO; using System.IO;
using System.Linq.Expressions; using System.Linq.Expressions;
using Cysharp.Threading.Tasks.Sample;
// using DG.Tweening; // using DG.Tweening;
@ -269,32 +268,33 @@ public class SandboxMain : MonoBehaviour
async Task Test1() async Task Test1()
{ {
var r = await TcsAsync("https://bing.com/"); // var r = await TcsAsync("https://bing.com/");
await Task.Yield();
Debug.Log("TASKASYNC"); Debug.Log("TASKASYNC");
} }
async UniTaskVoid Test2() //async UniTaskVoid Test2()
{ //{
try // try
{ // {
//var cts = new CancellationTokenSource(); // //var cts = new CancellationTokenSource();
//var r = UniAsync("https://bing.com/", cts.Token); // //var r = UniAsync("https://bing.com/", cts.Token);
//cts.Cancel(); // //cts.Cancel();
//await r; // //await r;
Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType); // Debug.Log("SendWebRequestDone:" + PlayerLoopInfo.CurrentLoopType);
// var foo = await UnityWebRequest.Get("https://bing.com/").SendWebRequest(); // // var foo = await UnityWebRequest.Get("https://bing.com/").SendWebRequest();
// foo.downloadHandler.text; // // foo.downloadHandler.text;
// // //
_ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().WithCancellation(CancellationToken.None); // _ = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().WithCancellation(CancellationToken.None);
Debug.Log("SendWebRequestWithCancellationDone:" + PlayerLoopInfo.CurrentLoopType); // Debug.Log("SendWebRequestWithCancellationDone:" + PlayerLoopInfo.CurrentLoopType);
} // }
catch // catch
{ // {
Debug.Log("Canceled"); // Debug.Log("Canceled");
} // }
} //}
IEnumerator Test3(string url) IEnumerator Test3(string url)
{ {
@ -303,17 +303,17 @@ public class SandboxMain : MonoBehaviour
Debug.Log("COROUTINE"); Debug.Log("COROUTINE");
} }
static async Task<UnityWebRequest> TcsAsync(string url) //static async Task<UnityWebRequest> TcsAsync(string url)
{ //{
var req = await UnityWebRequest.Get(url).SendWebRequest(); // var req = await UnityWebRequest.Get(url).SendWebRequest();
return req; // return req;
} //}
static async UniTask<UnityWebRequest> UniAsync(string url, CancellationToken cancellationToken) //static async UniTask<UnityWebRequest> UniAsync(string url, CancellationToken cancellationToken)
{ //{
var req = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cancellationToken); // var req = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cancellationToken);
return req; // return req;
} //}
async Task<int> Test() async Task<int> Test()
{ {

View File

@ -14,14 +14,14 @@ public class FooMonoBehaviour : MonoBehaviour
private async UniTask Download(UnityWebRequest req, string filePath) private async UniTask Download(UnityWebRequest req, string filePath)
{ {
var foo = req.SendWebRequest(); _ = req.SendWebRequest();
var aaa = await foo;
Debug.Log(aaa);
// var aaa = await foo;
// Debug.Log(aaa);
await UniTask.Yield();
//File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty); //File.WriteAllText(filePath, req.downloadHandler.text ?? string.Empty);
} }
} }

View File

@ -1,2 +1,2 @@
m_EditorVersion: 2020.2.1f1 m_EditorVersion: 2020.2.0f1
m_EditorVersionWithRevision: 2020.2.1f1 (270dd8c3da1c) m_EditorVersionWithRevision: 2020.2.0f1 (3721df5a8b28)