7 using System.Threading.Tasks;
11 static partial class GameAnalyticsManager
17 private const string RemoteRequestVersion =
"3";
47 public static Consent UserConsented {
get;
private set; } = Consent.Unknown;
49 public static bool SendUserStatistics => UserConsented == Consent.Yes && loadedImplementation !=
null;
51 private static bool ConsentTextAvailable
52 => TextManager.ContainsTag(
"statisticsconsentheader")
53 && TextManager.ContainsTag(
"statisticsconsenttext");
55 private const string consentServerUrl =
"https://barotraumagame.com/baromaster/";
56 private const string consentServerFile =
"consentserver.php";
65 private class AuthTicket
67 public readonly
string Token;
68 public readonly Platform Platform;
70 public AuthTicket(
string token, Platform platform)
72 Token = token ??
string.Empty;
77 private static async Task<AuthTicket> GetAuthTicket()
79 if (SteamManager.IsInitialized)
81 return await GetSteamAuthTicket();
83 else if (EosInterface.IdQueries.IsLoggedIntoEosConnect)
85 return await GetEOSAuthTicket();
87 return new AuthTicket(
string.Empty, Platform.None);
90 private static async Task<AuthTicket> GetSteamAuthTicket()
92 var authTicket = await SteamManager.GetAuthTicketForGameAnalyticsConsent();
93 return authTicket.TryUnwrap(out var ticketUnwrapped) && ticketUnwrapped.Data is { Length: > 0 }
94 ?
new AuthTicket(ToolBoxCore.ByteArrayToHexString(ticketUnwrapped.Data), Platform.Steam)
95 : throw new Exception(
"Could not retrieve Steamworks authentication ticket for GameAnalytics");
98 private static async Task<AuthTicket> GetEOSAuthTicket()
100 var puid = EosInterface.IdQueries.GetLoggedInPuids().First();
101 var tokenResult = EosInterface.EosIdToken.FromProductUserId(puid);
102 if (tokenResult.TryUnwrapFailure(out var error))
104 throw new Exception($
"Could not retrieve EOS authentication ticket for GameAnalytics. {error}");
106 else if (tokenResult.TryUnwrapSuccess(out var token))
108 return new AuthTicket(token.JsonWebToken.ToString(), Platform.EOS);
110 throw new UnreachableCodeException();
119 public static void SetConsent(Consent consent, Action? onAnswerSent =
null)
121 if (consent == Consent.Yes)
124 "Cannot call SetConsent with value Consent.Yes, must only be set to this value via consent prompt");
126 SetConsentInternal(consent, onAnswerSent);
133 private static void SetConsentInternal(Consent consent, Action? onAnswerSent)
135 if (UserConsented == consent)
137 onAnswerSent?.Invoke();
141 if (consent == Consent.Ask)
144 GameMain.ExecuteAfterContentFinishedLoading(CreateConsentPrompt);
148 if (consent != Consent.No && consent != Consent.Yes)
150 UserConsented = consent;
154 if (consent == Consent.No)
156 UserConsented = consent;
161 "GameAnalyticsConsent.SendAnswerToRemoteDatabase",
162 SendAnswerToRemoteDatabase(consent),
165 onAnswerSent?.Invoke();
166 if (!t.TryGetResult(out
bool success) || !success) { return; }
168 UserConsented = consent;
169 if (consent == Consent.Yes)
180 private static async Task<bool> SendAnswerToRemoteDatabase(Consent consent)
182 AuthTicket authTicket;
185 authTicket = await GetAuthTicket();
189 DebugConsole.ThrowError($
"Error in {nameof(GameAnalyticsManager)}.{nameof(SendAnswerToRemoteDatabase)}. Could not get an authentication ticket.", e);
192 if (authTicket.Platform == Platform.None)
194 DebugConsole.AddWarning($
"Error in {nameof(GameAnalyticsManager)}.{nameof(SendAnswerToRemoteDatabase)}. Not logged in to any platform.");
197 if (
string.IsNullOrEmpty(authTicket.Token))
199 DebugConsole.ThrowError($
"Error in {nameof(GameAnalyticsManager)}.{nameof(SendAnswerToRemoteDatabase)}. {authTicket.Platform} authentication ticket was empty.");
203 IRestResponse response;
206 var client =
new RestClient(consentServerUrl);
208 var request =
new RestRequest(consentServerFile, Method.GET);
209 request.AddParameter(
"authticket", authTicket.Token);
210 if (consent == Consent.Ask)
212 request.AddParameter(
"action",
"resetconsent");
216 request.AddParameter(
"action",
"setconsent");
217 request.AddParameter(
"consent", consent == Consent.Yes ? 1 : 0);
219 request.AddParameter(
"request_version", RemoteRequestVersion);
220 request.AddParameter(
"platform", authTicket.Platform);
222 response = await client.ExecuteAsync(request, Method.GET);
226 DebugConsole.ThrowError(
"Error while connecting to consent server", e);
230 if (!CheckResponse(response)) {
return false; }
232 if (!
string.IsNullOrWhiteSpace(response.Content))
234 DebugConsole.ThrowError($
"Error in GameAnalyticsManager.SetContent. Consent server reported an error: {response.Content.Trim()}");
240 public static void ResetConsent()
243 "GameAnalyticsConsent.ResetConsentInternal",
244 SendAnswerToRemoteDatabase(Consent.Ask),
247 if (!t.TryGetResult(out bool success) || !success) { return; }
248 DebugConsole.NewMessage(
"Reset GameAnalytics consent.");
252 static partial
void CreateConsentPrompt();
254 public static void InitIfConsented()
260 if (!ConsentTextAvailable)
262 SetConsent(Consent.Unknown);
266 if (!SteamManager.IsInitialized && EosInterface.IdQueries.GetLoggedInPuids() is not { Length: > 0 })
268 DebugConsole.AddWarning(
"Error in GameAnalyticsManager.GetConsent: Could not get a Steam or EOS authentication ticket (not connected to Steam or EOS).");
269 SetConsent(Consent.Error);
274 "GameAnalyticsConsent.RequestAnswerFromRemoteDatabase",
275 RequestAnswerFromRemoteDatabase(),
278 if (!t.TryGetResult(out Consent consent)) { return; }
279 SetConsentInternal(consent, onAnswerSent:
null);
283 private static async Task<Consent> RequestAnswerFromRemoteDatabase()
285 static void error(
string reason, Exception? exception)
287 DebugConsole.ThrowError($
"Error in {nameof(GameAnalyticsManager)}.{nameof(RequestAnswerFromRemoteDatabase)}: {reason}", exception);
288 SetConsent(Consent.Error);
291 AuthTicket authTicket;
294 authTicket = await GetAuthTicket();
298 error(
"Could not get an authentication ticket.", e);
299 return Consent.Error;
301 if (authTicket.Platform == Platform.None)
303 error($
"Could not get an authentication ticket. Not logged in to any platform.", exception:
null);
304 return Consent.Error;
310 client =
new RestClient(consentServerUrl);
314 error(
"Error while connecting to consent server.", e);
315 return Consent.Error;
318 var request =
new RestRequest(consentServerFile, Method.GET);
319 request.AddParameter(
"authticket", authTicket.Token);
320 request.AddParameter(
"action",
"getconsent");
321 request.AddParameter(
"request_version", RemoteRequestVersion);
322 request.AddParameter(
"platform", authTicket.Platform);
324 IRestResponse response;
327 response = await client.ExecuteAsync(request);
331 error(
"Error executing the request to the consent server.", e.GetInnermost());
332 return Consent.Error;
335 if (!CheckResponse(response))
337 return Consent.Error;
339 if (
string.IsNullOrEmpty(response.Content))
343 return response.Content[0] ==
'1'
348 private static bool CheckResponse(IRestResponse response)
350 if (response.ErrorException !=
null)
352 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"MasterServerErrorException",
"[error]", response.ErrorException.ToString()));
356 if (response.StatusCode == HttpStatusCode.OK) {
return true; }
358 switch (response.StatusCode)
360 case HttpStatusCode.NotFound:
361 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariable(
"MasterServerError404",
"[masterserverurl]", consentServerUrl));
363 case HttpStatusCode.ServiceUnavailable:
364 DebugConsole.ThrowErrorLocalized(TextManager.Get(
"MasterServerErrorUnavailable"));
367 DebugConsole.ThrowErrorLocalized(TextManager.GetWithVariables(
"MasterServerErrorDefault",
368 (
"[statuscode]", response.StatusCode.ToString()),
369 (
"[statusdescription]", response.StatusDescription)));