You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/PlayFabSDK/Entity/ScreenTimeTracker.cs

256 lines
8.6 KiB
C#

#if !DISABLE_PLAYFABENTITY_API && !DISABLE_PLAYFABCLIENT_API
using System;
using System.Collections.Generic;
using UnityEngine;
namespace PlayFab.Public
{
/// <summary>
/// Interface which can be used to implement class responsible for gathering and sending information about session.
/// </summary>
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();
}
/// <summary>
/// Class responsible for gathering and sending information about session, for example: focus duration, device info, etc.
/// </summary>
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<EventsModels.EventContents> eventsRequests = new Queue<EventsModels.EventContents>();
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();
}
}
/// <summary>
/// Start session, the function responsible for creating SessionID and gathering information about user and device
/// </summary>
/// <param name="playFabUserId">Result of the user's login, represent user ID</param>
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<string, object>
{
{ "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);
}
/// <summary>
/// Gather information about user's focus. Calculates interaction durations.
/// Name mimics MonoBehaviour method, for ease of integration.
/// </summary>
/// <param name="isFocused">State of focus</param>
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<string, object> {
{ "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();
}
}
/// <summary>
/// Sends events to server.
/// </summary>
public void Send()
{
if (PlayFabSettings.staticPlayer.IsClientLoggedIn() && (isSending == false))
{
isSending = true;
EventsModels.WriteEventsRequest request = new EventsModels.WriteEventsRequest();
request.Events = new List<EventsModels.EventContents>();
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;
}
}
/// <summary>
/// Callback to handle successful server interaction.
/// </summary>
/// <param name="response">Server response</param>
private void EventSentSuccessfulCallback(EventsModels.WriteEventsResponse response)
{
// add code to work with successful callback
}
/// <summary>
/// Callback to handle unsuccessful server interaction.
/// </summary>
/// <param name="response">Server response</param>
private void EventSentErrorCallback(PlayFabError response)
{
Debug.LogWarning("Failed to send session data. Error: " + response.GenerateErrorReport());
}
#region Unused MonoBehaviour compatibility methods
/// <summary>
/// Unused
/// Name mimics MonoBehaviour method, for ease of integration.
/// </summary>
public void OnEnable()
{
// add code sending events on enable
}
/// <summary>
/// Unused
/// Name mimics MonoBehaviour method, for ease of integration.
/// </summary>
public void OnDisable()
{
// add code sending events on disable
}
/// <summary>
/// Unused
/// Name mimics MonoBehaviour method, for ease of integration.
/// </summary>
public void OnDestroy()
{
// add code sending events on destroy
}
#endregion
/// <summary>
/// Trying to send event during game exit. Note: works only on certain platforms.
/// Name mimics MonoBehaviour method, for ease of integration.
/// </summary>
public void OnApplicationQuit()
{
// trying to send events during game exit
Send();
}
}
}
#endif