/// <summary>Pushes queued subscribe actions to the server.</summary> public static void PushSubscriptionChanges(Action onCompletedNoErrors, Action <List <WebRequestError> > onCompletedWithErrors) { int responsesPending = (LocalUser.QueuedSubscribes.Count + LocalUser.QueuedUnsubscribes.Count); // early outs if (LocalUser.AuthenticationState == AuthenticationState.NoToken || responsesPending == 0) { if (onCompletedNoErrors != null) { onCompletedNoErrors(); } return; } // set up vars string userToken = LocalUser.OAuthToken; List <WebRequestError> errors = new List <WebRequestError>(); List <int> subscribesPushed = new List <int>(LocalUser.QueuedSubscribes.Count); List <int> unsubscribesPushed = new List <int>(LocalUser.QueuedUnsubscribes.Count); // callback Action onRequestCompleted = () => { if (responsesPending <= 0) { if (userToken == LocalUser.OAuthToken) { foreach (int modId in subscribesPushed) { LocalUser.QueuedSubscribes.Remove(modId); } foreach (int modId in unsubscribesPushed) { LocalUser.QueuedUnsubscribes.Remove(modId); } LocalUser.Save(); } if (errors.Count == 0 && onCompletedNoErrors != null) { onCompletedNoErrors(); } else if (errors.Count > 0 && onCompletedWithErrors != null) { onCompletedWithErrors(errors); } } }; // - push - foreach (int modId in LocalUser.QueuedSubscribes) { APIClient.SubscribeToMod(modId, (p) => { subscribesPushed.Add(modId); --responsesPending; onRequestCompleted(); }, (e) => { // Error for "Mod is already subscribed" if (e.webRequest.responseCode == 400) { subscribesPushed.Add(modId); } // Error for "Mod is unavailable" else if (e.webRequest.responseCode == 404) { subscribesPushed.Add(modId); } // Error for real else { errors.Add(e); } --responsesPending; onRequestCompleted(); }); } foreach (int modId in LocalUser.QueuedUnsubscribes) { APIClient.UnsubscribeFromMod(modId, () => { --responsesPending; unsubscribesPushed.Remove(modId); onRequestCompleted(); }, (e) => { // Error for "Mod is already subscribed" if (e.webRequest.responseCode == 400) { unsubscribesPushed.Remove(modId); } // Error for "Mod is unavailable" else if (e.webRequest.responseCode == 404) { unsubscribesPushed.Remove(modId); } // Error for real else { errors.Add(e); } --responsesPending; onRequestCompleted(); }); } }
/// <summary>Attempts to reauthenticate using the stored external auth ticket.</summary> public static void ReauthenticateWithStoredExternalAuthData(Action <UserProfile> onSuccess, Action <WebRequestError> onError) { ExternalAuthenticationData authData = LocalUser.ExternalAuthentication; Debug.Assert(!string.IsNullOrEmpty(authData.ticket)); Debug.Assert(authData.provider != ExternalAuthenticationProvider.None); Action <string> onSuccessWrapper = (t) => { LocalUser.OAuthToken = t; LocalUser.WasTokenRejected = false; LocalUser.Save(); if (onSuccess != null) { UserAccountManagement.UpdateUserProfile(onSuccess, onError); } }; switch (LocalUser.ExternalAuthentication.provider) { case ExternalAuthenticationProvider.Steam: { APIClient.RequestSteamAuthentication(authData.ticket, onSuccessWrapper, onError); } break; case ExternalAuthenticationProvider.GOG: { APIClient.RequestGOGAuthentication(authData.ticket, onSuccessWrapper, onError); } break; case ExternalAuthenticationProvider.ItchIO: { APIClient.RequestItchIOAuthentication(authData.ticket, onSuccessWrapper, onError); } break; case ExternalAuthenticationProvider.OculusRift: { string token = authData.ticket; string nonce = null; string userIdString = null; int userId = -1; string errorMessage = null; if (authData.additionalData == null) { errorMessage = "The user id and nonce are missing."; } else if (!authData.additionalData.TryGetValue(ExternalAuthenticationData.OculusRiftKeys.NONCE, out nonce) || string.IsNullOrEmpty(nonce)) { errorMessage = "The nonce is missing."; } else if (!authData.additionalData.TryGetValue(ExternalAuthenticationData.OculusRiftKeys.USER_ID, out userIdString) || string.IsNullOrEmpty(userIdString)) { errorMessage = "The user id is missing."; } else if (!int.TryParse(userIdString, out userId)) { errorMessage = "The user id is not parseable as an integer."; } if (errorMessage != null) { Debug.LogWarning("[mod.io] Unable to authenticate using stored Oculus Rift user data.\n" + errorMessage); if (onError != null) { var error = WebRequestError.GenerateLocal(errorMessage); onError(error); } return; } else { APIClient.RequestOculusRiftAuthentication(nonce, userId, token, onSuccessWrapper, onError); } } break; case ExternalAuthenticationProvider.XboxLive: { APIClient.RequestXboxLiveAuthentication(authData.ticket, onSuccessWrapper, onError); } break; default: { throw new System.NotImplementedException(); } } }
/// <summary>Tracks and logs a download upon it completing.</summary> public static void DebugDownload(UnityWebRequestAsyncOperation operation, LocalUser userData, string downloadLocation, int timeSent = -1) { #if DEBUG Debug.Assert(operation != null); UnityWebRequest webRequest = operation.webRequest; string userIdString = DebugUtilities.GenerateUserIdString(userData.profile); if (timeSent < 0) { timeSent = ServerTimeStamp.Now; } RequestDebugData debugData; if (DebugUtilities.webRequestDebugData.TryGetValue(webRequest, out debugData)) { var logString = new System.Text.StringBuilder(); logString.AppendLine("[mod.io] Duplicate Web Request Sent"); logString.Append("URL: "); logString.Append(webRequest.url); logString.Append(" ("); logString.Append(webRequest.method.ToUpper()); logString.AppendLine(")"); if (!string.IsNullOrEmpty(downloadLocation)) { logString.Append("Download Location: "); logString.AppendLine(downloadLocation); } if (debugData.timeSent >= 0) { logString.Append("Sent: "); logString.Append(ServerTimeStamp.ToLocalDateTime(debugData.timeSent).ToString()); logString.Append(" ["); logString.Append(debugData.timeSent.ToString()); logString.AppendLine("]"); } if (timeSent >= 0) { logString.Append("Re-sent: "); logString.Append(ServerTimeStamp.ToLocalDateTime(timeSent).ToString()); logString.Append(" ["); logString.Append(timeSent.ToString()); logString.AppendLine("]"); } Debug.Log(logString.ToString()); } else { debugData = new RequestDebugData() { userIdString = userIdString, timeSent = timeSent, downloadLocation = downloadLocation, }; if (PluginSettings.REQUEST_LOGGING.logOnSend) { var logString = new System.Text.StringBuilder(); logString.AppendLine("[mod.io] Web Request Sent"); logString.Append("URL: "); logString.Append(webRequest.url); logString.Append(" ("); logString.Append(webRequest.method.ToUpper()); logString.AppendLine(")"); if (!string.IsNullOrEmpty(debugData.downloadLocation)) { logString.Append("Download Location: "); logString.AppendLine(debugData.downloadLocation); } if (debugData.timeSent >= 0) { logString.Append("Sent: "); logString.Append(ServerTimeStamp.ToLocalDateTime(debugData.timeSent).ToString()); logString.Append(" ["); logString.Append(debugData.timeSent.ToString()); logString.AppendLine("]"); } logString.AppendLine(); string requestString = DebugUtilities.GetRequestInfo(webRequest, debugData.userIdString); logString.AppendLine("------[ Request ]------"); logString.AppendLine(requestString); Debug.Log(logString.ToString()); } if (PluginSettings.REQUEST_LOGGING.logAllResponses || PluginSettings.REQUEST_LOGGING.errorsAsWarnings) { DebugUtilities.webRequestDebugData.Add(webRequest, debugData); // handle completion if (operation.isDone) { DebugUtilities.OnOperationCompleted(operation); } else { operation.completed += DebugUtilities.OnOperationCompleted; } } } #endif // DEBUG }
/// <summary>Pulls the subscriptions from the server and stores the changes.</summary> public static void PullSubscriptionChanges(Action <List <ModProfile> > onSuccess, Action <WebRequestError> onError) { // early out if (LocalUser.AuthenticationState == AuthenticationState.NoToken) { if (onSuccess != null) { onSuccess(new List <ModProfile>(0)); } return; } // holding vars string userToken = LocalUser.OAuthToken; List <ModProfile> remoteOnlySubscriptions = new List <ModProfile>(); // set filter and initial pagination RequestFilter subscriptionFilter = new RequestFilter(); subscriptionFilter.AddFieldFilter(ModIO.API.GetUserSubscriptionsFilterFields.gameId, new EqualToFilter <int>(PluginSettings.GAME_ID)); APIPaginationParameters pagination = new APIPaginationParameters() { limit = APIPaginationParameters.LIMIT_MAX, offset = 0, }; // define actions Action getNextPage = null; Action <RequestPage <ModProfile> > onPageReceived = null; Action onAllPagesReceived = null; getNextPage = () => { APIClient.GetUserSubscriptions(subscriptionFilter, pagination, (response) => { onPageReceived(response); // check if all pages received if (response != null && response.items != null && response.items.Length > 0 && response.resultTotal > response.size + response.resultOffset) { pagination.offset = response.resultOffset + response.size; getNextPage(); } else { onAllPagesReceived(); if (onSuccess != null) { onSuccess(remoteOnlySubscriptions); } } }, (e) => { if (onError != null) { onError(e); } }); }; onPageReceived = (r) => { foreach (ModProfile profile in r.items) { if (profile != null) { remoteOnlySubscriptions.Add(profile); } } }; onAllPagesReceived = () => { if (userToken != LocalUser.OAuthToken) { return; } List <int> localOnlySubs = new List <int>(LocalUser.SubscribedModIds); // NOTE(@jackson): Unsub actions *should not* be found in activeUser.subscribedModIds foreach (int modId in LocalUser.QueuedUnsubscribes) { #if DEBUG if (localOnlySubs.Contains(modId)) { Debug.LogWarning("[mod.io] A locally subscribed mod was found in the" + " queuedUnsubscribes. This should not occur - please" + " ensure that any mod ids added to" + " activeUser.queuedUnsubscribes are removed from" + " activeUser.subscribedModIds or use" + " UserAccountManagement.UnsubscribeFromMod() to handle" + " this automatically."); } #endif localOnlySubs.Remove(modId); } List <int> newSubs = new List <int>(); // build new subs list for (int i = 0; i < remoteOnlySubscriptions.Count; ++i) { ModProfile profile = remoteOnlySubscriptions[i]; // remove if in queued subs LocalUser.QueuedSubscribes.Remove(profile.id); // if in unsub queue if (LocalUser.QueuedUnsubscribes.Contains(profile.id)) { remoteOnlySubscriptions.RemoveAt(i); --i; } // if locally subbed else if (localOnlySubs.Remove(profile.id)) { remoteOnlySubscriptions.RemoveAt(i); --i; } // if not locally subbed && if not in unsub queue else { newSubs.Add(profile.id); } } // -- update locally -- // remove new unsubs foreach (int modId in localOnlySubs) { // if not in sub queue if (!LocalUser.QueuedSubscribes.Contains(modId)) { LocalUser.SubscribedModIds.Remove(modId); } } LocalUser.SubscribedModIds.AddRange(newSubs); // save LocalUser.Save(); }; // get pages getNextPage(); }
// ---------[ Initialization ]--------- /// <summary>Sets the initial Singleton values.</summary> static LocalUser() { LocalUser._instance = new LocalUser(); LocalUser.AssertListsNotNull(ref LocalUser._instance); LocalUser.isLoaded = false; }