Upload files to "/"
This commit is contained in:
787
Core.cs
Normal file
787
Core.cs
Normal file
@@ -0,0 +1,787 @@
|
||||
using AmplitudeMiniJSON;
|
||||
using BestHTTP;
|
||||
using BestHTTP.Forms;
|
||||
using BestHTTP.Logger;
|
||||
using Discord;
|
||||
using ExitGames.Client.Photon.LoadBalancing;
|
||||
using RecRoom.Async;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using UnityEditor.Build.Content;
|
||||
using UnityEditor.VersionControl;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Diagnostics;
|
||||
using static UnityEngine.Networking.SyncList<T>;
|
||||
|
||||
namespace RecNet
|
||||
{
|
||||
public class Core
|
||||
{
|
||||
public delegate void ApiCallback(string error);
|
||||
|
||||
public delegate void ApiCallback<T>(string error, T result);
|
||||
|
||||
[Serializable]
|
||||
private class NameServerResponse
|
||||
{
|
||||
// This too :P
|
||||
public string RecNetStatus;
|
||||
|
||||
public string Auth;
|
||||
|
||||
public string API;
|
||||
|
||||
public string WWW;
|
||||
|
||||
public string Notifications;
|
||||
|
||||
public string Images;
|
||||
|
||||
// new adds...
|
||||
public string CDN;
|
||||
|
||||
public string Commerce;
|
||||
|
||||
public string Matchmaking;
|
||||
|
||||
public string Storage;
|
||||
|
||||
public string Chat;
|
||||
|
||||
public string Leaderboard;
|
||||
|
||||
public string Accounts;
|
||||
|
||||
public string Rooms;
|
||||
}
|
||||
|
||||
private class VersionCheckResponse : IRecNetObject
|
||||
{
|
||||
public bool ValidVersion { get; private set; }
|
||||
|
||||
public void Deserialize(Dictionary<string, object> dict)
|
||||
{
|
||||
ValidVersion = Util.GetKey<bool>("ValidVersion", dict);
|
||||
}
|
||||
}
|
||||
|
||||
private delegate HTTPRequest ConstructRequestCallback(Uri uri);
|
||||
|
||||
private delegate IEnumerator AsyncConstructRequestCallback(Uri uri, Action<HTTPRequest> callback);
|
||||
|
||||
private class QueuedApiCall
|
||||
{
|
||||
public Service Service;
|
||||
public string Uri;
|
||||
public ConstructRequestCallback ConstructRequest;
|
||||
public AsyncConstructRequestCallback AsyncConstructRequest;
|
||||
public Promise<HTTPResponse> Promise;
|
||||
|
||||
public QueuedApiCall(Service service, string uri, ConstructRequestCallback constructRequest)
|
||||
{
|
||||
Service = service;
|
||||
Uri = uri;
|
||||
ConstructRequest = constructRequest;
|
||||
AsyncConstructRequest = null;
|
||||
Promise = new Promise<HTTPResponse>();
|
||||
}
|
||||
|
||||
public QueuedApiCall(Service service, string uri, AsyncConstructRequestCallback asyncConstructRequest)
|
||||
{
|
||||
Service = service;
|
||||
Uri = uri;
|
||||
ConstructRequest = null;
|
||||
AsyncConstructRequest = asyncConstructRequest;
|
||||
Promise = new Promise<HTTPResponse>();
|
||||
}
|
||||
}
|
||||
|
||||
private const int API_RETRIES = 3;
|
||||
|
||||
private const int PARALLEL_WORKER_THREAD_COUNT = 4;
|
||||
|
||||
private const int MAX_BACKOFF_EXPONENT = 6;
|
||||
|
||||
private const float REFRESH_LOGIN_INTERVAL = 3600f;
|
||||
|
||||
public static readonly Uri RecNetOfflineUri;
|
||||
|
||||
public const bool REC_NET_OFFLINE_MODE = false;
|
||||
|
||||
public static readonly string RecNetEnv;
|
||||
|
||||
private static bool inScreenMode;
|
||||
|
||||
private static bool initialized;
|
||||
|
||||
private static Queue<QueuedApiCall> loginQueue;
|
||||
|
||||
private static Queue<QueuedApiCall> serialQueue;
|
||||
|
||||
private static Queue<QueuedApiCall> parallelQueue;
|
||||
|
||||
private static Queue<QueuedApiCall> imageQueue;
|
||||
|
||||
private static Queue<Action> eventQueue;
|
||||
|
||||
public static long LocalProfileId { get; private set; }
|
||||
|
||||
private static Dictionary<Service, Uri> ServiceUris;
|
||||
|
||||
private static HashSet<string> HostsRequiringRecNetAccessToken;
|
||||
|
||||
public static bool IsProdOrStagingEnv => string.IsNullOrWhiteSpace(RecNetEnv) || RecNetEnv.Equals("Prod", StringComparison.OrdinalIgnoreCase) || RecNetEnv.Equals("Staging", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static bool IsDevBuildOrDevEnv => !IsProdOrStagingEnv;
|
||||
|
||||
public static bool InScreenMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return inScreenMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (inScreenMode != value)
|
||||
{
|
||||
inScreenMode = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Core()
|
||||
{
|
||||
RecNetOfflineUri = new Uri("http://offline");
|
||||
inScreenMode = true;
|
||||
initialized = false;
|
||||
|
||||
ServiceUris = new Dictionary<Service, Uri>();
|
||||
HostsRequiringRecNetAccessToken = new HashSet<string>();
|
||||
|
||||
loginQueue = new Queue<QueuedApiCall>();
|
||||
serialQueue = new Queue<QueuedApiCall>();
|
||||
parallelQueue = new Queue<QueuedApiCall>();
|
||||
imageQueue = new Queue<QueuedApiCall>();
|
||||
eventQueue = new Queue<Action>();
|
||||
|
||||
HTTPManager.UseAlternateSSLDefaultValue = true;
|
||||
#if !UNITY_EDITOR
|
||||
HTTPManager.Logger.Level = Loglevels.All;
|
||||
#endif
|
||||
|
||||
string[] commandLineArgs = Environment.GetCommandLineArgs();
|
||||
if (commandLineArgs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string[] array = commandLineArgs;
|
||||
foreach (string text in array)
|
||||
{
|
||||
if (text != null && text.StartsWith("+RecNet:", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
RecNetEnv = text.Substring("+RecNet:".Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Get(Service service, string requestUri, bool executeSerially = true)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => new HTTPRequest(uri, HTTPMethods.Get), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Get(string requestUri, bool executeSerially = true)
|
||||
{
|
||||
return Get(Service.API, requestUri, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> GetFromCDN(string requestUri)
|
||||
{
|
||||
return Get(Service.CDN, requestUri, false);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> GetFromImageCDN(string requestUri)
|
||||
{
|
||||
return CallApi(Service.Images, requestUri, (Uri uri) => new HTTPRequest(uri, HTTPMethods.Get), imageQueue);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(Service service, string requestUri, Dictionary<string, string> form, bool executeSerially = true)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructSimpleFormPost(uri, form), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(Service service, string requestUri, WWWForm form, bool executeSerially = true)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructUnityFormPost(uri, form), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(Service service, string requestUri, HTTPFormBase form, bool executeSerially = true)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructUnityFormPost(uri, form), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(Service service, string requestUri, HTTPMultiPartForm multiPartForm, bool executeSerially = true)
|
||||
{
|
||||
return Post(service, requestUri, (HTTPFormBase)multiPartForm, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(Service service, string requestUri, string json, bool executeSerially = true)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructJsonFormPost(uri, json), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post<TRequest>(Service service, string requestUri, TRequest request, bool executeSerially = true) where TRequest : IRecNetRequestObject
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructRecNetObjectPost(uri, request), executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(string requestUri, Dictionary<string, string> form, bool executeSerially = true)
|
||||
{
|
||||
return Post(Service.API, requestUri, form, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(string requestUri, WWWForm form, bool executeSerially = true)
|
||||
{
|
||||
return Post(Service.API, requestUri, form, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(string requestUri, HTTPFormBase form, bool executeSerially = true)
|
||||
{
|
||||
return Post(Service.API, requestUri, form, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(string requestUri, HTTPMultiPartForm multiPartForm, bool executeSerially = true)
|
||||
{
|
||||
return Post(requestUri, (HTTPFormBase)multiPartForm, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post(string requestUri, string json, bool executeSerially = true)
|
||||
{
|
||||
return Post(Service.API, requestUri, json, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> Post<TRequest>(string requestUri, TRequest request, bool executeSerially = true) where TRequest : IRecNetRequestObject
|
||||
{
|
||||
return Post(Service.API, requestUri, request, executeSerially);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> PostUsingLoginQueue(Service service, string requestUri, string json)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructJsonFormPost(uri, json), loginQueue);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> PostUsingBackgroundImageUploadQueue(Service service, string requestUri, HTTPFormBase form)
|
||||
{
|
||||
return CallApi(service, requestUri, (Uri uri) => ConstructUnityFormPost(uri, form), imageQueue);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> PostUsingLoginQueue(string requestUri, string json)
|
||||
{
|
||||
return PostUsingLoginQueue(Service.API, requestUri, json);
|
||||
}
|
||||
|
||||
public static IPromise<HTTPResponse> PostUsingBackgroundImageUploadQueue(string requestUri, HTTPFormBase form)
|
||||
{
|
||||
return PostUsingBackgroundImageUploadQueue(Service.API, requestUri, form);
|
||||
}
|
||||
|
||||
public static IEnumerator ConnectToRecNet(ApiCallback callback)
|
||||
{
|
||||
string url = "https://ns.rec.net/?v=2";
|
||||
string log = "Connecting to RecNet";
|
||||
|
||||
if (!string.IsNullOrEmpty(RecNetEnv))
|
||||
{
|
||||
url = $"{url}&e={Uri.EscapeDataString(RecNetEnv)}";
|
||||
log = $"{log} {RecNetEnv}";
|
||||
}
|
||||
|
||||
Debug.Log(log);
|
||||
|
||||
HTTPRequest request = new HTTPRequest(new Uri(url), HTTPMethods.Get);
|
||||
yield return request.Send();
|
||||
|
||||
string error = GetError(request, true);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
request = new HTTPRequest(new Uri("http://www.google.com/generate_204"), HTTPMethods.Get);
|
||||
yield return request.Send();
|
||||
if (request.Response != null && request.Response.StatusCode == 204)
|
||||
{
|
||||
Debug.LogError($"RecNet name server query failed (error code: summer): {error}");
|
||||
SafeInvoke(callback, "Failed to connect to RecNet (error code: summer)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Network connectivity test failed: {GetError(request, true)}");
|
||||
SafeInvoke(callback, "No internet connection");
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
try
|
||||
{
|
||||
NameServerResponse nameServerResponse = JsonUtility.FromJson<NameServerResponse>(request.Response.DataAsText);
|
||||
|
||||
if (nameServerResponse.RecNetStatus != null)
|
||||
{
|
||||
Debug.LogError(nameServerResponse.RecNetStatus);
|
||||
SafeInvoke(callback, nameServerResponse.RecNetStatus);
|
||||
yield break;
|
||||
}
|
||||
|
||||
TrySetServiceUri(Service.Auth, nameServerResponse.Auth);
|
||||
TrySetServiceUri(Service.API, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.WWW, nameServerResponse.WWW);
|
||||
TrySetServiceUri(Service.Commerce, nameServerResponse.Commerce);
|
||||
TrySetServiceUri(Service.Matchmaking, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.Notifications, nameServerResponse.Notifications);
|
||||
TrySetServiceUri(Service.Images, nameServerResponse.Images);
|
||||
TrySetServiceUri(Service.CDN, nameServerResponse.CDN);
|
||||
TrySetServiceUri(Service.Storage, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.Chat, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.Leaderboard, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.Accounts, nameServerResponse.API);
|
||||
TrySetServiceUri(Service.Rooms, nameServerResponse.API);
|
||||
|
||||
Service[] servicesRequiringAuth = new[]
|
||||
{
|
||||
Service.Auth,
|
||||
Service.API,
|
||||
Service.Commerce,
|
||||
Service.Matchmaking,
|
||||
Service.Notifications,
|
||||
//Service.Images,
|
||||
//Service.CDN,
|
||||
Service.Storage,
|
||||
Service.Chat,
|
||||
Service.Leaderboard,
|
||||
Service.Accounts,
|
||||
Service.Rooms
|
||||
};
|
||||
|
||||
HostsRequiringRecNetAccessToken = new HashSet<string>();
|
||||
|
||||
foreach (Service service in servicesRequiringAuth)
|
||||
if (ServiceUris.ContainsKey(service) && ServiceUris[service] != null)
|
||||
HostsRequiringRecNetAccessToken.Add(ServiceUris[service].Host);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
SafeInvoke(callback, "Failed to connect to RecNet (error code: locker)");
|
||||
yield break;
|
||||
}
|
||||
|
||||
var requestUri = $"api/versioncheck/v3?v={Uri.EscapeDataString(BuildSettings.Version)}&p={Convert.ToInt32(PlatformManager.Instance.CurrentPlatform)}";
|
||||
|
||||
yield return Get(requestUri).ExpectResponse<VersionCheckResponse>().Error(delegate (string err)
|
||||
{
|
||||
Debug.LogError($"RecNet version check failed: {err}");
|
||||
SafeInvoke(callback, "Failed to connect to RecNet (error code: student)");
|
||||
}).Then(delegate (VersionCheckResponse response)
|
||||
{
|
||||
if (!response.ValidVersion)
|
||||
SafeInvoke(callback, "Rec Room update required");
|
||||
|
||||
else
|
||||
SafeInvoke(callback, null);
|
||||
});
|
||||
}
|
||||
|
||||
private static void LazyInit()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
HTTPUpdateDelegator.OnBeforeApplicationQuit = delegate
|
||||
{
|
||||
Notifications.Disconnect();
|
||||
Login.Logout();
|
||||
Thread.Sleep(1000);
|
||||
return true;
|
||||
};
|
||||
|
||||
Notifications.Initialize();
|
||||
GameSessions.Initialize();
|
||||
Images.Initialize();
|
||||
Messages.Initialize();
|
||||
Moderation.Initialize();
|
||||
Presence.Initialize();
|
||||
Profiles.Initialize();
|
||||
Relationships.Initialize();
|
||||
PlayerSubscriptions.Initialize();
|
||||
PlayerReporting.Initialize();
|
||||
Avatars.Initialize();
|
||||
Rooms.Initialize();
|
||||
Storefronts.Initialize();
|
||||
Consumables.Initialize();
|
||||
PlayerEvents.Initialize();
|
||||
CommunityBoard.Initialize();
|
||||
Chats.Initialize();
|
||||
Login.Initialize();
|
||||
|
||||
GameObject gameObject = new GameObject("RecNetCoreCoroutineObject");
|
||||
UnityEngine.Object.DontDestroyOnLoad(gameObject);
|
||||
gameObject.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
RecNetCore recNetCore = gameObject.AddComponent<RecNetCore>();
|
||||
recNetCore.StartCoroutine(ProcessCallApiQueue(loginQueue));
|
||||
recNetCore.StartCoroutine(ProcessCallApiQueue(serialQueue));
|
||||
recNetCore.StartCoroutine(PeriodicRefreshLogin());
|
||||
|
||||
for (int num = 0; num < PARALLEL_WORKER_THREAD_COUNT; num++)
|
||||
recNetCore.StartCoroutine(ProcessCallApiQueue(parallelQueue));
|
||||
|
||||
for (int num2 = 0; num2 < PARALLEL_WORKER_THREAD_COUNT; num2++)
|
||||
recNetCore.StartCoroutine(ProcessCallApiQueue(imageQueue));
|
||||
|
||||
recNetCore.StartCoroutine(ProcessEventQueue());
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TrySetServiceUri(Service service, string uri)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(uri))
|
||||
ServiceUris[service] = new Uri(uri);
|
||||
}
|
||||
|
||||
private static float GetExponentialBackoffTime(int retryCount)
|
||||
=> (retryCount <= 0) ? 0f : ((float)(1 << Mathf.Min(retryCount - 1, MAX_BACKOFF_EXPONENT)));
|
||||
|
||||
private static HTTPRequest ConstructSimpleFormPost(Uri uri, Dictionary<string, string> form)
|
||||
{
|
||||
var request = new HTTPRequest(uri, HTTPMethods.Post);
|
||||
foreach (KeyValuePair<string, string> item in form)
|
||||
request.AddField(item.Key, item.Value);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static HTTPRequest ConstructUnityFormPost(Uri uri, WWWForm form)
|
||||
{
|
||||
HTTPRequest hTTPRequest = new HTTPRequest(uri, HTTPMethods.Post);
|
||||
hTTPRequest.SetFields(form);
|
||||
return hTTPRequest;
|
||||
}
|
||||
|
||||
private static HTTPRequest ConstructUnityFormPost(Uri uri, HTTPFormBase form)
|
||||
{
|
||||
HTTPRequest hTTPRequest = new HTTPRequest(uri, HTTPMethods.Post);
|
||||
hTTPRequest.SetForm(form);
|
||||
return hTTPRequest;
|
||||
}
|
||||
|
||||
private static HTTPRequest ConstructJsonFormPost(Uri uri, string json)
|
||||
{
|
||||
HTTPRequest hTTPRequest = new HTTPRequest(uri, HTTPMethods.Post);
|
||||
hTTPRequest.SetHeader("Content-Type", "application/json");
|
||||
hTTPRequest.RawData = Encoding.UTF8.GetBytes(json);
|
||||
return hTTPRequest;
|
||||
}
|
||||
|
||||
private static HTTPRequest ConstructRecNetObjectPost(Uri uri, IRecNetRequestObject request)
|
||||
{
|
||||
Dictionary<string, object> obj = request.Serialize();
|
||||
string json = Json.Serialize(obj);
|
||||
return ConstructJsonFormPost(uri, json);
|
||||
}
|
||||
|
||||
private static void AddRecNetAuthToken(HTTPRequest request)
|
||||
{
|
||||
if (RequiresRecNetAccessToken(request.CurrentUri))
|
||||
{
|
||||
if (Login.AccessToken != null)
|
||||
request.SetHeader("Authorization", $"Bearer {Login.AccessToken}");
|
||||
|
||||
request.OnBeforeRedirection += ClearRecNetAuthToken;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ClearRecNetAuthToken(HTTPRequest request, HTTPResponse response, Uri redirectUri)
|
||||
{
|
||||
if (!RequiresRecNetAccessToken(redirectUri))
|
||||
{
|
||||
request.RemoveHeader("Authorization");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Uri GetServiceUri(Service service)
|
||||
{
|
||||
ServiceUris.TryGetValue(service, out var uri);
|
||||
return uri;
|
||||
}
|
||||
|
||||
private static bool RequiresRecNetAccessToken(Uri uri) => HostsRequiringRecNetAccessToken.Contains(uri.Host);
|
||||
|
||||
private static IEnumerator PeriodicRefreshLogin()
|
||||
{
|
||||
WaitForSeconds wait = new WaitForSeconds(REFRESH_LOGIN_INTERVAL);
|
||||
while (true)
|
||||
{
|
||||
yield return wait;
|
||||
if (Login.AccessToken != null)
|
||||
{
|
||||
yield return Login.RefreshLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLocalProfileId(long profileId)
|
||||
{
|
||||
LocalProfileId = profileId;
|
||||
}
|
||||
|
||||
private static IPromise<HTTPResponse> CallApi(Service service, string uri, ConstructRequestCallback constructRequest, bool executeSerially)
|
||||
{
|
||||
Queue<QueuedApiCall> queue = executeSerially ? serialQueue : parallelQueue;
|
||||
return CallApi(service, uri, constructRequest, queue);
|
||||
}
|
||||
|
||||
private static IPromise<HTTPResponse> CallApi(Service service, string uri, ConstructRequestCallback constructRequest, Queue<QueuedApiCall> queue)
|
||||
{
|
||||
LazyInit();
|
||||
QueuedApiCall queuedApiCall = new QueuedApiCall(service, uri, constructRequest);
|
||||
queue.Enqueue(queuedApiCall);
|
||||
return queuedApiCall.Promise;
|
||||
}
|
||||
|
||||
private static IPromise<HTTPResponse> CallApi(Service service, string uri, AsyncConstructRequestCallback asyncConstructRequest, Queue<QueuedApiCall> queue)
|
||||
{
|
||||
LazyInit();
|
||||
QueuedApiCall queuedApiCall = new QueuedApiCall(service, uri, asyncConstructRequest);
|
||||
queue.Enqueue(queuedApiCall);
|
||||
return queuedApiCall.Promise;
|
||||
}
|
||||
|
||||
private static IEnumerator ProcessCallApiQueue(Queue<QueuedApiCall> queue)
|
||||
{
|
||||
int consecutiveRetries = 0;
|
||||
float previousErrorTime = 0f;
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitUntil(() => queue.Count > 0);
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QueuedApiCall apiCall = queue.Dequeue();
|
||||
|
||||
Uri baseUri = GetServiceUri(apiCall.Service);
|
||||
if (baseUri == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Uri uri = new Uri(baseUri, apiCall.Uri);
|
||||
|
||||
HTTPRequest request = null;
|
||||
|
||||
for (int retry = 0; retry < API_RETRIES; retry++)
|
||||
{
|
||||
if (request != null)
|
||||
{
|
||||
request.Dispose();
|
||||
request = null;
|
||||
}
|
||||
|
||||
if (consecutiveRetries > 0)
|
||||
{
|
||||
float exponentialBackoff = GetExponentialBackoffTime(consecutiveRetries);
|
||||
float timeToWait = exponentialBackoff - (Time.realtimeSinceStartup - previousErrorTime);
|
||||
if (timeToWait > 0f)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(timeToWait);
|
||||
}
|
||||
}
|
||||
|
||||
if (apiCall.ConstructRequest != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
request = apiCall.ConstructRequest(uri);
|
||||
if (queue == imageQueue)
|
||||
{
|
||||
request.Priority = -1;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return apiCall.AsyncConstructRequest(uri, delegate (HTTPRequest result)
|
||||
{
|
||||
request = result;
|
||||
});
|
||||
}
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
AddRecNetAuthToken(request);
|
||||
yield return request.Send();
|
||||
|
||||
if (request.Response == null || request.Response.StatusCode == 429 ||
|
||||
(request.Response.StatusCode >= 500 && request.Response.StatusCode < 600))
|
||||
{
|
||||
consecutiveRetries++;
|
||||
previousErrorTime = Time.realtimeSinceStartup;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (request.Response != null && request.Response.StatusCode == 401)
|
||||
{
|
||||
consecutiveRetries++;
|
||||
previousErrorTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
else
|
||||
{
|
||||
consecutiveRetries = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string error = GetError(request, false);
|
||||
if (error == null)
|
||||
{
|
||||
apiCall.Promise.Complete(request.Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
apiCall.Promise.Error(error);
|
||||
}
|
||||
}
|
||||
catch (Exception exception2)
|
||||
{
|
||||
Debug.LogException(exception2);
|
||||
}
|
||||
|
||||
request?.Dispose();
|
||||
request = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetError(HTTPRequest request, bool handleHttpStatusCodes)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return "Failed to construct Web request";
|
||||
}
|
||||
switch (request.State)
|
||||
{
|
||||
case HTTPRequestStates.Aborted:
|
||||
return "Aborted";
|
||||
case HTTPRequestStates.ConnectionTimedOut:
|
||||
return "Connection timed out";
|
||||
case HTTPRequestStates.TimedOut:
|
||||
return "Response timed out";
|
||||
case HTTPRequestStates.Error:
|
||||
Debug.LogException(request.Exception);
|
||||
return (request.Exception == null) ? "Unspecified error" : request.Exception.Message;
|
||||
case HTTPRequestStates.Finished:
|
||||
return (!handleHttpStatusCodes || request.Response.StatusCode < 400) ? null : ("HTTP Error " + request.Response.StatusCode);
|
||||
default:
|
||||
Debug.LogError("HTTPRequest is in an invalid state: " + request.State);
|
||||
return "Unexpected state";
|
||||
}
|
||||
}
|
||||
|
||||
public static void SafeInvoke(ApiCallback callback, string error)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SafeInvoke<T>(ApiCallback<T> callback, string error, T response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
callback(error, response);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DispatchOnUnityThread(Action callback)
|
||||
{
|
||||
lock (eventQueue)
|
||||
{
|
||||
eventQueue.Enqueue(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator RunOnBackgroundThread(Action callback)
|
||||
{
|
||||
bool complete = false;
|
||||
Thread thread = new Thread((ThreadStart)delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Exception ex2 = ex;
|
||||
Exception ex3 = ex2;
|
||||
DispatchOnUnityThread(delegate
|
||||
{
|
||||
Debug.LogException(ex3);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
complete = true;
|
||||
}
|
||||
});
|
||||
thread.Start();
|
||||
return new WaitUntil(() => complete);
|
||||
}
|
||||
|
||||
private static IEnumerator ProcessEventQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitUntil(() => eventQueue.Count > 0);
|
||||
Action callback;
|
||||
lock (eventQueue)
|
||||
{
|
||||
callback = eventQueue.Dequeue();
|
||||
}
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user