提交Unity 联机Pro

This commit is contained in:
PC-20230316NUNE\Administrator
2024-08-17 14:27:18 +08:00
parent f00193b000
commit 894100ae37
7448 changed files with 854473 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
namespace SRDebugger.Internal
{
public static class SRDebugApi
{
#if !UNITY_EDITOR
public const string Protocol = "https://";
#else
public const string Protocol = "http://";
#endif
public const string EndPoint = Protocol + "srdebugger.stompyrobot.uk";
public const string BugReportEndPoint = EndPoint + "/report/submit";
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5a0f930eb7b829c40a5ba3024eed594d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,96 @@
namespace SRDebugger.Internal
{
using System.Collections.Generic;
using System.IO;
using System.Net;
using SRF;
public static class SRDebugApiUtil
{
public static string ParseErrorException(WebException ex)
{
if (ex.Response == null)
{
return ex.Message;
}
try
{
var response = ReadResponseStream(ex.Response);
return ParseErrorResponse(response);
}
catch
{
return ex.Message;
}
}
public static string ParseErrorResponse(string response, string fallback = "Unexpected Response")
{
try
{
var jsonTable = (Dictionary<string, object>) Json.Deserialize(response);
var error = "";
error += jsonTable["errorMessage"];
object value;
if (jsonTable.TryGetValue("errors", out value) && value is IList<object>)
{
var errors = value as IList<object>;
foreach (var e in errors)
{
error += "\n";
error += e;
}
}
return error;
}
catch
{
if (response.Contains("<html>"))
{
return fallback;
}
return response;
}
}
#if UNITY_EDITOR || (!NETFX_CORE && !UNITY_WINRT)
public static bool ReadResponse(HttpWebRequest request, out string result)
{
try
{
var response = request.GetResponse();
result = ReadResponseStream(response);
return true;
}
catch (WebException e)
{
result = ParseErrorException(e);
return false;
}
}
#endif
public static string ReadResponseStream(WebResponse stream)
{
using (var responseSteam = stream.GetResponseStream())
{
using (var streamReader = new StreamReader(responseSteam))
{
return streamReader.ReadToEnd();
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f41e06622aa4979458cf59f1a355095a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,189 @@
namespace SRDebugger.Internal
{
using System;
using System.Collections;
using System.Collections.Generic;
using SRF;
using UnityEngine;
using System.Text;
using UnityEngine.Networking;
class BugReportApi : MonoBehaviour
{
private string _apiKey;
private BugReport _bugReport;
private bool _isBusy;
private UnityWebRequest _webRequest;
private Action<BugReportSubmitResult> _onComplete;
private IProgress<float> _progress;
public static void Submit(BugReport report, string apiKey, Action<BugReportSubmitResult> onComplete, IProgress<float> progress)
{
var go = new GameObject("BugReportApi");
go.transform.parent = Hierarchy.Get("SRDebugger");
var bugReportApi = go.AddComponent<BugReportApi>();
bugReportApi.Init(report, apiKey, onComplete, progress);
bugReportApi.StartCoroutine(bugReportApi.Submit());
}
private void Init(BugReport report, string apiKey, Action<BugReportSubmitResult> onComplete, IProgress<float> progress)
{
_bugReport = report;
_apiKey = apiKey;
_onComplete = onComplete;
_progress = progress;
}
void Update()
{
if (_isBusy && _webRequest != null)
{
_progress.Report(_webRequest.uploadProgress);
}
}
IEnumerator Submit()
{
if (_isBusy)
{
throw new InvalidOperationException("BugReportApi is already sending a bug report");
}
// Reset state
_isBusy = true;
_webRequest = null;
string json;
byte[] jsonBytes;
try
{
json = BuildJsonRequest(_bugReport);
jsonBytes = Encoding.UTF8.GetBytes(json);
}
catch (Exception e)
{
Debug.LogError(e);
SetCompletionState(BugReportSubmitResult.Error("Error building bug report."));
yield break;
}
try
{
const string jsonContentType = "application/json";
_webRequest = new UnityWebRequest(SRDebugApi.BugReportEndPoint, UnityWebRequest.kHttpVerbPOST,
new DownloadHandlerBuffer(), new UploadHandlerRaw(jsonBytes)
{
contentType = jsonContentType
});
_webRequest.SetRequestHeader("Accept", jsonContentType);
_webRequest.SetRequestHeader("X-ApiKey", _apiKey);
}
catch (Exception e)
{
Debug.LogError(e);
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
if (_webRequest == null)
{
SetCompletionState(BugReportSubmitResult.Error("Error building bug report request."));
yield break;
}
yield return _webRequest.SendWebRequest();
#if UNITY_2018 || UNITY_2019
bool isError = _webRequest.isNetworkError;
#else
bool isError = _webRequest.result == UnityWebRequest.Result.ConnectionError || _webRequest.result == UnityWebRequest.Result.DataProcessingError;
#endif
if (isError)
{
SetCompletionState(BugReportSubmitResult.Error("Request Error: " + _webRequest.error));
_webRequest.Dispose();
yield break;
}
long responseCode = _webRequest.responseCode;
var responseJson = _webRequest.downloadHandler.text;
_webRequest.Dispose();
if (responseCode != 200)
{
SetCompletionState(BugReportSubmitResult.Error("Server: " + SRDebugApiUtil.ParseErrorResponse(responseJson, "Unknown response from server")));
yield break;
}
SetCompletionState(BugReportSubmitResult.Success);
}
private void SetCompletionState(BugReportSubmitResult result)
{
_bugReport.ScreenshotData = null; // Clear the heaviest data in case something holds onto it
_isBusy = false;
if (!result.IsSuccessful)
{
Debug.LogError("Bug Reporter Error: " + result.ErrorMessage);
}
Destroy(gameObject);
_onComplete(result);
}
private static string BuildJsonRequest(BugReport report)
{
var ht = new Hashtable();
ht.Add("userEmail", report.Email);
ht.Add("userDescription", report.UserDescription);
ht.Add("console", CreateConsoleDump());
ht.Add("systemInformation", report.SystemInformation);
ht.Add("applicationIdentifier", Application.identifier);
if (report.ScreenshotData != null)
{
ht.Add("screenshot", Convert.ToBase64String(report.ScreenshotData));
}
var json = Json.Serialize(ht);
return json;
}
private static List<List<string>> CreateConsoleDump()
{
var consoleLog = Service.Console.AllEntries;
var list = new List<List<string>>(consoleLog.Count);
foreach (var consoleEntry in consoleLog)
{
var entry = new List<string>(5);
entry.Add(consoleEntry.LogType.ToString());
entry.Add(consoleEntry.Message);
entry.Add(consoleEntry.StackTrace);
if (consoleEntry.Count > 1)
{
entry.Add(consoleEntry.Count.ToString());
}
list.Add(entry);
}
return list;
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dfdfb4c8f27446b4d8aa88d3980f4c50
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,29 @@
namespace SRDebugger.Internal
{
using System.Collections;
using UnityEngine;
public class BugReportScreenshotUtil
{
public static byte[] ScreenshotData;
public static IEnumerator ScreenshotCaptureCo()
{
if (ScreenshotData != null)
{
Debug.LogWarning("[SRDebugger] Warning, overriding existing screenshot data.");
}
yield return new WaitForEndOfFrame();
var tex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
tex.Apply();
ScreenshotData = tex.EncodeToPNG();
Object.Destroy(tex);
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52ead462624dc0c4d80e12ce55109a26
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,371 @@
 /*
----------------------------------------------------------------------------
"THE BEER-WARE LICENSE" (Revision 42):
Joao Portela wrote this file. As long as you retain this notice you
can do whatever you want with this stuff. If we meet some day, and you think
this stuff is worth it, you can buy me a beer in return.
Joao Portela
--------------------------------------------------------------------------
* https://github.com/joaoportela/CircullarBuffer-CSharp
*/
namespace SRDebugger
{
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Circular buffer.
/// When writting to a full buffer:
/// PushBack -> removes this[0] / Front()
/// PushFront -> removes this[Size-1] / Back()
/// this implementation is inspired by
/// http://www.boost.org/doc/libs/1_53_0/libs/circular_buffer/doc/circular_buffer.html
/// because I liked their interface.
/// </summary>
public class CircularBuffer<T> : IEnumerable<T>, IReadOnlyList<T>
{
private readonly T[] _buffer;
/// <summary>
/// The _end. Index after the last element in the buffer.
/// </summary>
private int _end;
/// <summary>
/// The _size. Buffer size.
/// </summary>
private int _count;
/// <summary>
/// The _start. Index of the first element in buffer.
/// </summary>
private int _start;
public CircularBuffer(int capacity)
: this(capacity, new T[] {}) {}
/// <summary>
/// Initializes a new instance of the <see cref="CircularBuffer{T}" /> class.
/// </summary>
/// <param name='capacity'>
/// Buffer capacity. Must be positive.
/// </param>
/// <param name='items'>
/// Items to fill buffer with. Items length must be less than capacity.
/// Sugestion: use Skip(x).Take(y).ToArray() to build this argument from
/// any enumerable.
/// </param>
public CircularBuffer(int capacity, T[] items)
{
if (capacity < 1)
{
throw new ArgumentException(
"Circular buffer cannot have negative or zero capacity.", "capacity");
}
if (items == null)
{
throw new ArgumentNullException("items");
}
if (items.Length > capacity)
{
throw new ArgumentException(
"Too many items to fit circular buffer", "items");
}
_buffer = new T[capacity];
Array.Copy(items, _buffer, items.Length);
_count = items.Length;
_start = 0;
_end = _count == capacity ? 0 : _count;
}
/// <summary>
/// Maximum capacity of the buffer. Elements pushed into the buffer after
/// maximum capacity is reached (IsFull = true), will remove an element.
/// </summary>
public int Capacity
{
get { return _buffer.Length; }
}
public bool IsFull
{
get { return Count == Capacity; }
}
public bool IsEmpty
{
get { return Count == 0; }
}
/// <summary>
/// Current buffer size (the number of elements that the buffer has).
/// </summary>
public int Count
{
get { return _count; }
}
public T this[int index]
{
get
{
if (IsEmpty)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index));
}
if (index >= _count)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}",
index, _count));
}
var actualIndex = InternalIndex(index);
return _buffer[actualIndex];
}
set
{
if (IsEmpty)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index));
}
if (index >= _count)
{
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}",
index, _count));
}
var actualIndex = InternalIndex(index);
_buffer[actualIndex] = value;
}
}
public void Clear()
{
_count = 0;
_start = 0;
_end = 0;
}
#region IEnumerable<T> implementation
public IEnumerator<T> GetEnumerator()
{
var segments = new ArraySegment<T>[2] {ArrayOne(), ArrayTwo()};
foreach (var segment in segments)
{
for (var i = 0; i < segment.Count; i++)
{
yield return segment.Array[segment.Offset + i];
}
}
}
#endregion
#region IEnumerable implementation
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region IList<T> implementation
#endregion
/// <summary>
/// Element at the front of the buffer - this[0].
/// </summary>
/// <returns>The value of the element of type T at the front of the buffer.</returns>
public T Front()
{
ThrowIfEmpty();
return _buffer[_start];
}
/// <summary>
/// Element at the back of the buffer - this[Size - 1].
/// </summary>
/// <returns>The value of the element of type T at the back of the buffer.</returns>
public T Back()
{
ThrowIfEmpty();
return _buffer[(_end != 0 ? _end : _count) - 1];
}
/// <summary>
/// Pushes a new element to the back of the buffer. Back()/this[Size-1]
/// will now return this element.
/// When the buffer is full, the element at Front()/this[0] will be
/// popped to allow for this new element to fit.
/// </summary>
/// <param name="item">Item to push to the back of the buffer</param>
public void PushBack(T item)
{
if (IsFull)
{
_buffer[_end] = item;
Increment(ref _end);
_start = _end;
}
else
{
_buffer[_end] = item;
Increment(ref _end);
++_count;
}
}
/// <summary>
/// Pushes a new element to the front of the buffer. Front()/this[0]
/// will now return this element.
/// When the buffer is full, the element at Back()/this[Size-1] will be
/// popped to allow for this new element to fit.
/// </summary>
/// <param name="item">Item to push to the front of the buffer</param>
public void PushFront(T item)
{
if (IsFull)
{
Decrement(ref _start);
_end = _start;
_buffer[_start] = item;
}
else
{
Decrement(ref _start);
_buffer[_start] = item;
++_count;
}
}
/// <summary>
/// Removes the element at the back of the buffer. Decreassing the
/// Buffer size by 1.
/// </summary>
public void PopBack()
{
ThrowIfEmpty("Cannot take elements from an empty buffer.");
Decrement(ref _end);
_buffer[_end] = default(T);
--_count;
}
/// <summary>
/// Removes the element at the front of the buffer. Decreassing the
/// Buffer size by 1.
/// </summary>
public void PopFront()
{
ThrowIfEmpty("Cannot take elements from an empty buffer.");
_buffer[_start] = default(T);
Increment(ref _start);
--_count;
}
/// <summary>
/// Copies the buffer contents to an array, acording to the logical
/// contents of the buffer (i.e. independent of the internal
/// order/contents)
/// </summary>
/// <returns>A new array with a copy of the buffer contents.</returns>
public T[] ToArray()
{
var newArray = new T[Count];
var newArrayOffset = 0;
var segments = new ArraySegment<T>[2] {ArrayOne(), ArrayTwo()};
foreach (var segment in segments)
{
Array.Copy(segment.Array, segment.Offset, newArray, newArrayOffset, segment.Count);
newArrayOffset += segment.Count;
}
return newArray;
}
private void ThrowIfEmpty(string message = "Cannot access an empty buffer.")
{
if (IsEmpty)
{
throw new InvalidOperationException(message);
}
}
/// <summary>
/// Increments the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Increment(ref int index)
{
if (++index == Capacity)
{
index = 0;
}
}
/// <summary>
/// Decrements the provided index variable by one, wrapping
/// around if necessary.
/// </summary>
/// <param name="index"></param>
private void Decrement(ref int index)
{
if (index == 0)
{
index = Capacity;
}
index--;
}
/// <summary>
/// Converts the index in the argument to an index in <code>_buffer</code>
/// </summary>
/// <returns>
/// The transformed index.
/// </returns>
/// <param name='index'>
/// External index.
/// </param>
private int InternalIndex(int index)
{
return _start + (index < (Capacity - _start) ? index : index - Capacity);
}
// doing ArrayOne and ArrayTwo methods returning ArraySegment<T> as seen here:
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1957cccdcb0c4ef7d80a34a990065818d
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b
// should help a lot with the code.
#region Array items easy access.
// The array is composed by at most two non-contiguous segments,
// the next two methods allow easy access to those.
private ArraySegment<T> ArrayOne()
{
if (_start <= _end)
{
return new ArraySegment<T>(_buffer, _start, _end - _start);
}
return new ArraySegment<T>(_buffer, _start, _buffer.Length - _start);
}
private ArraySegment<T> ArrayTwo()
{
if (_start < _end)
{
return new ArraySegment<T>(_buffer, _end, 0);
}
return new ArraySegment<T>(_buffer, 0, _end);
}
#endregion
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 39fd981a95abcb647a9c6ecd52007e71
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,4 @@
/*
* This file has been deleted.
* This empty file is left here to ensure it is properly overwritten when importing a new version of the package over an old version.
*/

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f809cd2d9d5ae074cbecca0ab4d472b6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,23 @@
using System;
using SRF;
using SRF.Service;
namespace SRDebugger.Internal
{
/// <summary>
/// The default bug report handler - this submits to the SRDebugger API using the API key configured in the SRDebugger
/// settings window.
/// </summary>
internal class InternalBugReporterHandler : IBugReporterHandler
{
public bool IsUsable
{
get { return Settings.Instance.EnableBugReporter && !string.IsNullOrWhiteSpace(Settings.Instance.ApiKey); }
}
public void Submit(BugReport report, Action<BugReportSubmitResult> onComplete, IProgress<float> progress)
{
BugReportApi.Submit(report, Settings.Instance.ApiKey, onComplete, progress);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f424b0a8b303d9843b15a6bf70ebf4ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using SRF.Service;
namespace SRDebugger.Internal
{
/// <summary>
/// Workaround for the debug panel not being initialized on startup.
/// SROptions needs to register itself but not cause auto-initialization.
/// This class buffers requests to register contains until there is a handler in place to deal with them.
/// Once the handler is in place, all buffered requests are passed in and future requests invoke the handler directly.
/// </summary>
[Service(typeof(InternalOptionsRegistry))]
public sealed class InternalOptionsRegistry
{
private List<object> _registeredContainers = new List<object>();
private Action<object> _handler;
public void AddOptionContainer(object obj)
{
if (_handler != null)
{
_handler(obj);
return;
}
_registeredContainers.Add(obj);
}
public void SetHandler(Action<object> action)
{
_handler = action;
foreach (object o in _registeredContainers)
{
_handler(o);
}
_registeredContainers = null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12f482545d1833a4e823d79898cb46ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,174 @@
namespace SRDebugger.Internal
{
using System;
using System.Collections.Generic;
using System.Linq;
using SRF;
using UI.Controls;
using UI.Controls.Data;
using UnityEngine;
using Object = UnityEngine.Object;
public static class OptionControlFactory
{
private static IList<DataBoundControl> _dataControlPrefabs;
private static ActionControl _actionControlPrefab;
private static readonly Dictionary<OptionType, DataBoundControl> TypeCache = new Dictionary<OptionType, DataBoundControl>();
public static bool CanCreateControl(OptionDefinition from)
{
PopulateDataControlPrefabs();
if (from.Property != null)
{
return TryGetDataControlPrefab(from) != null;
}
else
{
return _actionControlPrefab != null;
}
}
/// <summary>
/// Create a control from an <c>OptionDefinition</c>, optionally providing <paramref name="categoryPrefix" /> to remove
/// the category name from the start of the control.
/// </summary>
/// <param name="from"></param>
/// <param name="categoryPrefix"></param>
/// <returns></returns>
public static OptionsControlBase CreateControl(OptionDefinition from, string categoryPrefix = null)
{
PopulateDataControlPrefabs();
if (from.Property != null)
{
return CreateDataControl(from, categoryPrefix);
}
if (from.Method != null)
{
return CreateActionControl(from, categoryPrefix);
}
throw new Exception("OptionDefinition did not contain property or method.");
}
private static void PopulateDataControlPrefabs()
{
if (_dataControlPrefabs == null)
{
_dataControlPrefabs = Resources.LoadAll<DataBoundControl>(SRDebugPaths.DataControlsResourcesPath);
}
if (_actionControlPrefab == null)
{
_actionControlPrefab =
Resources.LoadAll<ActionControl>(SRDebugPaths.DataControlsResourcesPath).FirstOrDefault();
}
if (_actionControlPrefab == null)
{
Debug.LogError("[SRDebugger.Options] Cannot find ActionControl prefab.");
}
}
private static ActionControl CreateActionControl(OptionDefinition from, string categoryPrefix = null)
{
var control = SRInstantiate.Instantiate(_actionControlPrefab);
if (control == null)
{
Debug.LogWarning("[SRDebugger.OptionsTab] Error creating action control from prefab");
return null;
}
control.SetMethod(from.Name, from.Method);
control.Option = from;
return control;
}
private static DataBoundControl CreateDataControl(OptionDefinition from, string categoryPrefix = null)
{
var prefab = TryGetDataControlPrefab(from);
if (prefab == null)
{
Debug.LogWarning(
"[SRDebugger.OptionsTab] Can't find data control for type {0}".Fmt(from.Property.PropertyType));
return null;
}
var instance = SRInstantiate.Instantiate(prefab);
try
{
var n = from.Name;
// Remove category name from the start of the property name
if (!string.IsNullOrEmpty(categoryPrefix) && n.StartsWith(categoryPrefix))
{
n = n.Substring(categoryPrefix.Length);
}
instance.Bind(n, from.Property);
instance.Option = from;
}
catch (Exception e)
{
Debug.LogError("[SRDebugger.Options] Error binding to property {0}".Fmt(from.Name));
Debug.LogException(e);
Object.Destroy(instance);
instance = null;
}
return instance;
}
private static DataBoundControl TryGetDataControlPrefab(OptionDefinition from)
{
OptionType type = new OptionType(@from.Property.PropertyType, !@from.Property.CanWrite);
DataBoundControl control;
if (!TypeCache.TryGetValue(type, out control))
{
control = _dataControlPrefabs.FirstOrDefault(p =>
p.CanBind(@from.Property.PropertyType, !@from.Property.CanWrite));
TypeCache.Add(type, control);
}
return control;
}
private struct OptionType
{
public readonly Type Type;
public readonly bool IsReadyOnly;
public OptionType(Type type, bool isReadyOnly)
{
Type = type;
IsReadyOnly = isReadyOnly;
}
public bool Equals(OptionType other)
{
return Equals(Type, other.Type) && IsReadyOnly == other.IsReadyOnly;
}
public override bool Equals(object obj)
{
return obj is OptionType other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return ((Type != null ? Type.GetHashCode() : 0) * 397) ^ IsReadyOnly.GetHashCode();
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0d6e807b7446052409d51e03ab38cfae
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,93 @@
using System;
using SRF.Helpers;
namespace SRDebugger
{
/// <summary>
/// Class describing how an option should be presented within the options panel.
/// </summary>
public sealed class OptionDefinition
{
/// <summary>
/// Display-name for the option.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// The category that this option should be placed within.
/// </summary>
public string Category { get; private set; }
/// <summary>
/// Sort order within the category. Order is low to high, (options with lower SortPriority will appear before
/// options with higher SortPriority).
/// </summary>
public int SortPriority { get; private set; }
/// <summary>
/// Whether this option is a method that should be invoked.
/// </summary>
public bool IsMethod
{
get { return Method != null; }
}
/// <summary>
/// Whether this option is a property that has a value.
/// </summary>
public bool IsProperty
{
get { return Property != null; }
}
/// <summary>
/// The underlying method for this OptionDefinition.
/// Can be null if <see cref="IsMethod"/> is false.
/// </summary>
public SRF.Helpers.MethodReference Method { get; private set; }
/// <summary>
/// The underlying property for this OptionDefinition.
/// Can be null if <see cref="IsProperty"/> is false.
/// </summary>
public SRF.Helpers.PropertyReference Property { get; private set; }
private OptionDefinition(string name, string category, int sortPriority)
{
Name = name;
Category = category;
SortPriority = sortPriority;
}
public OptionDefinition(string name, string category, int sortPriority, SRF.Helpers.MethodReference method)
: this(name, category, sortPriority)
{
Method = method;
}
public OptionDefinition(string name, string category, int sortPriority, SRF.Helpers.PropertyReference property)
: this(name, category, sortPriority)
{
Property = property;
}
public static OptionDefinition FromMethod(string name, Action callback, string category = "Default", int sortPriority = 0)
{
return new OptionDefinition(name, category, sortPriority, callback);;
}
/// <summary>
/// Create an option definition from a setter and getter lambda.
/// </summary>
/// <param name="name">Name to display in options menu.</param>
/// <param name="getter">Method to get the current value of the property.</param>
/// <param name="setter">Method to set the value of the property (can be null if read-only)</param>
/// <param name="category">Category to display the option in.</param>
/// <param name="sortPriority">Sort priority to arrange the option within the category.</param>
/// <returns>The created option definition.</returns>
public static OptionDefinition Create<T>(string name, Func<T> getter, Action<T> setter = null, string category = "Default", int sortPriority = 0)
{
return new OptionDefinition(name, category, sortPriority, PropertyReference.FromLambda(getter, setter));
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c6fb9bd71eceb4145a013bb7fe025c01
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,14 @@
namespace SRDebugger.Internal
{
public static class SRDebugPaths
{
public const string DataControlsResourcesPath = "SRDebugger/UI/Prefabs/Options";
public const string TriggerPrefabPath = "SRDebugger/UI/Prefabs/Trigger";
public const string DebugPanelPrefabPath = "SRDebugger/UI/Prefabs/DebugPanel";
public const string PinnedUIPrefabPath = "SRDebugger/UI/Prefabs/PinnedUI";
public const string DockConsolePrefabPath = "SRDebugger/UI/Prefabs/DockConsole";
public const string PinEntryPrefabPath = "SRDebugger/UI/Prefabs/PinEntry";
public const string BugReportPopoverPath = "SRDebugger/UI/Prefabs/BugReportPopover";
public const string BugReportSheetPath = "SRDebugger/UI/Prefabs/BugReportSheet";
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 487af04ceed59b6409168c2d466c2f73
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,73 @@
#if UNITY_EDITOR
using UnityEngine;
namespace SRDebugger.Scripts.Internal
{
/// <summary>
/// Behaviour that supports SRDebugger reloading itself after a script recompile is detected.
/// </summary>
public class SRScriptRecompileHelper : MonoBehaviour
{
private static SRScriptRecompileHelper _instance;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
if (_instance != null)
{
return;
}
var go = new GameObject("SRDebugger Script Recompile Helper (Editor Only)");
DontDestroyOnLoad(go);
go.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
go.AddComponent<SRScriptRecompileHelper>();
}
private bool _hasEnabled;
private bool _srdebuggerHasInitialized;
void OnEnable()
{
if(_instance != null)
{
Destroy(gameObject);
return;
}
_instance = this;
// Don't take any action on the first OnEnable()
if (!_hasEnabled)
{
_hasEnabled = true;
return;
}
// Next OnEnable() will be due to script reload.
AutoInitialize.OnLoadBeforeScene();
if (_srdebuggerHasInitialized)
{
Debug.Log("[SRScriptRecompileHelper] Restoring SRDebugger after script reload.", this);
SRDebug.Init();
}
}
void OnApplicationQuit()
{
// Destroy this object when leaving play mode (otherwise it will linger and a new instance will be created next time play mode is entered).
Destroy(gameObject);
}
public static void SetHasInitialized()
{
if (_instance == null)
{
Initialize();
}
_instance._srdebuggerHasInitialized = true;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5cb2b7903b1672544b02d628ac0c1dfd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 24000
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,122 @@
namespace SRDebugger.Internal
{
using Services;
using SRF.Service;
public static class Service
{
private static IConsoleService _consoleService;
private static IDebugPanelService _debugPanelService;
private static IDebugTriggerService _debugTriggerService;
private static IPinnedUIService _pinnedUiService;
private static IDebugCameraService _debugCameraService;
private static IOptionsService _optionsService;
private static IDockConsoleService _dockConsoleService;
#if UNITY_EDITOR && ((!UNITY_2017 && !UNITY_2018 && !UNITY_2019) || UNITY_2019_3_OR_NEWER)
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
public static void RuntimeInitialize()
{
// Clear service references at startup in case of "enter play mode without domain reload"
_consoleService = null;
_debugPanelService = null;
_debugTriggerService = null;
_pinnedUiService = null;
_debugCameraService = null;
_optionsService = null;
_dockConsoleService = null;
}
#endif
public static IConsoleService Console
{
get
{
if (_consoleService == null)
{
_consoleService = SRServiceManager.GetService<IConsoleService>();
}
return _consoleService;
}
}
public static IDockConsoleService DockConsole
{
get
{
if (_dockConsoleService == null)
{
_dockConsoleService = SRServiceManager.GetService<IDockConsoleService>();
}
return _dockConsoleService;
}
}
public static IDebugPanelService Panel
{
get
{
if (_debugPanelService == null)
{
_debugPanelService = SRServiceManager.GetService<IDebugPanelService>();
}
return _debugPanelService;
}
}
public static IDebugTriggerService Trigger
{
get
{
if (_debugTriggerService == null)
{
_debugTriggerService = SRServiceManager.GetService<IDebugTriggerService>();
}
return _debugTriggerService;
}
}
public static IPinnedUIService PinnedUI
{
get
{
if (_pinnedUiService == null)
{
_pinnedUiService = SRServiceManager.GetService<IPinnedUIService>();
}
return _pinnedUiService;
}
}
public static IDebugCameraService DebugCamera
{
get
{
if (_debugCameraService == null)
{
_debugCameraService = SRServiceManager.GetService<IDebugCameraService>();
}
return _debugCameraService;
}
}
public static IOptionsService Options
{
get
{
if (_optionsService == null)
{
_optionsService = SRServiceManager.GetService<IOptionsService>();
}
return _optionsService;
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fe213cfca7e22e844abfd16c258dd08e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,24 @@
namespace SRDebugger.Internal
{
public class SRDebugStrings
{
public static readonly SRDebugStrings Current = new SRDebugStrings();
public readonly string Console_MessageTruncated = "-- Message Truncated --";
public readonly string Console_NoStackTrace = "-- No Stack Trace Available --";
public readonly string PinEntryPrompt = "Enter code to open debug panel";
public readonly string Profiler_DisableProfilerInfo =
"Unity profiler is currently <b>enabled</b>. Disable to improve performance.";
public readonly string Profiler_EnableProfilerInfo =
"Unity profiler is currently <b>disabled</b>. Enable to show more information.";
public readonly string Profiler_NoProInfo =
"Unity profiler is currently <b>disabled</b>. Unity Pro is required to enable it.";
public readonly string Profiler_NotSupported = "Unity profiler is <b>not supported</b> in this build.";
public readonly string ProfilerCameraListenerHelp =
"This behaviour is attached by the SRDebugger profiler to calculate render times.";
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 34bc872e688c2eb4ca1794728e6ff4fd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,233 @@
using System.Diagnostics;
namespace SRDebugger.Internal
{
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using SRF.Helpers;
using UnityEngine;
using UnityEngine.EventSystems;
public static class SRDebuggerUtil
{
public static bool IsMobilePlatform
{
get
{
if (Application.isMobilePlatform)
{
return true;
}
switch (Application.platform)
{
#if UNITY_5 || UNITY_5_3_OR_NEWER
case RuntimePlatform.WSAPlayerARM:
case RuntimePlatform.WSAPlayerX64:
case RuntimePlatform.WSAPlayerX86:
#else
case RuntimePlatform.MetroPlayerARM:
case RuntimePlatform.MetroPlayerX64:
case RuntimePlatform.MetroPlayerX86:
#endif
return true;
default:
return false;
}
}
}
/// <summary>
/// If no event system exists, create one
/// </summary>
/// <returns>True if the event system was created as a result of this call</returns>
public static bool EnsureEventSystemExists()
{
if (!Settings.Instance.EnableEventSystemGeneration)
{
return false;
}
if (EventSystem.current != null)
{
return false;
}
var e = Object.FindObjectOfType<EventSystem>();
// Check if EventSystem is in the scene but not registered yet
if (e != null && e.gameObject.activeSelf && e.enabled)
{
return false;
}
Debug.LogWarning("[SRDebugger] No EventSystem found in scene - creating a default one. Disable this behaviour in Window -> SRDebugger -> Settings Window -> Advanced)");
CreateDefaultEventSystem();
return true;
}
public static void CreateDefaultEventSystem()
{
var go = new GameObject("EventSystem (Created by SRDebugger, disable in Window -> SRDebugger -> Settings Window -> Advanced)");
go.AddComponent<EventSystem>();
#if ENABLE_INPUT_SYSTEM && ENABLE_LEGACY_INPUT_MANAGER
switch (Settings.Instance.UIInputMode)
{
case Settings.UIModes.NewInputSystem:
AddInputSystem(go);
Debug.LogWarning("[SRDebugger] Automatically generated EventSystem is using Unity Input System (can be changed to use Legacy Input in Window -> SRDebugger -> Settings Window -> Advanced)");
break;
case Settings.UIModes.LegacyInputSystem:
AddLegacyInputSystem(go);
Debug.LogWarning("[SRDebugger] Automatically generated EventSystem is using Legacy Input (can be changed to use Unity Input System in Window -> SRDebugger -> Settings Window -> Advanced)");
break;
}
#elif ENABLE_INPUT_SYSTEM
AddInputSystem(go);
#elif ENABLE_LEGACY_INPUT_MANAGER || (!ENABLE_INPUT_SYSTEM && !UNITY_2019_3_OR_NEWER)
AddLegacyInputSystem(go);
#endif
}
#if ENABLE_INPUT_SYSTEM
private static void AddInputSystem(GameObject go)
{
go.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
// Disable/re-enable to force some initialization.
// fix for input not being recognized until component is toggled off then on
go.SetActive(false);
go.SetActive(true);
}
#endif
#if ENABLE_LEGACY_INPUT_MANAGER || (!ENABLE_INPUT_SYSTEM && !UNITY_2019_3_OR_NEWER)
private static void AddLegacyInputSystem(GameObject go)
{
go.AddComponent<StandaloneInputModule>();
}
#endif
/// <summary>
/// Scan <paramref name="obj" /> for valid options and return a collection of them.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static List<OptionDefinition> ScanForOptions(object obj)
{
var options = new List<OptionDefinition>();
#if NETFX_CORE
var members = obj.GetType().GetTypeInfo().DeclaredMembers;
#else
var members =
obj.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty |
BindingFlags.SetProperty | BindingFlags.InvokeMethod);
#endif
var ignoreAssembly = typeof(MonoBehaviour).Assembly;
foreach (var memberInfo in members)
{
// Skip any properties that are from built-in Unity types (e.g. Behaviour, MonoBehaviour)
if (memberInfo.DeclaringType != null && memberInfo.DeclaringType.Assembly == ignoreAssembly)
{
continue;
}
var browsable = memberInfo.GetCustomAttribute<BrowsableAttribute>();
if (browsable != null)
{
if (!browsable.Browsable)
continue;
}
// Find user-specified category name from attribute
var categoryAttribute = SRReflection.GetAttribute<CategoryAttribute>(memberInfo);
var category = categoryAttribute == null ? "Default" : categoryAttribute.Category;
// Find user-specified sorting priority from attribute
var sortAttribute = SRReflection.GetAttribute<SortAttribute>(memberInfo);
var sortPriority = sortAttribute == null ? 0 : sortAttribute.SortPriority;
// Find user-specified display name from attribute
var nameAttribute = SRReflection.GetAttribute<DisplayNameAttribute>(memberInfo);
var name = nameAttribute == null ? memberInfo.Name : nameAttribute.DisplayName;
if (memberInfo is PropertyInfo)
{
var propertyInfo = memberInfo as PropertyInfo;
// Only allow properties with public read/write
#if NETFX_CORE
if(propertyInfo.GetMethod == null)
continue;
// Ignore static members
if (propertyInfo.GetMethod.IsStatic)
continue;
#else
if (propertyInfo.GetGetMethod() == null)
{
continue;
}
// Ignore static members
if ((propertyInfo.GetGetMethod().Attributes & MethodAttributes.Static) != 0)
{
continue;
}
#endif
options.Add(new OptionDefinition(name, category, sortPriority,
new SRF.Helpers.PropertyReference(obj, propertyInfo)));
}
else if (memberInfo is MethodInfo)
{
var methodInfo = memberInfo as MethodInfo;
if (methodInfo.IsStatic)
{
continue;
}
// Skip methods with parameters or non-void return type
if (methodInfo.ReturnType != typeof (void) || methodInfo.GetParameters().Length > 0)
{
continue;
}
options.Add(new OptionDefinition(name, category, sortPriority,
new SRF.Helpers.MethodReference(obj, methodInfo)));
}
}
return options;
}
public static string GetNumberString(int value, int max, string exceedsMaxString)
{
if (value >= max)
{
return exceedsMaxString;
}
return value.ToString();
}
public static void ConfigureCanvas(Canvas canvas)
{
if (Settings.Instance.UseDebugCamera)
{
canvas.worldCamera = Service.DebugCamera.Camera;
canvas.renderMode = RenderMode.ScreenSpaceCamera;
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0e0f09d4b116f524381461f865525607
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData: