using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Assets.UserReporting.Scripts.Plugin;
using Unity.Cloud.UserReporting.Client;
using UnityEngine;
using UnityEngine.Analytics;
using UnityEngine.Networking;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
namespace Unity.Cloud.UserReporting.Plugin
{
///
/// Represents the Unity user reporting platform.
///
public class UnityUserReportingPlatform : IUserReportingPlatform, ILogListener
{
#region Nested Types
///
/// Represents a log message.
///
private struct LogMessage
{
#region Fields
///
/// Gets or sets the log string.
///
public string LogString;
///
/// Gets or sets the log type.
///
public LogType LogType;
///
/// Gets or sets the stack trace.
///
public string StackTrace;
#endregion
}
///
/// Represents a post operation.
///
private class PostOperation
{
#region Properties
///
/// Gets or sets the callback.
///
public Action Callback { get; set; }
///
/// Gets or sets the progress callback.
///
public Action ProgressCallback { get; set; }
///
/// Gets or sets the web request.
///
public UnityWebRequest WebRequest { get; set; }
#endregion
}
///
/// Represents a profiler sampler.
///
private struct ProfilerSampler
{
#region Fields
///
/// Gets or sets the name.
///
public string Name;
///
/// Gets or sets the recorder.
///
public Recorder Recorder;
#endregion
#region Methods
///
/// Gets the value of the sampler.
///
/// The value of the sampler.
public double GetValue()
{
if (this.Recorder == null)
{
return 0;
}
return this.Recorder.elapsedNanoseconds / 1000000.0;
}
#endregion
}
///
/// Represents a screenshot operation.
///
private class ScreenshotOperation
{
#region Properties
///
/// Gets or sets the callback.
///
public Action Callback { get; set; }
///
/// Gets or sets the frame number.
///
public int FrameNumber { get; set; }
///
/// Gets or sets the maximum height.
///
public int MaximumHeight { get; set; }
///
/// Gets or sets the maximum width.
///
public int MaximumWidth { get; set; }
///
/// Gets or sets the PNG data.
///
public byte[] PngData { get; set; }
///
/// Gets or sets the source.
///
public object Source { get; set; }
///
/// Gets or sets the stage.
///
public ScreenshotStage Stage { get; set; }
///
/// Gets or sets the texture.
///
public Texture2D Texture { get; set; }
///
/// Gets or sets the texture (resized).
///
public Texture2D TextureResized { get; set; }
///
/// Gets or sets the Unity frame.
///
public int UnityFrame { get; set; }
///
/// Gets or sets the wait frames.
///
public int WaitFrames { get; set; }
#endregion
}
///
/// Represents a screenshot stage.
///
private enum ScreenshotStage
{
///
/// Render.
///
Render = 0,
///
/// Read pixels.
///
ReadPixels = 1,
///
/// Gets pixels.
///
GetPixels = 2,
///
/// Encode to PNG.
///
EncodeToPNG = 3,
///
/// Done.
///
Done = 4
}
#endregion
#region Constructors
///
/// Creates a new instance of the class.
///
public UnityUserReportingPlatform()
{
this.logMessages = new List();
this.postOperations = new List();
this.screenshotOperations = new List();
this.screenshotStopwatch = new Stopwatch();
// Recorders
this.profilerSamplers = new List();
Dictionary samplerNames = this.GetSamplerNames();
foreach (KeyValuePair kvp in samplerNames)
{
Sampler sampler = Sampler.Get(kvp.Key);
if (sampler.isValid)
{
Recorder recorder = sampler.GetRecorder();
recorder.enabled = true;
ProfilerSampler profilerSampler = new ProfilerSampler();
profilerSampler.Name = kvp.Value;
profilerSampler.Recorder = recorder;
this.profilerSamplers.Add(profilerSampler);
}
}
// Log Messages
LogDispatcher.Register(this);
}
#endregion
#region Fields
private List logMessages;
private List postOperations;
private List profilerSamplers;
private List screenshotOperations;
private Stopwatch screenshotStopwatch;
private List taskOperations;
#endregion
#region Methods
///
public T DeserializeJson(string json)
{
return SimpleJson.SimpleJson.DeserializeObject(json);
}
///
public void OnEndOfFrame(UserReportingClient client)
{
// Screenshot Operations
int screenshotOperationIndex = 0;
while (screenshotOperationIndex < this.screenshotOperations.Count)
{
ScreenshotOperation screenshotOperation = this.screenshotOperations[screenshotOperationIndex];
if (screenshotOperation.Stage == ScreenshotStage.Render && screenshotOperation.WaitFrames < 1)
{
Camera cameraSource = screenshotOperation.Source as Camera;
if (cameraSource != null)
{
this.screenshotStopwatch.Reset();
this.screenshotStopwatch.Start();
RenderTexture renderTexture = new RenderTexture(screenshotOperation.MaximumWidth, screenshotOperation.MaximumHeight, 24);
RenderTexture originalTargetTexture = cameraSource.targetTexture;
cameraSource.targetTexture = renderTexture;
cameraSource.Render();
cameraSource.targetTexture = originalTargetTexture;
this.screenshotStopwatch.Stop();
client.SampleClientMetric("Screenshot.Render", this.screenshotStopwatch.ElapsedMilliseconds);
screenshotOperation.Source = renderTexture;
screenshotOperation.Stage = ScreenshotStage.ReadPixels;
screenshotOperation.WaitFrames = 15;
screenshotOperationIndex++;
continue;
}
else
{
screenshotOperation.Stage = ScreenshotStage.ReadPixels;
}
}
if (screenshotOperation.Stage == ScreenshotStage.ReadPixels && screenshotOperation.WaitFrames < 1)
{
this.screenshotStopwatch.Reset();
this.screenshotStopwatch.Start();
RenderTexture renderTextureSource = screenshotOperation.Source as RenderTexture;
if (renderTextureSource != null)
{
RenderTexture originalActiveTexture = RenderTexture.active;
RenderTexture.active = renderTextureSource;
screenshotOperation.Texture = new Texture2D(renderTextureSource.width, renderTextureSource.height, TextureFormat.ARGB32, true);
screenshotOperation.Texture.ReadPixels(new Rect(0, 0, renderTextureSource.width, renderTextureSource.height), 0, 0);
screenshotOperation.Texture.Apply();
RenderTexture.active = originalActiveTexture;
}
else
{
screenshotOperation.Texture = new Texture2D(Screen.width, Screen.height, TextureFormat.ARGB32, true);
screenshotOperation.Texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
screenshotOperation.Texture.Apply();
}
this.screenshotStopwatch.Stop();
client.SampleClientMetric("Screenshot.ReadPixels", this.screenshotStopwatch.ElapsedMilliseconds);
screenshotOperation.Stage = ScreenshotStage.GetPixels;
screenshotOperation.WaitFrames = 15;
screenshotOperationIndex++;
continue;
}
if (screenshotOperation.Stage == ScreenshotStage.GetPixels && screenshotOperation.WaitFrames < 1)
{
this.screenshotStopwatch.Reset();
this.screenshotStopwatch.Start();
int maximumWidth = screenshotOperation.MaximumWidth > 32 ? screenshotOperation.MaximumWidth : 32;
int maximumHeight = screenshotOperation.MaximumHeight > 32 ? screenshotOperation.MaximumHeight : 32;
int width = screenshotOperation.Texture.width;
int height = screenshotOperation.Texture.height;
int mipLevel = 0;
while (width > maximumWidth || height > maximumHeight)
{
width /= 2;
height /= 2;
mipLevel++;
}
screenshotOperation.TextureResized = new Texture2D(width, height);
screenshotOperation.TextureResized.SetPixels(screenshotOperation.Texture.GetPixels(mipLevel));
screenshotOperation.TextureResized.Apply();
this.screenshotStopwatch.Stop();
client.SampleClientMetric("Screenshot.GetPixels", this.screenshotStopwatch.ElapsedMilliseconds);
screenshotOperation.Stage = ScreenshotStage.EncodeToPNG;
screenshotOperation.WaitFrames = 15;
screenshotOperationIndex++;
continue;
}
if (screenshotOperation.Stage == ScreenshotStage.EncodeToPNG && screenshotOperation.WaitFrames < 1)
{
this.screenshotStopwatch.Reset();
this.screenshotStopwatch.Start();
screenshotOperation.PngData = screenshotOperation.TextureResized.EncodeToPNG();
this.screenshotStopwatch.Stop();
client.SampleClientMetric("Screenshot.EncodeToPNG", this.screenshotStopwatch.ElapsedMilliseconds);
screenshotOperation.Stage = ScreenshotStage.Done;
screenshotOperationIndex++;
continue;
}
if (screenshotOperation.Stage == ScreenshotStage.Done && screenshotOperation.WaitFrames < 1)
{
screenshotOperation.Callback(screenshotOperation.FrameNumber, screenshotOperation.PngData);
UnityEngine.Object.Destroy(screenshotOperation.Texture);
UnityEngine.Object.Destroy(screenshotOperation.TextureResized);
this.screenshotOperations.Remove(screenshotOperation);
}
screenshotOperation.WaitFrames--;
}
}
///
public void Post(string endpoint, string contentType, byte[] content, Action progressCallback, Action callback)
{
UnityWebRequest webRequest = new UnityWebRequest(endpoint, "POST");
webRequest.uploadHandler = new UploadHandlerRaw(content);
webRequest.downloadHandler = new DownloadHandlerBuffer();
webRequest.SetRequestHeader("Content-Type", contentType);
webRequest.SendWebRequest();
UnityUserReportingPlatform.PostOperation postOperation = new UnityUserReportingPlatform.PostOperation();
postOperation.WebRequest = webRequest;
postOperation.Callback = callback;
postOperation.ProgressCallback = progressCallback;
this.postOperations.Add(postOperation);
}
public void ReceiveLogMessage(string logString, string stackTrace, LogType logType)
{
lock (this.logMessages)
{
LogMessage logMessage = new LogMessage();
logMessage.LogString = logString;
logMessage.StackTrace = stackTrace;
logMessage.LogType = logType;
this.logMessages.Add(logMessage);
}
}
///
public void RunTask(Func