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.
256 lines
8.6 KiB
C#
256 lines
8.6 KiB
C#
1 week ago
|
#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
|