Did a few things
This commit is contained in:
44
Assets/UserReporting/Scripts/Client/AttachmentExtensions.cs
Normal file
44
Assets/UserReporting/Scripts/Client/AttachmentExtensions.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extensions for working with attachments.
|
||||
/// </summary>
|
||||
public static class AttachmentExtensions
|
||||
{
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds a JSON attachment.
|
||||
/// </summary>
|
||||
/// <param name="instance">The extended instance.</param>
|
||||
/// <param name="name">The name of the attachment.</param>
|
||||
/// <param name="fileName">The file name.</param>
|
||||
/// <param name="contents">The contents.</param>
|
||||
public static void AddJson(this List<UserReportAttachment> instance, string name, string fileName, string contents)
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
instance.Add(new UserReportAttachment(name, fileName, "application/json", System.Text.Encoding.UTF8.GetBytes(contents)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a text attachment.
|
||||
/// </summary>
|
||||
/// <param name="instance">The extended instance.</param>
|
||||
/// <param name="name">The name of the attachment.</param>
|
||||
/// <param name="fileName">The file name.</param>
|
||||
/// <param name="contents">The contents.</param>
|
||||
public static void AddText(this List<UserReportAttachment> instance, string name, string fileName, string contents)
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
instance.Add(new UserReportAttachment(name, fileName, "text/plain", System.Text.Encoding.UTF8.GetBytes(contents)));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4af71f750a87f404d96c2fe0ef956ad8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
346
Assets/UserReporting/Scripts/Client/CyclicalList.cs
Normal file
346
Assets/UserReporting/Scripts/Client/CyclicalList.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cyclical list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items in the list.</typeparam>
|
||||
public class CyclicalList<T> : IList<T>
|
||||
{
|
||||
#region Nested Types
|
||||
|
||||
/// <summary>
|
||||
/// Represents an enumerator for a cyclical list.
|
||||
/// </summary>
|
||||
private struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Enumerator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="list">The list.</param>
|
||||
public Enumerator(CyclicalList<T> list)
|
||||
{
|
||||
this.list = list;
|
||||
this.currentIndex = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
private CyclicalList<T> list;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current item.
|
||||
/// </summary>
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.currentIndex < 0 || this.currentIndex >= this.list.Count)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return this.list[this.currentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current item.
|
||||
/// </summary>
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return this.Current; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the enumerator.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the next item.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether the move was successful.</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
this.currentIndex++;
|
||||
return this.currentIndex < this.list.count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumerator.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="CyclicalList{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The capacity.</param>
|
||||
public CyclicalList(int capacity)
|
||||
{
|
||||
this.items = new T[capacity];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private int count;
|
||||
|
||||
private T[] items;
|
||||
|
||||
private int nextPointer;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the capacity.
|
||||
/// </summary>
|
||||
public int Capacity
|
||||
{
|
||||
get { return this.items.Length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return this.count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the cyclical list is read only.
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexes into the cyclical list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>The item at the specified index.</returns>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= this.count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
return this.items[this.GetPointer(index)];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (index < 0 || index >= this.count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
this.items[this.GetPointer(index)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the cyclical list.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
this.items[this.nextPointer] = item;
|
||||
this.count++;
|
||||
if (this.count > this.items.Length)
|
||||
{
|
||||
this.count = this.items.Length;
|
||||
}
|
||||
this.nextPointer++;
|
||||
if (this.nextPointer >= this.items.Length)
|
||||
{
|
||||
this.nextPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cyclical list.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
this.count = 0;
|
||||
this.nextPointer = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the cyclical list contains the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>A value indicating whether the cyclical list contains the specified item.</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
foreach (T listItem in this)
|
||||
{
|
||||
if (listItem.Equals(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the cyclical list to an array.
|
||||
/// </summary>
|
||||
/// <param name="array">The array.</param>
|
||||
/// <param name="arrayIndex">The array index.</param>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (T listItem in this)
|
||||
{
|
||||
int currentArrayIndex = arrayIndex + i;
|
||||
if (currentArrayIndex >= array.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
array[currentArrayIndex] = listItem;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enumerator.
|
||||
/// </summary>
|
||||
/// <returns>The enumerator.</returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enumerator.
|
||||
/// </summary>
|
||||
/// <returns>The enumerator.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next eviction.
|
||||
/// </summary>
|
||||
/// <returns>The next eviction.</returns>
|
||||
public T GetNextEviction()
|
||||
{
|
||||
return this.items[this.nextPointer];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>The pointer.</returns>
|
||||
private int GetPointer(int index)
|
||||
{
|
||||
if (index < 0 || index >= this.count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
if (this.count < this.items.Length)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
return (this.nextPointer + index) % this.count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The index of the specified item.</returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (T listItem in this)
|
||||
{
|
||||
if (listItem.Equals(item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item into the cyclical list. This is a no-op on a cyclical list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
if (index < 0 || index >= this.count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the cyclical list.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>A value indicating whether the removal was successful. This is a no-op on a cyclical list.</returns>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the cyclical list at the specified index. This is a no-op on a cyclical list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= this.count)
|
||||
{
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/CyclicalList.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/CyclicalList.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ea7b024ee3157f4682b1e25563f0801
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud.UserReporting.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user reporting platform.
|
||||
/// </summary>
|
||||
public interface IUserReportingPlatform
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Deserialized the specified JSON.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <param name="json">The JSON.</param>
|
||||
/// <returns>The deserialized object instance.</returns>
|
||||
T DeserializeJson<T>(string json);
|
||||
|
||||
/// <summary>
|
||||
/// Gets device metadata.
|
||||
/// </summary>
|
||||
/// <returns>Device metadata.</returns>
|
||||
IDictionary<string, string> GetDeviceMetadata();
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a user report.
|
||||
/// </summary>
|
||||
/// <param name="userReport">The user report.</param>
|
||||
void ModifyUserReport(UserReport userReport);
|
||||
|
||||
/// <summary>
|
||||
/// Called at the end of a frame.
|
||||
/// </summary>
|
||||
/// <param name="client">The client.</param>
|
||||
void OnEndOfFrame(UserReportingClient client);
|
||||
|
||||
/// <summary>
|
||||
/// Posts to an endpoint.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="progressCallback">The progress callback. Provides the upload and download progress.</param>
|
||||
/// <param name="callback">The callback. Provides a value indicating whether the post was successful and provides the resulting byte array.</param>
|
||||
void Post(string endpoint, string contentType, byte[] content, Action<float, float> progressCallback, Action<bool, byte[]> callback);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a task asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="task">The task.</param>
|
||||
/// <param name="callback">The callback.</param>
|
||||
void RunTask(Func<object> task, Action<object> callback);
|
||||
|
||||
/// <summary>
|
||||
/// Sends an analytics event.
|
||||
/// </summary>
|
||||
/// <param name="eventName">The event name.</param>
|
||||
/// <param name="eventData">The event data.</param>
|
||||
void SendAnalyticsEvent(string eventName, Dictionary<string, object> eventData);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified object instance.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object instance.</param>
|
||||
/// <returns>The JSON.</returns>
|
||||
string SerializeJson(object instance);
|
||||
|
||||
/// <summary>
|
||||
/// Takes a screenshot.
|
||||
/// </summary>
|
||||
/// <param name="frameNumber">The frame number.</param>
|
||||
/// <param name="maximumWidth">The maximum width.</param>
|
||||
/// <param name="maximumHeight">The maximum height.</param>
|
||||
/// <param name="source">The source. Passing null will capture the screen. Passing a camera will capture the camera's view. Passing a render texture will capture the render texture.</param>
|
||||
/// <param name="callback">The callback. Provides the screenshot.</param>
|
||||
void TakeScreenshot(int frameNumber, int maximumWidth, int maximumHeight, object source, Action<int, byte[]> callback);
|
||||
|
||||
/// <summary>
|
||||
/// Called on update.
|
||||
/// </summary>
|
||||
/// <param name="client">The client.</param>
|
||||
void Update(UserReportingClient client);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e4889c60fb7bfe47a47c238c63badde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
13
Assets/UserReporting/Scripts/Client/LicenseLevel.cs
Normal file
13
Assets/UserReporting/Scripts/Client/LicenseLevel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Unity.Cloud.Authorization
|
||||
{
|
||||
public enum LicenseLevel
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Personal = 10,
|
||||
|
||||
Plus = 20,
|
||||
|
||||
Pro = 30,
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/LicenseLevel.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/LicenseLevel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61d18ba6de6f7724caf99f1af5eec743
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
28
Assets/UserReporting/Scripts/Client/MetricsGatheringMode.cs
Normal file
28
Assets/UserReporting/Scripts/Client/MetricsGatheringMode.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Cloud.UserReporting.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a metrics gathering mode.
|
||||
/// </summary>
|
||||
public enum MetricsGatheringMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Automatic. Some metrics are gathered automatically.
|
||||
/// </summary>
|
||||
Automatic = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Manual. No metrics are gathered automatically.
|
||||
/// </summary>
|
||||
Manual = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Disabled. No metrics are gathered. Sampling a metric is a no-op.
|
||||
/// </summary>
|
||||
Disabled = 2
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b64ba1730b76fd459ba483b0b108bd5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud.UserReporting.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a null user reporting platform.
|
||||
/// </summary>
|
||||
public class NullUserReportingPlatform : IUserReportingPlatform
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public T DeserializeJson<T>(string json)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public IDictionary<string, string> GetDeviceMetadata()
|
||||
{
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void ModifyUserReport(UserReport userReport)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void OnEndOfFrame(UserReportingClient client)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void Post(string endpoint, string contentType, byte[] content, Action<float, float> progressCallback, Action<bool, byte[]> callback)
|
||||
{
|
||||
progressCallback(1, 1);
|
||||
callback(true, content);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void RunTask(Func<object> task, Action<object> callback)
|
||||
{
|
||||
callback(task());
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void SendAnalyticsEvent(string eventName, Dictionary<string, object> eventData)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public string SerializeJson(object instance)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void TakeScreenshot(int frameNumber, int maximumWidth, int maximumHeight, object source, Action<int, byte[]> callback)
|
||||
{
|
||||
callback(frameNumber, new byte[0]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUserReportingPlatform"/>
|
||||
public void Update(UserReportingClient client)
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53a1dcd097bd55a45a676eced2947d02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
80
Assets/UserReporting/Scripts/Client/PngHelper.cs
Normal file
80
Assets/UserReporting/Scripts/Client/PngHelper.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides static methods for helping with PNG images.
|
||||
/// </summary>
|
||||
public static class PngHelper
|
||||
{
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a PNG image's height from base 64 encoded data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>The height.</returns>
|
||||
public static int GetPngHeightFromBase64Data(string data)
|
||||
{
|
||||
// Preconditions
|
||||
if (data == null || data.Length < 32)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implementation
|
||||
byte[] bytes = Convert.FromBase64String(data.Substring(0, 32));
|
||||
byte[] heightBytes = PngHelper.Slice(bytes, 20, 24);
|
||||
Array.Reverse(heightBytes);
|
||||
int height = BitConverter.ToInt32(heightBytes, 0);
|
||||
return height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a PNG image's width from base 64 encoded data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>The width.</returns>
|
||||
public static int GetPngWidthFromBase64Data(string data)
|
||||
{
|
||||
// Preconditions
|
||||
if (data == null || data.Length < 32)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implementation
|
||||
byte[] bytes = Convert.FromBase64String(data.Substring(0, 32));
|
||||
byte[] widthBytes = PngHelper.Slice(bytes, 16, 20);
|
||||
Array.Reverse(widthBytes);
|
||||
int width = BitConverter.ToInt32(widthBytes, 0);
|
||||
return width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slices a byte array.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns>The slices byte array.</returns>
|
||||
private static byte[] Slice(byte[] source, int start, int end)
|
||||
{
|
||||
if (end < 0)
|
||||
{
|
||||
end = source.Length + end;
|
||||
}
|
||||
|
||||
int len = end - start;
|
||||
byte[] res = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
res[i] = source[i + start];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/PngHelper.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/PngHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c70eccc2beb89c54a93499fd68044b27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
47
Assets/UserReporting/Scripts/Client/Preconditions.cs
Normal file
47
Assets/UserReporting/Scripts/Client/Preconditions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides static methods for helping with preconditions.
|
||||
/// </summary>
|
||||
public static class Preconditions
|
||||
{
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that an argument is less than or equal to the specified length.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="argumentName">The argument name.</param>
|
||||
public static void ArgumentIsLessThanOrEqualToLength(object value, int length, string argumentName)
|
||||
{
|
||||
string stringValue = value as string;
|
||||
if (stringValue != null && stringValue.Length > length)
|
||||
{
|
||||
throw new ArgumentException(argumentName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that an argument is not null or whitespace.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="argumentName">The argument name.</param>
|
||||
public static void ArgumentNotNullOrWhitespace(object value, string argumentName)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(argumentName);
|
||||
}
|
||||
string stringValue = value as string;
|
||||
if (stringValue != null && stringValue.Trim() == string.Empty)
|
||||
{
|
||||
throw new ArgumentNullException(argumentName);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/Preconditions.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/Preconditions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf95f7e6c9cf4c74388b0ff0888257c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
113
Assets/UserReporting/Scripts/Client/SerializableException.cs
Normal file
113
Assets/UserReporting/Scripts/Client/SerializableException.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Unity.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a serializable exception.
|
||||
/// </summary>
|
||||
public class SerializableException
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SerializableException"/> class.
|
||||
/// </summary>
|
||||
public SerializableException()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SerializableException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
public SerializableException(Exception exception)
|
||||
{
|
||||
// Message
|
||||
this.Message = exception.Message;
|
||||
|
||||
// Full Text
|
||||
this.FullText = exception.ToString();
|
||||
|
||||
// Type
|
||||
Type exceptionType = exception.GetType();
|
||||
this.Type = exceptionType.FullName;
|
||||
|
||||
// Stack Trace
|
||||
this.StackTrace = new List<SerializableStackFrame>();
|
||||
StackTrace stackTrace = new StackTrace(exception, true);
|
||||
foreach (StackFrame stackFrame in stackTrace.GetFrames())
|
||||
{
|
||||
this.StackTrace.Add(new SerializableStackFrame(stackFrame));
|
||||
}
|
||||
|
||||
// Problem Identifier
|
||||
if (this.StackTrace.Count > 0)
|
||||
{
|
||||
SerializableStackFrame stackFrame = this.StackTrace[0];
|
||||
this.ProblemIdentifier = string.Format("{0} at {1}.{2}", this.Type, stackFrame.DeclaringType, stackFrame.MethodName);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ProblemIdentifier = this.Type;
|
||||
}
|
||||
|
||||
// Detailed Problem Identifier
|
||||
if (this.StackTrace.Count > 1)
|
||||
{
|
||||
SerializableStackFrame stackFrame1 = this.StackTrace[0];
|
||||
SerializableStackFrame stackFrame2 = this.StackTrace[1];
|
||||
this.DetailedProblemIdentifier = string.Format("{0} at {1}.{2} from {3}.{4}", this.Type, stackFrame1.DeclaringType, stackFrame1.MethodName, stackFrame2.DeclaringType, stackFrame2.MethodName);
|
||||
}
|
||||
|
||||
// Inner Exception
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
this.InnerException = new SerializableException(exception.InnerException);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the detailed problem identifier.
|
||||
/// </summary>
|
||||
public string DetailedProblemIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full text.
|
||||
/// </summary>
|
||||
public string FullText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the inner exception.
|
||||
/// </summary>
|
||||
public SerializableException InnerException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the problem identifier.
|
||||
/// </summary>
|
||||
public string ProblemIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stack trace.
|
||||
/// </summary>
|
||||
public List<SerializableStackFrame> StackTrace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1036b9a96f07b7648bf8e729d177e7a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Unity.Cloud
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a serializable stack frame.
|
||||
/// </summary>
|
||||
public class SerializableStackFrame
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SerializableStackFrame"/> class.
|
||||
/// </summary>
|
||||
public SerializableStackFrame()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SerializableStackFrame"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stackFrame">The stack frame.</param>
|
||||
public SerializableStackFrame(StackFrame stackFrame)
|
||||
{
|
||||
MethodBase method = stackFrame.GetMethod();
|
||||
Type declaringType = method.DeclaringType;
|
||||
this.DeclaringType = declaringType != null ? declaringType.FullName : null;
|
||||
this.Method = method.ToString();
|
||||
this.MethodName = method.Name;
|
||||
this.FileName = stackFrame.GetFileName();
|
||||
this.FileLine = stackFrame.GetFileLineNumber();
|
||||
this.FileColumn = stackFrame.GetFileColumnNumber();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the declaring type.
|
||||
/// </summary>
|
||||
public string DeclaringType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file column.
|
||||
/// </summary>
|
||||
public int FileColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file line.
|
||||
/// </summary>
|
||||
public int FileLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file name.
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method.
|
||||
/// </summary>
|
||||
public string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method name.
|
||||
/// </summary>
|
||||
public string MethodName { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd25aacdb05e8244888888bc4b834af0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
247
Assets/UserReporting/Scripts/Client/UserReport.cs
Normal file
247
Assets/UserReporting/Scripts/Client/UserReport.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report.
|
||||
/// </summary>
|
||||
public class UserReport : UserReportPreview
|
||||
{
|
||||
#region Nested Types
|
||||
|
||||
/// <summary>
|
||||
/// Provides sorting for metrics.
|
||||
/// </summary>
|
||||
private class UserReportMetricSorter : IComparer<UserReportMetric>
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Compare(UserReportMetric x, UserReportMetric y)
|
||||
{
|
||||
return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReport"/> class.
|
||||
/// </summary>
|
||||
public UserReport()
|
||||
{
|
||||
this.AggregateMetrics = new List<UserReportMetric>();
|
||||
this.Attachments = new List<UserReportAttachment>();
|
||||
this.ClientMetrics = new List<UserReportMetric>();
|
||||
this.DeviceMetadata = new List<UserReportNamedValue>();
|
||||
this.Events = new List<UserReportEvent>();
|
||||
this.Fields = new List<UserReportNamedValue>();
|
||||
this.Measures = new List<UserReportMeasure>();
|
||||
this.Screenshots = new List<UserReportScreenshot>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attachments.
|
||||
/// </summary>
|
||||
public List<UserReportAttachment> Attachments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client metrics.
|
||||
/// </summary>
|
||||
public List<UserReportMetric> ClientMetrics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device metadata.
|
||||
/// </summary>
|
||||
public List<UserReportNamedValue> DeviceMetadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the events.
|
||||
/// </summary>
|
||||
public List<UserReportEvent> Events { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fields.
|
||||
/// </summary>
|
||||
public List<UserReportNamedValue> Fields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the measures.
|
||||
/// </summary>
|
||||
public List<UserReportMeasure> Measures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the screenshots.
|
||||
/// </summary>
|
||||
public List<UserReportScreenshot> Screenshots { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clones the user report.
|
||||
/// </summary>
|
||||
/// <returns>The cloned user report.</returns>
|
||||
public UserReport Clone()
|
||||
{
|
||||
UserReport userReport = new UserReport();
|
||||
userReport.AggregateMetrics = this.AggregateMetrics != null ? this.AggregateMetrics.ToList() : null;
|
||||
userReport.Attachments = this.Attachments != null ? this.Attachments.ToList() : null;
|
||||
userReport.ClientMetrics = this.ClientMetrics != null ? this.ClientMetrics.ToList() : null;
|
||||
userReport.ContentLength = this.ContentLength;
|
||||
userReport.DeviceMetadata = this.DeviceMetadata != null ? this.DeviceMetadata.ToList() : null;
|
||||
userReport.Dimensions = this.Dimensions.ToList();
|
||||
userReport.Events = this.Events != null ? this.Events.ToList() : null;
|
||||
userReport.ExpiresOn = this.ExpiresOn;
|
||||
userReport.Fields = this.Fields != null ? this.Fields.ToList() : null;
|
||||
userReport.Identifier = this.Identifier;
|
||||
userReport.IPAddress = this.IPAddress;
|
||||
userReport.Measures = this.Measures != null ? this.Measures.ToList() : null;
|
||||
userReport.ProjectIdentifier = this.ProjectIdentifier;
|
||||
userReport.ReceivedOn = this.ReceivedOn;
|
||||
userReport.Screenshots = this.Screenshots != null ? this.Screenshots.ToList() : null;
|
||||
userReport.Summary = this.Summary;
|
||||
userReport.Thumbnail = this.Thumbnail;
|
||||
return userReport;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes the user report. This is called by the client and only needs to be called when constructing a user report manually.
|
||||
/// </summary>
|
||||
public void Complete()
|
||||
{
|
||||
// Thumbnail
|
||||
if (this.Screenshots.Count > 0)
|
||||
{
|
||||
this.Thumbnail = this.Screenshots[this.Screenshots.Count - 1];
|
||||
}
|
||||
|
||||
// Aggregate Metrics
|
||||
Dictionary<string, UserReportMetric> aggregateMetrics = new Dictionary<string, UserReportMetric>();
|
||||
foreach (UserReportMeasure measure in this.Measures)
|
||||
{
|
||||
foreach (UserReportMetric metric in measure.Metrics)
|
||||
{
|
||||
if (!aggregateMetrics.ContainsKey(metric.Name))
|
||||
{
|
||||
UserReportMetric userReportMetric = new UserReportMetric();
|
||||
userReportMetric.Name = metric.Name;
|
||||
aggregateMetrics.Add(metric.Name, userReportMetric);
|
||||
}
|
||||
UserReportMetric aggregateMetric = aggregateMetrics[metric.Name];
|
||||
aggregateMetric.Sample(metric.Average);
|
||||
aggregateMetrics[metric.Name] = aggregateMetric;
|
||||
}
|
||||
}
|
||||
if (this.AggregateMetrics == null)
|
||||
{
|
||||
this.AggregateMetrics = new List<UserReportMetric>();
|
||||
}
|
||||
foreach (KeyValuePair<string, UserReportMetric> kvp in aggregateMetrics)
|
||||
{
|
||||
this.AggregateMetrics.Add(kvp.Value);
|
||||
}
|
||||
this.AggregateMetrics.Sort(new UserReportMetricSorter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the user report by replace null lists with empty lists.
|
||||
/// </summary>
|
||||
public void Fix()
|
||||
{
|
||||
this.AggregateMetrics = this.AggregateMetrics ?? new List<UserReportMetric>();
|
||||
this.Attachments = this.Attachments ?? new List<UserReportAttachment>();
|
||||
this.ClientMetrics = this.ClientMetrics ?? new List<UserReportMetric>();
|
||||
this.DeviceMetadata = this.DeviceMetadata ?? new List<UserReportNamedValue>();
|
||||
this.Dimensions = this.Dimensions ?? new List<UserReportNamedValue>();
|
||||
this.Events = this.Events ?? new List<UserReportEvent>();
|
||||
this.Fields = this.Fields ?? new List<UserReportNamedValue>();
|
||||
this.Measures = this.Measures ?? new List<UserReportMeasure>();
|
||||
this.Screenshots = this.Screenshots ?? new List<UserReportScreenshot>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dimension string for the dimensions associated with this user report.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetDimensionsString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < this.Dimensions.Count; i++)
|
||||
{
|
||||
UserReportNamedValue dimension = this.Dimensions[i];
|
||||
stringBuilder.Append(dimension.Name);
|
||||
stringBuilder.Append(": ");
|
||||
stringBuilder.Append(dimension.Value);
|
||||
if (i != this.Dimensions.Count - 1)
|
||||
{
|
||||
stringBuilder.Append(", ");
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes screenshots above a certain size from the user report.
|
||||
/// </summary>
|
||||
/// <param name="maximumWidth">The maximum width.</param>
|
||||
/// <param name="maximumHeight">The maximum height.</param>
|
||||
/// <param name="totalBytes">The total bytes allowed by screenshots.</param>
|
||||
/// <param name="ignoreCount">The number of screenshots to ignoreCount.</param>
|
||||
public void RemoveScreenshots(int maximumWidth, int maximumHeight, int totalBytes, int ignoreCount)
|
||||
{
|
||||
int byteCount = 0;
|
||||
for (int i = this.Screenshots.Count; i > 0; i--)
|
||||
{
|
||||
if (i < ignoreCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UserReportScreenshot screenshot = this.Screenshots[i];
|
||||
byteCount += screenshot.DataBase64.Length;
|
||||
if (byteCount > totalBytes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (screenshot.Width > maximumWidth || screenshot.Height > maximumHeight)
|
||||
{
|
||||
this.Screenshots.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Casts the user report to a user report preview.
|
||||
/// </summary>
|
||||
/// <returns>The user report preview.</returns>
|
||||
public UserReportPreview ToPreview()
|
||||
{
|
||||
UserReportPreview userReportPreview = new UserReportPreview();
|
||||
userReportPreview.AggregateMetrics = this.AggregateMetrics != null ? this.AggregateMetrics.ToList() : null;
|
||||
userReportPreview.ContentLength = this.ContentLength;
|
||||
userReportPreview.Dimensions = this.Dimensions != null ? this.Dimensions.ToList() : null;
|
||||
userReportPreview.ExpiresOn = this.ExpiresOn;
|
||||
userReportPreview.Identifier = this.Identifier;
|
||||
userReportPreview.IPAddress = this.IPAddress;
|
||||
userReportPreview.ProjectIdentifier = this.ProjectIdentifier;
|
||||
userReportPreview.ReceivedOn = this.ReceivedOn;
|
||||
userReportPreview.Summary = this.Summary;
|
||||
userReportPreview.Thumbnail = this.Thumbnail;
|
||||
return userReportPreview;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/UserReport.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/UserReport.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e237c906e28cc824ebeb2d2774f619c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,28 @@
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report appearance hint.
|
||||
/// </summary>
|
||||
public enum UserReportAppearanceHint
|
||||
{
|
||||
/// <summary>
|
||||
/// Display normally.
|
||||
/// </summary>
|
||||
Normal = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Display landscape.
|
||||
/// </summary>
|
||||
Landscape = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Display portrait.
|
||||
/// </summary>
|
||||
Portrait = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Display large.
|
||||
/// </summary>
|
||||
Large = 3
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1060310e28bbbf942b4e46efd6d76488
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
59
Assets/UserReporting/Scripts/Client/UserReportAttachment.cs
Normal file
59
Assets/UserReporting/Scripts/Client/UserReportAttachment.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report attachment.
|
||||
/// </summary>
|
||||
public struct UserReportAttachment
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportAttachment"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="fileName">The file name.</param>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <param name="data">The data.</param>
|
||||
public UserReportAttachment(string name, string fileName, string contentType, byte[] data)
|
||||
{
|
||||
this.Name = name;
|
||||
this.FileName = fileName;
|
||||
this.ContentType = contentType;
|
||||
this.DataBase64 = Convert.ToBase64String(data);
|
||||
this.DataIdentifier = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the content type.
|
||||
/// </summary>
|
||||
public string ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data (base 64 encoded).
|
||||
/// </summary>
|
||||
public string DataBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data identifier. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public string DataIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file name.
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8207a3c1fa17deb429e6402efd1e6725
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
52
Assets/UserReporting/Scripts/Client/UserReportEvent.cs
Normal file
52
Assets/UserReporting/Scripts/Client/UserReportEvent.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report event.
|
||||
/// </summary>
|
||||
public struct UserReportEvent
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception.
|
||||
/// </summary>
|
||||
public SerializableException Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frame number.
|
||||
/// </summary>
|
||||
public int FrameNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full message.
|
||||
/// </summary>
|
||||
public string FullMessage
|
||||
{
|
||||
get { return string.Format("{0}{1}{2}", this.Message, Environment.NewLine, this.StackTrace); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level.
|
||||
/// </summary>
|
||||
public UserReportEventLevel Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stack trace.
|
||||
/// </summary>
|
||||
public string StackTrace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/UserReportEvent.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/UserReportEvent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1167bb63e766c824a899a1e17778b1f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
28
Assets/UserReporting/Scripts/Client/UserReportEventLevel.cs
Normal file
28
Assets/UserReporting/Scripts/Client/UserReportEventLevel.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report event level.
|
||||
/// </summary>
|
||||
public enum UserReportEventLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Info.
|
||||
/// </summary>
|
||||
Info = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Success.
|
||||
/// </summary>
|
||||
Success = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Warning.
|
||||
/// </summary>
|
||||
Warning = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Error.
|
||||
/// </summary>
|
||||
Error = 3
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 779f88f0c218c3e4db442a004d648705
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
71
Assets/UserReporting/Scripts/Client/UserReportList.cs
Normal file
71
Assets/UserReporting/Scripts/Client/UserReportList.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report list.
|
||||
/// </summary>
|
||||
public class UserReportList
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportList"/> class.
|
||||
/// </summary>
|
||||
public UserReportList()
|
||||
{
|
||||
this.UserReportPreviews = new List<UserReportPreview>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the continuation token.
|
||||
/// </summary>
|
||||
public string ContinuationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error.
|
||||
/// </summary>
|
||||
public string Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the list has more items.
|
||||
/// </summary>
|
||||
public bool HasMore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user report previews.
|
||||
/// </summary>
|
||||
public List<UserReportPreview> UserReportPreviews { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Completes the list. This only need to be called by the creator of the list.
|
||||
/// </summary>
|
||||
/// <param name="originalLimit">The original limit.</param>
|
||||
/// <param name="continuationToken">The continuation token.</param>
|
||||
public void Complete(int originalLimit, string continuationToken)
|
||||
{
|
||||
if (this.UserReportPreviews.Count > 0)
|
||||
{
|
||||
if (this.UserReportPreviews.Count > originalLimit)
|
||||
{
|
||||
while (this.UserReportPreviews.Count > originalLimit)
|
||||
{
|
||||
this.UserReportPreviews.RemoveAt(this.UserReportPreviews.Count - 1);
|
||||
}
|
||||
this.ContinuationToken = continuationToken;
|
||||
this.HasMore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/UserReportList.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/UserReportList.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc881d42ec009e14a8c8e119e935daaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
34
Assets/UserReporting/Scripts/Client/UserReportMeasure.cs
Normal file
34
Assets/UserReporting/Scripts/Client/UserReportMeasure.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report measure.
|
||||
/// </summary>
|
||||
public struct UserReportMeasure
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end frame number.
|
||||
/// </summary>
|
||||
public int EndFrameNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metadata.
|
||||
/// </summary>
|
||||
public List<UserReportNamedValue> Metadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metrics.
|
||||
/// </summary>
|
||||
public List<UserReportMetric> Metrics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start frame number.
|
||||
/// </summary>
|
||||
public int StartFrameNumber { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df06a0bc122dc6c4fa1badd66c586aa8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
69
Assets/UserReporting/Scripts/Client/UserReportMetric.cs
Normal file
69
Assets/UserReporting/Scripts/Client/UserReportMetric.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report metrics.
|
||||
/// </summary>
|
||||
public struct UserReportMetric
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the average.
|
||||
/// </summary>
|
||||
public double Average
|
||||
{
|
||||
get { return this.Sum / this.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count.
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum.
|
||||
/// </summary>
|
||||
public double Maximum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum.
|
||||
/// </summary>
|
||||
public double Minimum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sum.
|
||||
/// </summary>
|
||||
public double Sum { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Samples a value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public void Sample(double value)
|
||||
{
|
||||
if (this.Count == 0)
|
||||
{
|
||||
this.Minimum = double.MaxValue;
|
||||
this.Maximum = double.MinValue;
|
||||
}
|
||||
|
||||
this.Count++;
|
||||
this.Sum += value;
|
||||
this.Minimum = Math.Min(this.Minimum, value);
|
||||
this.Maximum = Math.Max(this.Maximum, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/UserReporting/Scripts/Client/UserReportMetric.cs.meta
Normal file
11
Assets/UserReporting/Scripts/Client/UserReportMetric.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb60fdec80e5deb4d9825895b0cc7432
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
37
Assets/UserReporting/Scripts/Client/UserReportNamedValue.cs
Normal file
37
Assets/UserReporting/Scripts/Client/UserReportNamedValue.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report named value.
|
||||
/// </summary>
|
||||
public struct UserReportNamedValue
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportNamedValue"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public UserReportNamedValue(string name, string value)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81a8f683f28633f49a066010ec11fc54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
108
Assets/UserReporting/Scripts/Client/UserReportPreview.cs
Normal file
108
Assets/UserReporting/Scripts/Client/UserReportPreview.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Cloud.Authorization;
|
||||
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report preview or the fly weight version of a user report.
|
||||
/// </summary>
|
||||
public class UserReportPreview
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportPreview"/> class.
|
||||
/// </summary>
|
||||
public UserReportPreview()
|
||||
{
|
||||
this.Dimensions = new List<UserReportNamedValue>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the aggregate metrics.
|
||||
/// </summary>
|
||||
public List<UserReportMetric> AggregateMetrics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the appearance hint.
|
||||
/// </summary>
|
||||
public UserReportAppearanceHint AppearanceHint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content length. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public long ContentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dimensions.
|
||||
/// </summary>
|
||||
public List<UserReportNamedValue> Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time at which the user report expires. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public DateTime ExpiresOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the geo country.
|
||||
/// </summary>
|
||||
public string GeoCountry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public string Identifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IP address. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public string IPAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user report is hidden in the UI if a dimension filter is not specified. This is recommended for automated or high volume reports.
|
||||
/// </summary>
|
||||
public bool IsHiddenWithoutDimension { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user report is silent. Silent user reports do not send events to integrations. This is recommended for automated or high volume reports.
|
||||
/// </summary>
|
||||
public bool IsSilent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the user report is temporary. Temporary user reports are short lived and not queryable.
|
||||
/// </summary>
|
||||
public bool IsTemporary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the license level. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public LicenseLevel LicenseLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the project identifier.
|
||||
/// </summary>
|
||||
public string ProjectIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time at which the user report was received. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public DateTime ReceivedOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the summary.
|
||||
/// </summary>
|
||||
public string Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thumbnail. This screenshot will be resized by the server if too large. Keep the last screenshot small in order to reduce report size and increase submission speed.
|
||||
/// </summary>
|
||||
public UserReportScreenshot Thumbnail { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6328297e77ead774dbe3da7844b2d13d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
43
Assets/UserReporting/Scripts/Client/UserReportScreenshot.cs
Normal file
43
Assets/UserReporting/Scripts/Client/UserReportScreenshot.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Unity.Cloud.UserReporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user report screenshot.
|
||||
/// </summary>
|
||||
public struct UserReportScreenshot
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data (base 64 encoded). Screenshots must be in PNG format.
|
||||
/// </summary>
|
||||
public string DataBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data identifier. This property will be overwritten by the server if provided.
|
||||
/// </summary>
|
||||
public string DataIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frame number.
|
||||
/// </summary>
|
||||
public int FrameNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height.
|
||||
/// </summary>
|
||||
public int Height
|
||||
{
|
||||
get { return PngHelper.GetPngHeightFromBase64Data(this.DataBase64); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width.
|
||||
/// </summary>
|
||||
public int Width
|
||||
{
|
||||
get { return PngHelper.GetPngWidthFromBase64Data(this.DataBase64); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1e5dd7364d072845908195f56c7d791
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
665
Assets/UserReporting/Scripts/Client/UserReportingClient.cs
Normal file
665
Assets/UserReporting/Scripts/Client/UserReportingClient.cs
Normal file
@@ -0,0 +1,665 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Cloud.UserReporting.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user reporting client.
|
||||
/// </summary>
|
||||
public class UserReportingClient
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportingClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="projectIdentifier">The project identifier.</param>
|
||||
/// <param name="platform">The platform.</param>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
public UserReportingClient(string endpoint, string projectIdentifier, IUserReportingPlatform platform, UserReportingClientConfiguration configuration)
|
||||
{
|
||||
// Arguments
|
||||
this.Endpoint = endpoint;
|
||||
this.ProjectIdentifier = projectIdentifier;
|
||||
this.Platform = platform;
|
||||
this.Configuration = configuration;
|
||||
|
||||
// Configuration Clean Up
|
||||
this.Configuration.FramesPerMeasure = this.Configuration.FramesPerMeasure > 0 ? this.Configuration.FramesPerMeasure : 1;
|
||||
this.Configuration.MaximumEventCount = this.Configuration.MaximumEventCount > 0 ? this.Configuration.MaximumEventCount : 1;
|
||||
this.Configuration.MaximumMeasureCount = this.Configuration.MaximumMeasureCount > 0 ? this.Configuration.MaximumMeasureCount : 1;
|
||||
this.Configuration.MaximumScreenshotCount = this.Configuration.MaximumScreenshotCount > 0 ? this.Configuration.MaximumScreenshotCount : 1;
|
||||
|
||||
// Lists
|
||||
this.clientMetrics = new Dictionary<string, UserReportMetric>();
|
||||
this.currentMeasureMetadata = new Dictionary<string, string>();
|
||||
this.currentMetrics = new Dictionary<string, UserReportMetric>();
|
||||
this.events = new CyclicalList<UserReportEvent>(configuration.MaximumEventCount);
|
||||
this.measures = new CyclicalList<UserReportMeasure>(configuration.MaximumMeasureCount);
|
||||
this.screenshots = new CyclicalList<UserReportScreenshot>(configuration.MaximumScreenshotCount);
|
||||
|
||||
// Device Metadata
|
||||
this.deviceMetadata = new List<UserReportNamedValue>();
|
||||
foreach (KeyValuePair<string, string> kvp in this.Platform.GetDeviceMetadata())
|
||||
{
|
||||
this.AddDeviceMetadata(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
// Client Version
|
||||
this.AddDeviceMetadata("UserReportingClientVersion", "2.0");
|
||||
|
||||
// Synchronized Action
|
||||
this.synchronizedActions = new List<Action>();
|
||||
this.currentSynchronizedActions = new List<Action>();
|
||||
|
||||
// Update Stopwatch
|
||||
this.updateStopwatch = new Stopwatch();
|
||||
|
||||
// Is Connected to Logger
|
||||
this.IsConnectedToLogger = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private Dictionary<string, UserReportMetric> clientMetrics;
|
||||
|
||||
private Dictionary<string, string> currentMeasureMetadata;
|
||||
|
||||
private Dictionary<string, UserReportMetric> currentMetrics;
|
||||
|
||||
private List<Action> currentSynchronizedActions;
|
||||
|
||||
private List<UserReportNamedValue> deviceMetadata;
|
||||
|
||||
private CyclicalList<UserReportEvent> events;
|
||||
|
||||
private int frameNumber;
|
||||
|
||||
private bool isMeasureBoundary;
|
||||
|
||||
private int measureFrames;
|
||||
|
||||
private CyclicalList<UserReportMeasure> measures;
|
||||
|
||||
private CyclicalList<UserReportScreenshot> screenshots;
|
||||
|
||||
private int screenshotsSaved;
|
||||
|
||||
private int screenshotsTaken;
|
||||
|
||||
private List<Action> synchronizedActions;
|
||||
|
||||
private Stopwatch updateStopwatch;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration.
|
||||
/// </summary>
|
||||
public UserReportingClientConfiguration Configuration { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoint.
|
||||
/// </summary>
|
||||
public string Endpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the client is connected to the logger. If true, log messages will be included in user reports.
|
||||
/// </summary>
|
||||
public bool IsConnectedToLogger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the client is self reporting. If true, event and metrics about the client will be included in user reports.
|
||||
/// </summary>
|
||||
public bool IsSelfReporting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the platform.
|
||||
/// </summary>
|
||||
public IUserReportingPlatform Platform { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the project identifier.
|
||||
/// </summary>
|
||||
public string ProjectIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether user reporting events should be sent to analytics.
|
||||
/// </summary>
|
||||
public bool SendEventsToAnalytics { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds device metadata.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void AddDeviceMetadata(string name, string value)
|
||||
{
|
||||
lock (this.deviceMetadata)
|
||||
{
|
||||
UserReportNamedValue userReportNamedValue = new UserReportNamedValue();
|
||||
userReportNamedValue.Name = name;
|
||||
userReportNamedValue.Value = value;
|
||||
this.deviceMetadata.Add(userReportNamedValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds measure metadata. Measure metadata is associated with a period of time.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void AddMeasureMetadata(string name, string value)
|
||||
{
|
||||
if (this.currentMeasureMetadata.ContainsKey(name))
|
||||
{
|
||||
this.currentMeasureMetadata[name] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentMeasureMetadata.Add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a synchronized action.
|
||||
/// </summary>
|
||||
/// <param name="action">The action.</param>
|
||||
private void AddSynchronizedAction(Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException("action");
|
||||
}
|
||||
lock (this.synchronizedActions)
|
||||
{
|
||||
this.synchronizedActions.Add(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the screenshots.
|
||||
/// </summary>
|
||||
public void ClearScreenshots()
|
||||
{
|
||||
lock (this.screenshots)
|
||||
{
|
||||
this.screenshots.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user report.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback. Provides the user report that was created.</param>
|
||||
public void CreateUserReport(Action<UserReport> callback)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Info, "Creating user report.");
|
||||
this.WaitForPerforation(this.screenshotsTaken, () =>
|
||||
{
|
||||
this.Platform.RunTask(() =>
|
||||
{
|
||||
// Start Stopwatch
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
|
||||
// Copy Data
|
||||
UserReport userReport = new UserReport();
|
||||
userReport.ProjectIdentifier = this.ProjectIdentifier;
|
||||
|
||||
// Device Metadata
|
||||
lock (this.deviceMetadata)
|
||||
{
|
||||
userReport.DeviceMetadata = this.deviceMetadata.ToList();
|
||||
}
|
||||
|
||||
// Events
|
||||
lock (this.events)
|
||||
{
|
||||
userReport.Events = this.events.ToList();
|
||||
}
|
||||
|
||||
// Measures
|
||||
lock (this.measures)
|
||||
{
|
||||
userReport.Measures = this.measures.ToList();
|
||||
}
|
||||
|
||||
// Screenshots
|
||||
lock (this.screenshots)
|
||||
{
|
||||
userReport.Screenshots = this.screenshots.ToList();
|
||||
}
|
||||
|
||||
// Complete
|
||||
userReport.Complete();
|
||||
|
||||
// Modify
|
||||
this.Platform.ModifyUserReport(userReport);
|
||||
|
||||
// Stop Stopwatch
|
||||
stopwatch.Stop();
|
||||
|
||||
// Sample Client Metric
|
||||
this.SampleClientMetric("UserReportingClient.CreateUserReport.Task", stopwatch.ElapsedMilliseconds);
|
||||
|
||||
// Copy Client Metrics
|
||||
foreach (KeyValuePair<string, UserReportMetric> kvp in this.clientMetrics)
|
||||
{
|
||||
userReport.ClientMetrics.Add(kvp.Value);
|
||||
}
|
||||
|
||||
// Return
|
||||
return userReport;
|
||||
}, (result) => { callback(result as UserReport); });
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoint.
|
||||
/// </summary>
|
||||
/// <returns>The endpoint.</returns>
|
||||
public string GetEndpoint()
|
||||
{
|
||||
if (this.Endpoint == null)
|
||||
{
|
||||
return "https://localhost";
|
||||
}
|
||||
return this.Endpoint.Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an event.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
public void LogEvent(UserReportEventLevel level, string message)
|
||||
{
|
||||
this.LogEvent(level, message, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an event.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="stackTrace">The stack trace.</param>
|
||||
public void LogEvent(UserReportEventLevel level, string message, string stackTrace)
|
||||
{
|
||||
this.LogEvent(level, message, stackTrace, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an event with a stack trace and exception.
|
||||
/// </summary>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="stackTrace">The stack trace.</param>
|
||||
/// <param name="exception">The exception.</param>
|
||||
private void LogEvent(UserReportEventLevel level, string message, string stackTrace, Exception exception)
|
||||
{
|
||||
lock (this.events)
|
||||
{
|
||||
UserReportEvent userReportEvent = new UserReportEvent();
|
||||
userReportEvent.Level = level;
|
||||
userReportEvent.Message = message;
|
||||
userReportEvent.FrameNumber = this.frameNumber;
|
||||
userReportEvent.StackTrace = stackTrace;
|
||||
userReportEvent.Timestamp = DateTime.UtcNow;
|
||||
if (exception != null)
|
||||
{
|
||||
userReportEvent.Exception = new SerializableException(exception);
|
||||
}
|
||||
this.events.Add(userReportEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
public void LogException(Exception exception)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Error, null, null, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Samples a client metric. These metrics are only sample when self reporting is enabled.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void SampleClientMetric(string name, double value)
|
||||
{
|
||||
if (double.IsInfinity(value) || double.IsNaN(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!this.clientMetrics.ContainsKey(name))
|
||||
{
|
||||
UserReportMetric newUserReportMetric = new UserReportMetric();
|
||||
newUserReportMetric.Name = name;
|
||||
this.clientMetrics.Add(name, newUserReportMetric);
|
||||
}
|
||||
UserReportMetric userReportMetric = this.clientMetrics[name];
|
||||
userReportMetric.Sample(value);
|
||||
this.clientMetrics[name] = userReportMetric;
|
||||
|
||||
// Self Reporting
|
||||
if (this.IsSelfReporting)
|
||||
{
|
||||
this.SampleMetric(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Samples a metric. Metrics can be sampled frequently and have low overhead.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void SampleMetric(string name, double value)
|
||||
{
|
||||
if (this.Configuration.MetricsGatheringMode == MetricsGatheringMode.Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (double.IsInfinity(value) || double.IsNaN(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!this.currentMetrics.ContainsKey(name))
|
||||
{
|
||||
UserReportMetric newUserReportMetric = new UserReportMetric();
|
||||
newUserReportMetric.Name = name;
|
||||
this.currentMetrics.Add(name, newUserReportMetric);
|
||||
}
|
||||
UserReportMetric userReportMetric = this.currentMetrics[name];
|
||||
userReportMetric.Sample(value);
|
||||
this.currentMetrics[name] = userReportMetric;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a user report to disk.
|
||||
/// </summary>
|
||||
/// <param name="userReport">The user report.</param>
|
||||
public void SaveUserReportToDisk(UserReport userReport)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Info, "Saving user report to disk.");
|
||||
string json = this.Platform.SerializeJson(userReport);
|
||||
File.WriteAllText("UserReport.json", json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a user report to the server.
|
||||
/// </summary>
|
||||
/// <param name="userReport">The user report.</param>
|
||||
/// <param name="callback">The callback. Provides a value indicating whether sending the user report was successful and provides the user report after it is modified by the server.</param>
|
||||
public void SendUserReport(UserReport userReport, Action<bool, UserReport> callback)
|
||||
{
|
||||
this.SendUserReport(userReport, null, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a user report to the server.
|
||||
/// </summary>
|
||||
/// <param name="userReport">The user report.</param>
|
||||
/// <param name="progressCallback">The progress callback. Provides the upload and download progress.</param>
|
||||
/// <param name="callback">The callback. Provides a value indicating whether sending the user report was successful and provides the user report after it is modified by the server.</param>
|
||||
public void SendUserReport(UserReport userReport, Action<float, float> progressCallback, Action<bool, UserReport> callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (userReport == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (userReport.Identifier != null)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Warning, "Identifier cannot be set on the client side. The value provided was discarded.");
|
||||
return;
|
||||
}
|
||||
if (userReport.ContentLength != 0)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Warning, "ContentLength cannot be set on the client side. The value provided was discarded.");
|
||||
return;
|
||||
}
|
||||
if (userReport.ReceivedOn != default(DateTime))
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Warning, "ReceivedOn cannot be set on the client side. The value provided was discarded.");
|
||||
return;
|
||||
}
|
||||
if (userReport.ExpiresOn != default(DateTime))
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Warning, "ExpiresOn cannot be set on the client side. The value provided was discarded.");
|
||||
return;
|
||||
}
|
||||
this.LogEvent(UserReportEventLevel.Info, "Sending user report.");
|
||||
string json = this.Platform.SerializeJson(userReport);
|
||||
byte[] jsonData = Encoding.UTF8.GetBytes(json);
|
||||
string endpoint = this.GetEndpoint();
|
||||
string url = string.Format(string.Format("{0}/api/userreporting", endpoint));
|
||||
this.Platform.Post(url, "application/json", jsonData, (uploadProgress, downloadProgress) =>
|
||||
{
|
||||
if (progressCallback != null)
|
||||
{
|
||||
progressCallback(uploadProgress, downloadProgress);
|
||||
}
|
||||
}, (success, result) =>
|
||||
{
|
||||
this.AddSynchronizedAction(() =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
try
|
||||
{
|
||||
string jsonResult = Encoding.UTF8.GetString(result);
|
||||
UserReport userReportResult = this.Platform.DeserializeJson<UserReport>(jsonResult);
|
||||
if (userReportResult != null)
|
||||
{
|
||||
if (this.SendEventsToAnalytics)
|
||||
{
|
||||
Dictionary<string, object> eventData = new Dictionary<string, object>();
|
||||
eventData.Add("UserReportIdentifier", userReport.Identifier);
|
||||
this.Platform.SendAnalyticsEvent("UserReportingClient.SendUserReport", eventData);
|
||||
}
|
||||
callback(success, userReportResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(false, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Error, string.Format("Sending user report failed: {0}", ex.ToString()));
|
||||
callback(false, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Error, "Sending user report failed.");
|
||||
callback(false, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Error, string.Format("Sending user report failed: {0}", ex.ToString()));
|
||||
callback(false, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a screenshot.
|
||||
/// </summary>
|
||||
/// <param name="maximumWidth">The maximum width.</param>
|
||||
/// <param name="maximumHeight">The maximum height.</param>
|
||||
/// <param name="callback">The callback. Provides the screenshot.</param>
|
||||
public void TakeScreenshot(int maximumWidth, int maximumHeight, Action<UserReportScreenshot> callback)
|
||||
{
|
||||
this.TakeScreenshotFromSource(maximumWidth, maximumHeight, null, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a screenshot.
|
||||
/// </summary>
|
||||
/// <param name="maximumWidth">The maximum width.</param>
|
||||
/// <param name="maximumHeight">The maximum height.</param>
|
||||
/// <param name="source">The source. Passing null will capture the screen. Passing a camera will capture the camera's view. Passing a render texture will capture the render texture.</param>
|
||||
/// <param name="callback">The callback. Provides the screenshot.</param>
|
||||
public void TakeScreenshotFromSource(int maximumWidth, int maximumHeight, object source, Action<UserReportScreenshot> callback)
|
||||
{
|
||||
this.LogEvent(UserReportEventLevel.Info, "Taking screenshot.");
|
||||
this.screenshotsTaken++;
|
||||
this.Platform.TakeScreenshot(this.frameNumber, maximumWidth, maximumHeight, source, (passedFrameNumber, data) =>
|
||||
{
|
||||
this.AddSynchronizedAction(() =>
|
||||
{
|
||||
lock (this.screenshots)
|
||||
{
|
||||
UserReportScreenshot userReportScreenshot = new UserReportScreenshot();
|
||||
userReportScreenshot.FrameNumber = passedFrameNumber;
|
||||
userReportScreenshot.DataBase64 = Convert.ToBase64String(data);
|
||||
this.screenshots.Add(userReportScreenshot);
|
||||
this.screenshotsSaved++;
|
||||
callback(userReportScreenshot);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the user reporting client, which updates networking communication, screenshotting, and metrics gathering.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
// Stopwatch
|
||||
this.updateStopwatch.Reset();
|
||||
this.updateStopwatch.Start();
|
||||
|
||||
// Update Platform
|
||||
this.Platform.Update(this);
|
||||
|
||||
// Measures
|
||||
if (this.Configuration.MetricsGatheringMode != MetricsGatheringMode.Disabled)
|
||||
{
|
||||
this.isMeasureBoundary = false;
|
||||
int framesPerMeasure = this.Configuration.FramesPerMeasure;
|
||||
if (this.measureFrames >= framesPerMeasure)
|
||||
{
|
||||
lock (this.measures)
|
||||
{
|
||||
UserReportMeasure userReportMeasure = new UserReportMeasure();
|
||||
userReportMeasure.StartFrameNumber = this.frameNumber - framesPerMeasure;
|
||||
userReportMeasure.EndFrameNumber = this.frameNumber - 1;
|
||||
UserReportMeasure evictedUserReportMeasure = this.measures.GetNextEviction();
|
||||
if (evictedUserReportMeasure.Metrics != null)
|
||||
{
|
||||
userReportMeasure.Metadata = evictedUserReportMeasure.Metadata;
|
||||
userReportMeasure.Metrics = evictedUserReportMeasure.Metrics;
|
||||
}
|
||||
else
|
||||
{
|
||||
userReportMeasure.Metadata = new List<UserReportNamedValue>();
|
||||
userReportMeasure.Metrics = new List<UserReportMetric>();
|
||||
}
|
||||
userReportMeasure.Metadata.Clear();
|
||||
userReportMeasure.Metrics.Clear();
|
||||
foreach (KeyValuePair<string, string> kvp in this.currentMeasureMetadata)
|
||||
{
|
||||
UserReportNamedValue userReportNamedValue = new UserReportNamedValue();
|
||||
userReportNamedValue.Name = kvp.Key;
|
||||
userReportNamedValue.Value = kvp.Value;
|
||||
userReportMeasure.Metadata.Add(userReportNamedValue);
|
||||
}
|
||||
foreach (KeyValuePair<string, UserReportMetric> kvp in this.currentMetrics)
|
||||
{
|
||||
userReportMeasure.Metrics.Add(kvp.Value);
|
||||
}
|
||||
this.currentMetrics.Clear();
|
||||
this.measures.Add(userReportMeasure);
|
||||
this.measureFrames = 0;
|
||||
this.isMeasureBoundary = true;
|
||||
}
|
||||
}
|
||||
this.measureFrames++;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isMeasureBoundary = true;
|
||||
}
|
||||
|
||||
// Synchronization
|
||||
lock (this.synchronizedActions)
|
||||
{
|
||||
foreach (Action synchronizedAction in this.synchronizedActions)
|
||||
{
|
||||
this.currentSynchronizedActions.Add(synchronizedAction);
|
||||
}
|
||||
this.synchronizedActions.Clear();
|
||||
}
|
||||
|
||||
// Perform Synchronized Actions
|
||||
foreach (Action synchronizedAction in this.currentSynchronizedActions)
|
||||
{
|
||||
synchronizedAction();
|
||||
}
|
||||
this.currentSynchronizedActions.Clear();
|
||||
|
||||
// Frame Number
|
||||
this.frameNumber++;
|
||||
|
||||
// Stopwatch
|
||||
this.updateStopwatch.Stop();
|
||||
this.SampleClientMetric("UserReportingClient.Update", this.updateStopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the user reporting client at the end of the frame, which updates networking communication, screenshotting, and metrics gathering.
|
||||
/// </summary>
|
||||
public void UpdateOnEndOfFrame()
|
||||
{
|
||||
// Stopwatch
|
||||
this.updateStopwatch.Reset();
|
||||
this.updateStopwatch.Start();
|
||||
|
||||
// Update Platform
|
||||
this.Platform.OnEndOfFrame(this);
|
||||
|
||||
// Stopwatch
|
||||
this.updateStopwatch.Stop();
|
||||
this.SampleClientMetric("UserReportingClient.UpdateOnEndOfFrame", this.updateStopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for perforation, a boundary between measures when no screenshots are in progress.
|
||||
/// </summary>
|
||||
/// <param name="currentScreenshotsTaken">The current screenshots taken.</param>
|
||||
/// <param name="callback">The callback.</param>
|
||||
private void WaitForPerforation(int currentScreenshotsTaken, Action callback)
|
||||
{
|
||||
if (this.screenshotsSaved >= currentScreenshotsTaken && this.isMeasureBoundary)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AddSynchronizedAction(() => { this.WaitForPerforation(currentScreenshotsTaken, callback); });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 192cdc972af5b47488dec15571884eec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,84 @@
|
||||
namespace Unity.Cloud.UserReporting.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents configuration for the user reporting client.
|
||||
/// </summary>
|
||||
public class UserReportingClientConfiguration
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportingClientConfiguration"/> class.
|
||||
/// </summary>
|
||||
public UserReportingClientConfiguration()
|
||||
{
|
||||
this.MaximumEventCount = 100;
|
||||
this.MaximumMeasureCount = 300;
|
||||
this.FramesPerMeasure = 60;
|
||||
this.MaximumScreenshotCount = 10;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportingClientConfiguration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="maximumEventCount">The maximum event count. This is a rolling window.</param>
|
||||
/// <param name="maximumMeasureCount">The maximum measure count. This is a rolling window.</param>
|
||||
/// <param name="framesPerMeasure">The number of frames per measure. A user report is only created on the boundary between measures. A large number of frames per measure will increase user report creation time by this number of frames in the worst case.</param>
|
||||
/// <param name="maximumScreenshotCount">The maximum screenshot count. This is a rolling window.</param>
|
||||
public UserReportingClientConfiguration(int maximumEventCount, int maximumMeasureCount, int framesPerMeasure, int maximumScreenshotCount)
|
||||
{
|
||||
this.MaximumEventCount = maximumEventCount;
|
||||
this.MaximumMeasureCount = maximumMeasureCount;
|
||||
this.FramesPerMeasure = framesPerMeasure;
|
||||
this.MaximumScreenshotCount = maximumScreenshotCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="UserReportingClientConfiguration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="maximumEventCount">The maximum event count. This is a rolling window.</param>
|
||||
/// <param name="metricsGatheringMode">The metrics gathering mode.</param>
|
||||
/// <param name="maximumMeasureCount">The maximum measure count. This is a rolling window.</param>
|
||||
/// <param name="framesPerMeasure">The number of frames per measure. A user report is only created on the boundary between measures. A large number of frames per measure will increase user report creation time by this number of frames in the worst case.</param>
|
||||
/// <param name="maximumScreenshotCount">The maximum screenshot count. This is a rolling window.</param>
|
||||
public UserReportingClientConfiguration(int maximumEventCount, MetricsGatheringMode metricsGatheringMode, int maximumMeasureCount, int framesPerMeasure, int maximumScreenshotCount)
|
||||
{
|
||||
this.MaximumEventCount = maximumEventCount;
|
||||
this.MetricsGatheringMode = metricsGatheringMode;
|
||||
this.MaximumMeasureCount = maximumMeasureCount;
|
||||
this.FramesPerMeasure = framesPerMeasure;
|
||||
this.MaximumScreenshotCount = maximumScreenshotCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frames per measure.
|
||||
/// </summary>
|
||||
public int FramesPerMeasure { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum event count.
|
||||
/// </summary>
|
||||
public int MaximumEventCount { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum measure count.
|
||||
/// </summary>
|
||||
public int MaximumMeasureCount { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum screenshot count.
|
||||
/// </summary>
|
||||
public int MaximumScreenshotCount { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metrics gathering mode.
|
||||
/// </summary>
|
||||
public MetricsGatheringMode MetricsGatheringMode { get; internal set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ec0cef74a76c284fa3d592f3005f6f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user