using Barotrauma.Steam; using RestSharp; using System; using System.Net; namespace Barotrauma { static partial class GameAnalyticsManager { public enum Consent { /// /// No attempt to contact the consent server has been made /// Unknown, /// /// An error occurred while attempting to retrieve consent status /// Error, /// /// The consent status was not saved on the remote database /// Ask, /// /// The user explicitly denied consent /// No, /// /// The user explicitly granted consent /// Yes } public static Consent UserConsented { get; private set; } = Consent.Unknown; public static bool SendUserStatistics => UserConsented == Consent.Yes && loadedImplementation != null; private static bool consentTextAvailable => TextManager.ContainsTag("statisticsconsentheader") && TextManager.ContainsTag("statisticsconsenttext"); private readonly static string consentServerUrl = "https://barotraumagame.com/baromaster/"; private readonly static string consentServerFile = "consentserver.php"; private static string GetAuthTicket() { Steamworks.AuthTicket authTicket = SteamManager.GetAuthSessionTicket(); //convert byte array to hex return BitConverter.ToString(authTicket.Data).Replace("-", ""); } /// /// Sets the consent status. This method cannot be called to /// set the status to Consent.Yes; only a positive response from /// the database or the user accepting via the privacy policy /// prompt should enable it. /// public static void SetConsent(Consent consent) { if (consent == Consent.Yes) { throw new Exception( "Cannot call SetConsent with value Consent.Yes, must only be set to this value via consent prompt"); } SetConsentInternal(consent); } /// /// Implementation of the bulk of SetConsent. /// DO NOT CALL THIS UNLESS NEEDED. /// private static void SetConsentInternal(Consent consent) { if (UserConsented == consent) { return; } if (consent == Consent.Ask) { CreateConsentPrompt(); } if (consent != Consent.No && consent != Consent.Yes) { UserConsented = consent; ShutDown(); return; } if (consent == Consent.No) { UserConsented = consent; ShutDown(); } string authTicketStr; try { authTicketStr = GetAuthTicket(); } catch (Exception e) { DebugConsole.ThrowError("Error in GameAnalyticsManager.SetConsent. Could not get a Steam authentication ticket.", e); return; } RestClient client = null; try { client = new RestClient(consentServerUrl); } catch (Exception e) { DebugConsole.ThrowError("Error while connecting to consent server", e); } if (client == null) { return; } var request = new RestRequest(consentServerFile, Method.GET); request.AddParameter("authticket", authTicketStr); request.AddParameter("action", "setconsent"); request.AddParameter("consent", consent == Consent.Yes ? 1 : 0); var response = client.Execute(request, Method.GET); if (CheckResponse(response)) { UserConsented = consent; if (consent == Consent.Yes) { Init(); } } } static partial void CreateConsentPrompt(); public static void InitIfConsented() { #if DEBUG return; #endif if (!consentTextAvailable) { SetConsent(Consent.Unknown); return; } static void error(string reason, Exception exception) { DebugConsole.ThrowError($"Error in GameAnalyticsManager.GetConsent: {reason}", exception); SetConsent(Consent.Error); } if (!SteamManager.IsInitialized) { DebugConsole.AddWarning("Error in GameAnalyticsManager.GetConsent: Could not get a Steam authentication ticket (not connected to Steam)."); SetConsent(Consent.Error); return; } string authTicketStr; try { authTicketStr = GetAuthTicket(); } catch (Exception e) { error("Could not get a Steam authentication ticket.", e); return; } RestClient client; try { client = new RestClient(consentServerUrl); } catch (Exception e) { error("Error while connecting to consent server.", e); return; } var request = new RestRequest(consentServerFile, Method.GET); request.AddParameter("authticket", authTicketStr); request.AddParameter("action", "getconsent"); TaskPool.Add($"{nameof(GameAnalyticsManager)}.{nameof(InitIfConsented)}", client.ExecuteAsync(request), (t) => { if (t.Exception != null) { error("Error executing the request to the consent server.", t.Exception.InnerException); return; } if (!t.TryGetResult(out IRestResponse response)) { return; } if (!CheckResponse(response)) { SetConsent(Consent.Error); } else if (string.IsNullOrEmpty(response.Content)) { SetConsent(Consent.Ask); } else { SetConsentInternal(response.Content[0] == '1' ? Consent.Yes : Consent.No); } }); } private static bool CheckResponse(IRestResponse response) { if (response.ErrorException != null) { DebugConsole.ThrowError(TextManager.GetWithVariable("MasterServerErrorException", "[error]", response.ErrorException.ToString())); return false; } else if (response.StatusCode != HttpStatusCode.OK) { switch (response.StatusCode) { case HttpStatusCode.NotFound: DebugConsole.ThrowError(TextManager.GetWithVariable("MasterServerError404", "[masterserverurl]", consentServerUrl)); break; case HttpStatusCode.ServiceUnavailable: DebugConsole.ThrowError(TextManager.Get("MasterServerErrorUnavailable")); break; default: DebugConsole.ThrowError(TextManager.GetWithVariables("MasterServerErrorDefault", ("[statuscode]", response.StatusCode.ToString()), ("[statusdescription]", response.StatusDescription))); break; } } return response.StatusCode == HttpStatusCode.OK; } } }