#if !DISABLE_PLAYFABENTITY_API && !DISABLE_PLAYFABCLIENT_API using System; using System.Collections.Generic; using UnityEngine; namespace PlayFab.Public { /// /// Interface which can be used to implement class responsible for gathering and sending information about session. /// public interface IScreenTimeTracker { // Unity MonoBehaviour callbacks void OnEnable(); void OnDisable(); void OnDestroy(); void OnApplicationQuit(); void OnApplicationFocus(bool isFocused); // Class specific methods void ClientSessionStart(string entityId, string entityType, string playFabUserId); void Send(); } /// /// Class responsible for gathering and sending information about session, for example: focus duration, device info, etc. /// public class ScreenTimeTracker : IScreenTimeTracker { private Guid focusId; private Guid gameSessionID = Guid.NewGuid(); private bool initialFocus = true; private bool isSending = false; private DateTime focusOffDateTime = DateTime.UtcNow; private DateTime focusOnDateTime = DateTime.UtcNow; private Queue eventsRequests = new Queue(); private EventsModels.EntityKey entityKey = new EventsModels.EntityKey(); private const string eventNamespace = "com.playfab.events.sessions"; private const int maxBatchSizeInEvents = 10; private PlayFabEventsInstanceAPI eventApi; public ScreenTimeTracker() { eventApi = new PlayFabEventsInstanceAPI(PlayFabSettings.staticPlayer); } private void EnsureSingleGameSessionId() { if (gameSessionID == Guid.Empty) { gameSessionID = Guid.NewGuid(); } } /// /// Start session, the function responsible for creating SessionID and gathering information about user and device /// /// Result of the user's login, represent user ID public void ClientSessionStart(string entityId, string entityType, string playFabUserId) { EnsureSingleGameSessionId(); entityKey.Id = entityId; entityKey.Type = entityType; EventsModels.EventContents eventInfo = new EventsModels.EventContents(); eventInfo.Name = "client_session_start"; eventInfo.EventNamespace = eventNamespace; eventInfo.Entity = entityKey; eventInfo.OriginalTimestamp = DateTime.UtcNow; var payload = new Dictionary { { "UserID", playFabUserId}, { "DeviceType", SystemInfo.deviceType}, { "DeviceModel", SystemInfo.deviceModel}, { "OS", SystemInfo.operatingSystem }, { "ClientSessionID", gameSessionID }, }; eventInfo.Payload = payload; eventsRequests.Enqueue(eventInfo); // Fake a focus-on event at the time of the first login: OnApplicationFocus(true); } /// /// Gather information about user's focus. Calculates interaction durations. /// Name mimics MonoBehaviour method, for ease of integration. /// /// State of focus public void OnApplicationFocus(bool isFocused) { EnsureSingleGameSessionId(); EventsModels.EventContents eventInfo = new EventsModels.EventContents(); DateTime currentUtcDateTime = DateTime.UtcNow; eventInfo.Name = "client_focus_change"; eventInfo.EventNamespace = eventNamespace; eventInfo.Entity = entityKey; double focusStateDuration = 0.0; if (initialFocus) { focusId = Guid.NewGuid(); } if (isFocused) { // start counting focus-on time focusOnDateTime = currentUtcDateTime; // new id per focus focusId = Guid.NewGuid(); if (!initialFocus) { focusStateDuration = (currentUtcDateTime - focusOffDateTime).TotalSeconds; // this check safeguards from manual time changes while app is running if (focusStateDuration < 0) { focusStateDuration = 0; } } } else { focusStateDuration = (currentUtcDateTime - focusOnDateTime).TotalSeconds; // this check safeguards from manual time changes while app is running if (focusStateDuration < 0) { focusStateDuration = 0; } // start counting focus-off time focusOffDateTime = currentUtcDateTime; } var payload = new Dictionary { { "FocusID", focusId }, { "FocusState", isFocused }, { "FocusStateDuration", focusStateDuration }, { "EventTimestamp", currentUtcDateTime }, { "ClientSessionID", gameSessionID }, }; eventInfo.OriginalTimestamp = currentUtcDateTime; eventInfo.Payload = payload; eventsRequests.Enqueue(eventInfo); initialFocus = false; if (!isFocused) { // Force the eventsRequests queue to empty. // If we are losing focus we should make an attempt to push out a focus lost event ASAP Send(); } } /// /// Sends events to server. /// public void Send() { if (PlayFabSettings.staticPlayer.IsClientLoggedIn() && (isSending == false)) { isSending = true; EventsModels.WriteEventsRequest request = new EventsModels.WriteEventsRequest(); request.Events = new List(); while ((eventsRequests.Count > 0) && (request.Events.Count < maxBatchSizeInEvents)) { EventsModels.EventContents eventInfo = eventsRequests.Dequeue(); request.Events.Add(eventInfo); } if (request.Events.Count > 0) { eventApi.WriteEvents(request, EventSentSuccessfulCallback, EventSentErrorCallback); } isSending = false; } } /// /// Callback to handle successful server interaction. /// /// Server response private void EventSentSuccessfulCallback(EventsModels.WriteEventsResponse response) { // add code to work with successful callback } /// /// Callback to handle unsuccessful server interaction. /// /// Server response private void EventSentErrorCallback(PlayFabError response) { Debug.LogWarning("Failed to send session data. Error: " + response.GenerateErrorReport()); } #region Unused MonoBehaviour compatibility methods /// /// Unused /// Name mimics MonoBehaviour method, for ease of integration. /// public void OnEnable() { // add code sending events on enable } /// /// Unused /// Name mimics MonoBehaviour method, for ease of integration. /// public void OnDisable() { // add code sending events on disable } /// /// Unused /// Name mimics MonoBehaviour method, for ease of integration. /// public void OnDestroy() { // add code sending events on destroy } #endregion /// /// Trying to send event during game exit. Note: works only on certain platforms. /// Name mimics MonoBehaviour method, for ease of integration. /// public void OnApplicationQuit() { // trying to send events during game exit Send(); } } } #endif