提交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,44 @@
using SRDebugger.Services;
using SRF.Service;
namespace SRDebugger
{
using UnityEngine;
public static class AutoInitialize
{
#if UNITY_2018
private const RuntimeInitializeLoadType InitializeLoadType = RuntimeInitializeLoadType.BeforeSceneLoad;
#else
private const RuntimeInitializeLoadType InitializeLoadType = RuntimeInitializeLoadType.SubsystemRegistration;
#endif
/// <summary>
/// Initialize the console service before the scene has loaded to catch more of the initialization log.
/// </summary>
[RuntimeInitializeOnLoadMethod(InitializeLoadType)]
public static void OnLoadBeforeScene()
{
// Populate service manager with types from SRDebugger assembly (asmdef)
SRServiceManager.RegisterAssembly<IDebugService>();
if (Settings.Instance.IsEnabled)
{
// Initialize console if it hasn't already initialized.
SRServiceManager.GetService<IConsoleService>();
}
}
/// <summary>
/// Initialize SRDebugger after the scene has loaded.
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
public static void OnLoad()
{
if (Settings.Instance.IsEnabled)
{
SRDebug.Init();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d7261eeb6c603844ba967b14c0895ec8
timeCreated: 1426170121
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 3b75fb5ec74e4fa4ca7d8472defae919
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,101 @@
#if !DISABLE_SRDEBUGGER
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Text;
using SRF;
using UnityEngine;
using SRDebugger.Internal;
namespace SRDebugger.Editor
{
static class ApiSignup
{
public delegate void ApiSignupResultCallback(bool success, string apiKey, string email, string error);
public static void SignUp(string email, string invoiceNo, ApiSignupResultCallback resultCallback)
{
var requestData = new Hashtable();
requestData["emailAddress"] = email;
requestData["transactionNumber"] = invoiceNo;
try
{
var request = SendRequest(SRDebugApi.EndPoint + "/user/create", requestData, WebRequestMethods.Http.Post);
string result;
var didSucceed = SRDebugApiUtil.ReadResponse(request, out result);
if (didSucceed)
{
var jsonTable = (Dictionary<string, object>) Json.Deserialize(result);
resultCallback(true, (string) jsonTable["apiKey"], (string) jsonTable["emailAddress"], null);
}
else
{
resultCallback(false, null, null, SRDebugApiUtil.ParseErrorResponse(result));
}
}
catch (Exception e)
{
Debug.LogException(e);
resultCallback(false, null, null, "Internal Error (" + e.Message + ")");
}
}
public static string Verify(string apiKey)
{
var request = SendRequest(SRDebugApi.EndPoint + "/user/test", null, apiKey: apiKey);
string result;
SRDebugApiUtil.ReadResponse(request, out result);
return result;
}
private static HttpWebRequest SendRequest(string endpoint, Hashtable data,
string method = WebRequestMethods.Http.Get, string apiKey = null)
{
var request = (HttpWebRequest) WebRequest.Create(endpoint);
request.Timeout = 15000;
request.Method = method;
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
request.ContentType = "application/json";
request.Accept = "application/json";
if (!string.IsNullOrEmpty(apiKey))
{
request.Headers.Add("X-ApiKey", apiKey);
}
request.KeepAlive = false;
if (data != null)
{
var requestJson = Json.Serialize(data);
using (var requestStream = request.GetRequestStream())
{
using (var writer = new StreamWriter(requestStream, Encoding.UTF8))
{
writer.Write(requestJson);
}
}
}
return request;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,42 @@
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
class ApiSignupTermsWindow : EditorWindow
{
public static void Open()
{
GetWindowWithRect<ApiSignupTermsWindow>(new Rect(0, 0, 430, 345), true, "SRDebugger - Bug Reporter TOS",
true);
}
private void OnGUI()
{
GUILayout.Label("Terms and Conditions", SRInternalEditorUtil.Styles.HeaderLabel);
GUILayout.Label(
"The Bug Reporter service is provided free of charge to owners of SRDebugger. One valid license key of SRDebugger allows one account to be registered. You must not share your API key with another party. Stompy Robot LTD reserves the right to terminate your bug reporter account if your API key is shared with another party.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label(
"Stompy Robot LTD reserves the right to cancel the bug report service at any time without notice.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label(
"By signing up for the Bug Reporter service you grant Stompy Robot LTD permission to gather non-identifying information from users when submitting reports. You attest that your users are aware of the data collection and give their consent.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label(
"THE SERVICE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("Close"))
{
Close();
}
}
}
}

View File

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

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace SRDebugger.Editor
{
abstract class IntegrityIssue
{
private readonly string _title;
private readonly string _description;
private List<Fix> _fixes;
public string Title
{
get { return _title; }
}
public string Description
{
get { return _description; }
}
public IList<Fix> GetFixes()
{
if (_fixes == null)
{
_fixes = CreateFixes().ToList();
}
return _fixes;
}
protected IntegrityIssue(string title, string description)
{
_title = title;
_description = description;
}
protected abstract IEnumerable<Fix> CreateFixes();
}
abstract class Fix
{
private readonly string _name;
private readonly string _description;
private readonly bool _isAutoFix;
public string Name
{
get { return _name; }
}
public string Description
{
get { return _description; }
}
public bool IsAutoFix
{
get { return _isAutoFix; }
}
protected Fix(string name, string description, bool isAutoFix)
{
_name = name;
_description = description;
_isAutoFix = isAutoFix;
}
public abstract void Execute();
}
class DelegateFix : Fix
{
private readonly Action _fixMethod;
public DelegateFix(string name, string description, Action fixMethod) : base(name, description, true)
{
_fixMethod = fixMethod;
}
public override void Execute()
{
_fixMethod();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fab9c606533743f6bfcb59f8392b1a0c
timeCreated: 1611921984

View File

@@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SRF;
using UnityEngine;
using UnityEditor;
#pragma warning disable 162
namespace SRDebugger.Editor
{
[InitializeOnLoad]
static class Migrations
{
static Migrations()
{
RunMigrations();
}
private const bool EnableLog = false;
public class Migration
{
public readonly string Id;
public readonly string[] ObsoleteFiles;
public Migration(string id, string[] obsoleteFiles)
{
Id = id;
ObsoleteFiles = obsoleteFiles;
}
}
public static List<Migration> AvailableMigrations = new List<Migration>()
{
new Migration("DeleteOldEditorResources", new[]
{
"Editor/Resources/SRDebugger/BG_Dark.png",
"Editor/Resources/SRDebugger/BG_Light.png",
"Editor/Resources/SRDebugger/DemoSprite.png",
"Editor/Resources/SRDebugger/Logo_DarkBG.png",
"Editor/Resources/SRDebugger/Logo_LightBG.png",
"Editor/Resources/SRDebugger/WelcomeLogo_DarkBG.png",
"Editor/Resources/SRDebugger/WelcomeLogo_LightBG.png",
"Editor/Resources/SRDebugger/Icons/Dark/console-25.png",
"Editor/Resources/SRDebugger/Icons/Dark/options-25.png",
"Editor/Resources/SRDebugger/Icons/Dark/profiler-25.png",
"Editor/Resources/SRDebugger/Icons/Light/console-25.png",
"Editor/Resources/SRDebugger/Icons/Light/options-25.png",
"Editor/Resources/SRDebugger/Icons/Light/profiler-25.png",
})
};
public static void RunMigrations(bool forceRun = false)
{
if(EnableLog)
Debug.Log("[SRDebugger] Running Migrations...");
foreach (var m in AvailableMigrations)
{
var key = GetProjectPrefsKey(m.Id);
if (!forceRun && EditorPrefs.GetBool(key, false))
{
continue;
}
EditorPrefs.SetBool(key, true);
RunMigration(m);
}
}
public static void RunMigration(Migration migration)
{
if (EnableLog)
Debug.Log("Running Migration: " + migration.Id);
var assetPaths = AssetDatabase.GetAllAssetPaths();
var root = new DirectoryInfo(SRInternalEditorUtil.GetRootPath());
if(EnableLog)
Debug.Log("Using Root Path: " + root.FullName);
var obsoleteAssets = migration.ObsoleteFiles.Select(p => root + "/" + p).ToList();
var deleteQueue = assetPaths.Where(assetPath => obsoleteAssets.Contains(assetPath)).ToList();
if (deleteQueue.Count == 0)
return;
var message = "The following files used by a previous version of SRDebugger are obsolete and can be safely deleted: \n\n" +
deleteQueue.Aggregate((s1, s2) => s1 + "\n" + s2);
Debug.Log(message);
message += "\n\nIt is recommended to delete these files.";
if (EditorUtility.DisplayDialog("SRDebugger Migration Assistant",
message, "Delete Now", "Ignore"))
{
foreach (var s in deleteQueue)
{
Debug.Log("[SRDebugger] Deleting Asset {0}".Fmt(s));
if (!AssetDatabase.DeleteAsset(s))
{
Debug.LogWarning("[SRDebugger] Error deleting asset {0}".Fmt(s));
}
}
Debug.Log("[SRDebugger] Migration Complete");
}
else
{
EditorUtility.DisplayDialog("SRDebugger Migration Assitant",
"You can run this migration check again via the \"Run Migrations\" button in the advanced tab of the SRDebugger settings window.", "OK");
}
}
private static string GetProjectPrefsKey(string key)
{
return "SRDebugger_Migration_" + Application.dataPath + "_" + key;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4b80f539792faff4187cbd7fb1113d67
timeCreated: 1451840376
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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: c38092124e5b1114d9f037f64769dc5f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
public partial class SRDebugEditor
{
/// <summary>
/// Sets compiler define <paramref name="define"/> to be enabled/disabled on all build targets.
/// </summary>
static void SetCompileDefine(string define, bool enabled)
{
foreach (BuildTargetGroup targetGroup in GetAllBuildTargetGroups())
{
// Use hash set to remove duplicates.
List<string> defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup).Split(';').ToList();
bool alreadyExists = false;
for (var i = 0; i < defines.Count; i++)
{
if (string.Equals(define, defines[i], StringComparison.InvariantCultureIgnoreCase))
{
alreadyExists = true;
if (!enabled)
{
defines.RemoveAt(i);
}
}
}
if (!alreadyExists && enabled)
{
defines.Add(define);
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, string.Join(";", defines.ToArray()));
}
}
static void ForceRecompile()
{
AssetDatabase.ImportAsset(SRInternalEditorUtil.GetAssetPath("StompyRobot.SRDebugger.asmdef"), ImportAssetOptions.ForceUpdate);
}
static IEnumerable<BuildTargetGroup> GetAllBuildTargetGroups()
{
Type enumType = typeof(BuildTargetGroup);
string[] names = Enum.GetNames(enumType);
Array values = Enum.GetValues(enumType);
for (var i = 0; i < names.Length; i++)
{
string name = names[i];
BuildTargetGroup value = (BuildTargetGroup)values.GetValue(i);
if (value == BuildTargetGroup.Unknown) continue;
MemberInfo[] member = enumType.GetMember(name);
MemberInfo entry = member.FirstOrDefault(p => p.DeclaringType == enumType);
if (entry == null)
{
Debug.LogErrorFormat(
"[SRDebugger] Unhandled build target: {0}. SRDebugger disabled state may not be applied correctly to this platform.",
name);
continue;
}
if (entry.GetCustomAttributes(typeof(ObsoleteAttribute), true).Length != 0)
{
// obsolete, ignore.
continue;
}
yield return value;
}
}
}
}

View File

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

View File

@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
public partial class SRDebugEditor
{
internal const string DisabledDirectoryPostfix = "_DISABLED~";
// Paths to enable/disable (relative to SRDebugger root directory)
private static readonly string[] _resourcePaths = new[]
{
"Resources",
"usr",
"UI/Prefabs"
};
static void SetResourcesEnabled(bool enable)
{
AssetDatabase.StartAssetEditing();
foreach (ResourceDirectory d in GetResourcePaths())
{
d.SetDirectoryEnabled(enable);
}
AssetDatabase.StopAssetEditing();
AssetDatabase.Refresh();
AssetDatabase.ImportAsset(SRInternalEditorUtil.GetRootPath(
), ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate);
}
internal static IEnumerable<ResourceDirectory> GetResourcePaths()
{
foreach (string resourcePath in _resourcePaths)
{
string enabledPath = Path.Combine(SRInternalEditorUtil.GetRootPath(), resourcePath);
string disabledPath = Path.Combine(SRInternalEditorUtil.GetRootPath(), resourcePath) + DisabledDirectoryPostfix;
yield return new ResourceDirectory(enabledPath, disabledPath);
}
}
internal class ResourceDirectory
{
public readonly string EnabledPath;
public readonly string DisabledPath;
public readonly string EnabledPathMetaFile;
public readonly string DisabledPathMetaFile;
public readonly string DisabledPathBackupMetaFile;
public bool IsEnabled
{
get { return Directory.Exists(EnabledPath); }
}
public bool IsDisabled
{
get { return Directory.Exists(DisabledPath); }
}
public ResourceDirectory(string enabledPath, string disabledPath)
{
EnabledPath = enabledPath;
DisabledPath = disabledPath;
EnabledPathMetaFile = enabledPath + ".meta";
DisabledPathMetaFile = disabledPath + ".meta";
DisabledPathBackupMetaFile = disabledPath + ".meta.bak~";
}
public void SetDirectoryEnabled(bool enable)
{
if (IsEnabled && enable)
{
return;
}
if (IsDisabled && !enable)
{
return;
}
if (IsEnabled && IsDisabled)
{
// TODO
throw new Exception();
}
string title = string.Format("SRDebugger - {0} Resources", enable ? "Enable" : "Disable");
string oldPath = enable ? DisabledPath : EnabledPath;
string newPath = enable ? EnabledPath : DisabledPath;
bool useAssetDatabase = !enable;
string error = null;
if (useAssetDatabase)
{
error = AssetDatabase.MoveAsset(oldPath, newPath);
if (!string.IsNullOrEmpty(error))
{
if (EditorUtility.DisplayDialog(title, GetErrorMessage(enable, error), "Force Move", "Abort"))
{
useAssetDatabase = false;
}
}
}
if (!useAssetDatabase)
{
try
{
Directory.Move(oldPath, newPath);
}
catch (Exception e)
{
Debug.LogError("Error moving directory");
Debug.LogException(e);
error = "Exception occurred, see console for details.";
}
}
if (!string.IsNullOrEmpty(error))
{
string message = string.Format(
"An error occurred while attempting to {3} SRDebugger resource directory.\n\n Old Path: {0}\n New Path: {1}\n\n Error: \n{2}",
EnabledPath, DisabledPath, error, enable ? "enable" : "disable");
EditorUtility.DisplayDialog(title, message, "Continue");
return;
}
if (!enable)
{
// Disable meta files
if (File.Exists(DisabledPathMetaFile))
{
if (File.Exists(DisabledPathBackupMetaFile))
{
File.Delete(DisabledPathBackupMetaFile);
}
File.Move(DisabledPathMetaFile, DisabledPathBackupMetaFile);
}
}
else
{
// Enable backed up meta files
if (File.Exists(DisabledPathBackupMetaFile))
{
if (File.Exists(EnabledPathMetaFile))
{
File.Delete(EnabledPathMetaFile);
}
File.Move(DisabledPathBackupMetaFile, EnabledPathMetaFile);
}
}
}
private string GetErrorMessage(bool enable, string error)
{
return string.Format(
"An error occurred while attempting to {3} SRDebugger resources. \n\n Old Path: {0}\n New Path: {1}\n\n Error: \n{2}",
EnabledPath, DisabledPath, error, enable ? "enable" : "disable");
}
}
}
}

View File

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

View File

@@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
public static partial class SRDebugEditor
{
internal const string DisableSRDebuggerCompileDefine = "DISABLE_SRDEBUGGER";
/// <summary>
/// Is SRDebugger currently enabled or disabled.
/// </summary>
public static readonly bool IsEnabled =
#if DISABLE_SRDEBUGGER
false
#else
true
#endif
;
/// <summary>
/// Set SRDebugger to be enabled or disabled.
/// This is a synchronous operation, which means calling this as part of a build pipeline should be possible.
/// </summary>
/// <param name="enable"></param>
public static void SetEnabled(bool enable)
{
if (EditorApplication.isPlaying || EditorApplication.isCompiling)
{
Debug.LogError(
"[SRDebugger.SetEnabled] Can't change SRDebugger enabled state while in play mode or compiling scripts.");
throw new InvalidOperationException(
"Can't change SRDebugger enabled state while in play mode or compiling scripts.");
}
#if !DISABLE_SRDEBUGGER
AssetDatabase.SaveAssets(); // In case any pending changes to files about to be moved
// Try and unload the settings asset to prevent errors later (harmless error, but annoying)
SRInternalEditorUtil.EditorSettings.ClearCache();
GC.Collect();
EditorUtility.UnloadUnusedAssetsImmediate(true);
#endif
AssetDatabase.ReleaseCachedFileHandles();
SetCompileDefine(DisableSRDebuggerCompileDefine, !enable);
SetResourcesEnabled(enable);
ForceRecompile();
}
/// <summary>
/// Runs through a series of integrity checks that are fast to perform.
/// </summary>
internal static IEnumerable<IntegrityIssue> QuickIntegrityCheck()
{
int enabledCount = 0;
int disabledCount = 0;
foreach (ResourceDirectory directory in GetResourcePaths())
{
if (directory.IsEnabled && directory.IsDisabled)
{
yield return new SomeResourcesAreEnabledAndDisabledIntegrityIssue();
yield break;
}
if (directory.IsEnabled) enabledCount++;
if (directory.IsDisabled) disabledCount++;
}
if (enabledCount > 0 && disabledCount > 0)
{
#if DISABLE_SRDEBUGGER
yield return new SomeResourcesEnabledIntegrityIssue();
#else
yield return new SomeResourcesDisabledIntegrityIssue();
#endif
yield break; // Don't do any further resource-related checks.
}
if (!IsEnabled && enabledCount > 0)
{
yield return new ScriptsDisabledButResourcesEnabled();
}
if (IsEnabled && disabledCount > 0)
{
yield return new ScriptsEnabledButResourcesDisabled();
}
}
internal static void DrawDisabledWindowGui(ref bool isWorking)
{
SRInternalEditorUtil.BeginDrawBackground();
SRInternalEditorUtil.DrawLogo(SRInternalEditorUtil.GetLogo());
SRInternalEditorUtil.EndDrawBackground();
// Draw header/content divider
EditorGUILayout.BeginVertical(SRInternalEditorUtil.Styles.SettingsHeaderBoxStyle);
EditorGUILayout.EndVertical();
GUILayout.Label("SRDebugger Disabled", SRInternalEditorUtil.Styles.InspectorHeaderStyle);
GUILayout.Label(
"SRDebugger is currently disabled. SRDebugger must be enabled in order to access editor features.",
SRInternalEditorUtil.Styles.ParagraphLabel);
EditorGUILayout.HelpBox("Enabling SRDebugger will result in the tools being included in all builds of your game until it is disabled again.", MessageType.Warning);
GUILayout.Label("• "+ DisableSRDebuggerCompileDefine + " compiler define will be removed from all build configurations.", SRInternalEditorUtil.Styles.ListBulletPoint);
GUILayout.Label("• Disabled SRDebugger folders will be renamed so Unity imports them.", SRInternalEditorUtil.Styles.ListBulletPoint);
GUILayout.Label("• You can disable SRDebugger again at any time.", SRInternalEditorUtil.Styles.ListBulletPoint);
if (isWorking && !EditorApplication.isCompiling && !EditorApplication.isUpdating)
{
isWorking = false;
}
if (isWorking)
{
using (new EditorGUI.DisabledGroupScope(true))
{
GUILayout.Button("Working...");
}
}
else if (GUILayout.Button("Enable SRDebugger"))
{
isWorking = true;
try
{
SetEnabled(true);
}
catch (Exception)
{
isWorking = false;
throw;
}
}
}
#if DISABLE_SRDEBUGGER
class SomeResourcesEnabledIntegrityIssue : IntegrityIssue
{
private new const string Title = "Some SRDebugger resources are enabled.";
private new const string Description =
"SRDebugger is disabled, but some SRDebugger resource directories are enabled. \n\n" +
"This can occur if an unhandled error occurs while SRDebugger is being enabled or disabled, or if the resource directories are modified by hand.";
public SomeResourcesEnabledIntegrityIssue() : base(Title, Description)
{
}
protected override IEnumerable<Fix> CreateFixes()
{
yield return new DelegateFix(
"Disable all SRDebugger resources",
"All resource directories will be disabled.",
() => { SetResourcesEnabled(false); });
yield return new DelegateFix(
"Enable SRDebugger",
"Fully enable SRDebugger (activate scripts and enable resources).",
() => { SetEnabled(true); });
}
}
#else
class SomeResourcesDisabledIntegrityIssue : IntegrityIssue
{
private new const string Title = "Some SRDebugger resources are disabled.";
private new const string Description =
"SRDebugger is enabled, but some SRDebugger resource directories are disabled. \n\n" +
"This can occur if an unhandled error occurs while SRDebugger is being enabled or disabled, or if the resource directories are modified by hand.";
public SomeResourcesDisabledIntegrityIssue() : base(Title, Description)
{
}
protected override IEnumerable<Fix> CreateFixes()
{
yield return new DelegateFix(
"Enable all SRDebugger resources",
"All resource directories will be enabled.",
() => { SetResourcesEnabled(true); });
yield return new DelegateFix(
"Disable SRDebugger",
"Fully disable SRDebugger (deactivate scripts, exclude all resources from builds of your game).",
() => { SetEnabled(false); });
}
}
#endif
class SomeResourcesAreEnabledAndDisabledIntegrityIssue : IntegrityIssue
{
private new const string Title = "Duplicate SRDebugger resource directories";
private new const string Description =
"Some SRDebugger resource directories exist in both an enabled and disabled state. \n\n" +
"This can occur if a new version of SRDebugger is installed while SRDebugger is disabled, or if an unhandled error occurs while SRDebugger is being enabled/disabled.";
public SomeResourcesAreEnabledAndDisabledIntegrityIssue() : base(Title, Description)
{
}
protected override IEnumerable<Fix> CreateFixes()
{
if (!IsEnabled)
{
var deletePaths = GetResourcePaths().Where(p => p.IsDisabled && p.IsEnabled).ToList();
string paths = " - " + string.Join("\n - ", deletePaths
.SelectMany(p => new string[] { p.DisabledPath, p.DisabledPathBackupMetaFile }).ToArray());
yield return new DelegateFix(
"Keep enabled resources, disable SRDebugger",
"If you have just installed a new version of SRDebugger, this will keep the most up-to-date resources from the imported package. SRDebugger will be disabled after the old resources are deleted. \n\n The following paths will be deleted: \n\n" + paths,
() =>
{
foreach (ResourceDirectory rd in GetResourcePaths())
{
if (rd.IsEnabled && rd.IsDisabled)
{
Debug.Log("[SRDebugger] Delete Path: " + rd.DisabledPath);
Directory.Delete(rd.DisabledPath, true);
Debug.Log("[SRDebugger] Delete File: " + rd.DisabledPathBackupMetaFile);
File.Delete(rd.DisabledPathBackupMetaFile);
}
}
SetEnabled(false);
});
}
}
}
class ScriptsDisabledButResourcesEnabled : IntegrityIssue
{
private new const string Title = "SRDebugger resources are enabled while scripts are disabled";
private new const string Description =
"SRDebugger's resources directories are enabled, but SRDebugger scripts are disabled. \n" +
"This can occur if the resource directories or if the C# compile defines are modified manually.";
public ScriptsDisabledButResourcesEnabled() : base(Title, Description)
{
}
protected override IEnumerable<Fix> CreateFixes()
{
yield return new DelegateFix(
"Enable SRDebugger scripts",
"Remove compiler define (" + DisableSRDebuggerCompileDefine + ") SRDebugger can be disabled again from the settings menu.",
() =>
{
SetCompileDefine(DisableSRDebuggerCompileDefine, false);
});
yield return new DelegateFix(
"Disable SRDebugger resources",
"Resources will no longer be included in builds of your game (you can enable SRDebugger from the settings menu later)",
() =>
{
SetResourcesEnabled(false);
});
}
}
class ScriptsEnabledButResourcesDisabled : IntegrityIssue
{
private new const string Title = "SRDebugger scripts are enabled while resources are disabled.";
private new const string Description =
"SRDebugger resources directories are disabled, but SRDebugger scripts are still enabled. \n" +
"This can occur if the resource directories or C# compile defines are modified manually.";
public ScriptsEnabledButResourcesDisabled() : base(Title, Description)
{
}
protected override IEnumerable<Fix> CreateFixes()
{
yield return new DelegateFix(
"Disable SRDebugger scripts",
"Add compiler define (" + DisableSRDebuggerCompileDefine + ") to disable SRDebugger scripts (you can re-enable SRDebugger from the settings menu later)",
() =>
{
SetCompileDefine(DisableSRDebuggerCompileDefine, true);
});
yield return new DelegateFix(
"Enable SRDebugger resources",
"Resources will be included in builds of your game (you can disable SRDebugger from the settings menu later)",
() =>
{
SetResourcesEnabled(true);
});
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca60d3f1302b47e1a3c6f43ed06dedcf
timeCreated: 1611413144

View File

@@ -0,0 +1,11 @@
namespace SRDebugger.Editor
{
static class SRDebugEditorPaths
{
public const string EditorLogoPath = "UI/Sprites/Default/Logo.psd";
public const string SettingsMenuItemPath = "Window/SRDebugger/Settings Window";
public const string WelcomeItemPath = "Window/SRDebugger/Welcome Guide";
public const string SROptionsMenuItemPath = "Window/SRDebugger/SROptions Window";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6eeabd6854ed440ab38998300a297c0b
timeCreated: 1611414784

View File

@@ -0,0 +1,43 @@
namespace SRDebugger.Editor
{
class SRDebugEditorStrings
{
public static readonly SRDebugEditorStrings Current = new SRDebugEditorStrings();
public readonly string SettingsIsEnabledTooltip =
"If false, SRDebugger.Init prefab will not load SRDebugger. Manual calls to SRDebug.Instance.ShowDebugPanel() will still work.";
public readonly string SettingsAutoLoadTooltip =
"Automatically load SRDebugger when the game loads, even if SRDebugger.Init prefab is not present.";
public readonly string SettingsDefaultTabTooltip =
"Visible tab when panel is first opened.";
public readonly string SettingsKeyboardShortcutsTooltip =
"Enable Keyboard Shortcuts";
public readonly string SettingsCloseOnEscapeTooltip =
"Close debug panel when Escape is pressed.";
public readonly string SettingsKeyboardModifersTooltip =
"Modifier keys that must be held for keyboard shortcuts to execute.";
public readonly string SettingsDebugCameraTooltip =
"UI will render to a camera instead of overlaying the entire scene.";
public readonly string SettingsRateBoxContents =
"If you like SRDebugger, please consider leaving a rating on the Asset Store.";
public readonly string SettingsWebSiteUrl = "https://www.stompyrobot.uk/tools/srdebugger";
public readonly string SettingsAssetStoreUrl = "http://u3d.as/aZc";
public readonly string SettingsDocumentationUrl = "https://www.stompyrobot.uk/tools/srdebugger/documentation";
public readonly string SettingsSupportUrl =
"http://forum.unity3d.com/threads/srdebugger-debug-and-tweak-your-game-while-on-device-released.296403/";
public readonly string SettingsEnabledTabsDescription =
"Deselect any tabs that you do not wish to be available in the debug panel.";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cd05741ba3454346864f3c28fcdce56f
timeCreated: 1611414505

View File

@@ -0,0 +1,183 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
[InitializeOnLoad]
class SRIntegrityCheckWindow : EditorWindow
{
private List<IntegrityIssue> _results;
private Vector2 _scrollPosition;
private bool _applyingFix;
private static bool _isOpen;
static SRIntegrityCheckWindow()
{
// Delay call to prevent any UI stalls after compile complete.
EditorApplication.delayCall += () =>
{
if (!_isOpen && SRDebugEditor.QuickIntegrityCheck().Any())
{
Debug.Log("[SRDebugger] Some issues have been detected with SRDebugger, opening integrity check window.");
Open();
}
};
}
public static void Open()
{
var window = GetWindow<SRIntegrityCheckWindow>(true, "SRDebugger Integrity Check", true);
window.minSize = new Vector2(640, 400);
window.Show();
}
private void OnEnable()
{
_isOpen = true;
RefreshIntegrityCheck();
}
private void OnDisable()
{
_isOpen = false;
}
public void RefreshIntegrityCheck()
{
_results = SRDebugEditor.QuickIntegrityCheck().ToList();
}
private void OnGUI()
{
// Draw header area
SRInternalEditorUtil.BeginDrawBackground();
SRInternalEditorUtil.DrawLogo(SRInternalEditorUtil.GetLogo());
SRInternalEditorUtil.EndDrawBackground();
// Draw header/content divider
EditorGUILayout.BeginVertical(SRInternalEditorUtil.Styles.SettingsHeaderBoxStyle);
EditorGUILayout.EndVertical();
GUILayout.Label(
"SRDebugger automatically scans your project to find common issues with the SRDebugger installation.");
EditorGUILayout.Space();
// TODO: Enable button when there are some more 'expensive' integrity checks. For now no point as alt the checks are really quick
if (GUILayout.Button("Refresh"))
{
RefreshIntegrityCheck();
}
if (_applyingFix)
{
if (!EditorApplication.isCompiling && !EditorApplication.isUpdating)
{
_applyingFix = false;
RefreshIntegrityCheck();
}
EditorGUI.BeginDisabledGroup(_applyingFix);
}
EditorGUILayout.Space();
if (_results == null)
{
_results = new List<IntegrityIssue>();
}
EditorGUILayout.TextArea("Issues Detected: " + _results.Count, EditorStyles.boldLabel);
EditorGUILayout.Separator();
EditorGUILayout.Space();
if (_results.Count == 0)
{
EditorGUILayout.HelpBox("No issues have been found!", MessageType.None);
}
else
{
EditorGUILayout.HelpBox("It is highly recommended to backup your project before using this tool.", MessageType.Warning);
_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, false, false,
GUILayout.Width(position.width));
DrawIssuesList();
EditorGUILayout.EndScrollView();
}
if (_applyingFix)
{
EditorGUI.EndDisabledGroup();
}
}
private void DrawIssuesList()
{
EditorGUILayout.BeginVertical();
for (var i = 0; i < _results.Count; i++)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(_results[i].Title, EditorStyles.boldLabel);
GUILayout.Label(_results[i].Description, SRInternalEditorUtil.Styles.ParagraphLabel);
var fixes = _results[i].GetFixes();
if (fixes.Count > 0)
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
GUILayout.Label("Possible Fixes:", EditorStyles.miniBoldLabel);
foreach (Fix fix in fixes)
{
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
GUILayout.Space(10);
EditorGUILayout.BeginVertical();
GUILayout.Label(fix.Name, EditorStyles.boldLabel);
GUILayout.Label(fix.Description, SRInternalEditorUtil.Styles.ParagraphLabelItalic);
if (fix.IsAutoFix && GUILayout.Button("Apply Fix", GUILayout.Width(90)))
{
fix.Execute();
_applyingFix = true;
}
GUILayout.Space(2);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
}
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d3667acfb91436880aa7f35bcc5fe43
timeCreated: 1611919957

View File

@@ -0,0 +1,707 @@
using SRF;
namespace SRDebugger.Editor
{
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
static class SRInternalEditorUtil
{
// Path to this file from the root path
private const string TestPath = "SRDebugger/README.txt";
private static GUIStyle _bgStyle;
private static Texture2D _logoTexture;
private static Texture2D _welcomeLogoTexture;
private static Texture2D _bgTexture;
private static GUIStyle _middleAlign;
/// <summary>
/// Finds the path to the SRDebugger installation folder (e.g. Assets/StompyRobot/SRDebugger)
/// </summary>
/// <returns></returns>
public static string GetRootPath()
{
// Find assets that match this file name
var potentialAssets = AssetDatabase.FindAssets("README");
foreach (var potentialAsset in potentialAssets)
{
var path = AssetDatabase.GUIDToAssetPath(potentialAsset);
if (path.Contains(TestPath))
{
var rootPath = Path.GetDirectoryName(path);
return rootPath;
}
}
throw new Exception("Unable to find SRDebugger root path. Please ensure the README file in StompyRobot/SRDebugger still exists.");
}
/// <summary>
/// Finds the path to an SRDebugger asset relative to the installation root.
/// </summary>
/// <param name="relativeToRoot"></param>
/// <returns></returns>
public static string GetAssetPath(string relativeToRoot)
{
if (!relativeToRoot.StartsWith("/"))
{
relativeToRoot = "/" + relativeToRoot;
}
var p = GetRootPath() + relativeToRoot;
return p;
}
public static T LoadResource<T>(string path) where T : UnityEngine.Object
{
var p = GetAssetPath(path);
//Debug.Log("[SRDebugger] Loading " + p);
var asset = AssetDatabase.LoadAssetAtPath(p, typeof(T));
return asset as T;
}
#if !DISABLE_SRDEBUGGER
public enum SettingsResult
{
Cache,
Loaded,
Waiting,
Error
}
public static class EditorSettings
{
internal const string SettingsFilePath = "/usr/Resources/SRDebugger/Settings.asset";
internal const string DisabledSettingsFilePath = "/usr" + SRDebugEditor.DisabledDirectoryPostfix + "/Resources/SRDebugger/Settings.asset";
private static Settings _instance;
public static SettingsResult TryGetOrCreate(out Settings instance, out string message)
{
if (_instance != null)
{
instance = _instance;
message = string.Empty;
return SettingsResult.Cache;
}
try
{
SettingsResult result = InternalTryGetOrCreateSettings(out _instance, out message);
instance = _instance;
return result;
}
catch (Exception e)
{
instance = null;
message = e.ToString();
return SettingsResult.Error;
}
}
public static void ClearCache()
{
Settings.ClearCache(); // Just in case runtime settings are loaded.
if (_instance == null)
{
return;
}
var instance = _instance;
_instance = null;
Resources.UnloadAsset(instance);
instance = null;
GC.Collect();
EditorUtility.UnloadUnusedAssetsImmediate();
Resources.UnloadUnusedAssets();
}
private static SettingsResult InternalTryGetOrCreateSettings(out Settings instance, out string message)
{
instance = null;
message = null;
if (EditorApplication.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode)
{
message = "Settings can only be modified while in edit-mode. Exit play mode to make changes to SRDebugger settings.";
return SettingsResult.Waiting;
}
// If compiling, wait for that to finish. We might be disabling/enabling SRDebugger and don't want to recreate the settings while that is in progress.
if (EditorApplication.isCompiling || EditorApplication.isUpdating)
{
message = "Waiting for Unity to finish compiling/updating...";
return SettingsResult.Waiting;
}
// Check if there is a 'disabled' settings file, we don't want to create a new settings file if a disabled one exists.
string disabledSettingsFile = SRInternalEditorUtil.GetAssetPath(DisabledSettingsFilePath);
if (File.Exists(disabledSettingsFile))
{
message = "A settings file already exists but is disabled. Please ensure SRDebugger is correctly enabled or disabled.";
return SettingsResult.Error;
}
// Get resources folder path
var settingsAssetPath = SRInternalEditorUtil.GetAssetPath(SettingsFilePath);
// Load existing asset.
if (File.Exists(settingsAssetPath))
{
instance = AssetDatabase.LoadAssetAtPath<Settings>(settingsAssetPath);
if (instance == null)
{
message = "Error loading settings asset.";
return SettingsResult.Error;
}
return SettingsResult.Loaded;
}
Debug.Log("[SRDebugger] Creating settings asset at {0}".Fmt(settingsAssetPath));
instance = ScriptableObject.CreateInstance<Settings>();
string containingDirectory = Path.GetDirectoryName(settingsAssetPath);
if (containingDirectory == null)
{
message = "Error finding target settings directory.";
return SettingsResult.Error;
}
// Create directory if it doesn't exist
Directory.CreateDirectory(containingDirectory);
// Save instance if in editor
AssetDatabase.CreateAsset(instance, settingsAssetPath);
return SettingsResult.Loaded;
}
}
#endif
public static Texture2D GetLogo()
{
if (_logoTexture != null)
{
return _logoTexture;
}
return
_logoTexture =
LoadResource<Texture2D>("Editor/Logo_" + (EditorGUIUtility.isProSkin ? "DarkBG" : "LightBG") +
".png");
}
public static Texture2D GetWelcomeLogo()
{
if (_welcomeLogoTexture != null)
{
return _welcomeLogoTexture;
}
return
_welcomeLogoTexture =
LoadResource<Texture2D>("Editor/WelcomeLogo_" +
(EditorGUIUtility.isProSkin ? "DarkBG" : "LightBG") + ".png");
}
public static Texture2D GetBackground()
{
if (_bgTexture != null)
{
return _bgTexture;
}
return
_bgTexture =
LoadResource<Texture2D>("Editor/BG_" + (EditorGUIUtility.isProSkin ? "Dark" : "Light") + ".png");
}
public static void DrawLogo(Texture2D logo)
{
if (logo == null)
{
Debug.LogError("Error loading SRDebugger logo");
return;
}
#if !DISABLE_SRDEBUGGER
var rect =
#endif
EditorGUILayout.BeginVertical();
GUILayout.Space(15);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.DrawTexture(
GUILayoutUtility.GetRect(logo.width, logo.width, logo.height, logo.height, GUILayout.ExpandHeight(false),
GUILayout.ExpandWidth(false)),
logo);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.Space(15);
EditorGUILayout.EndVertical();
#if !DISABLE_SRDEBUGGER
var size = EditorStyles.miniLabel.CalcSize(new GUIContent(SRDebug.Version));
GUI.Label(new Rect(rect.xMax - size.x, rect.yMax - size.y, size.x, size.y), SRDebug.Version,
EditorStyles.miniLabel);
#endif
}
public static bool DrawInspectorFoldout(bool isVisible, string content)
{
isVisible = EditorGUILayout.Foldout(isVisible, content, Styles.InspectorHeaderFoldoutStyle);
EditorGUILayout.Separator();
return isVisible;
}
public static void BeginDrawBackground()
{
if (_bgStyle == null)
{
_bgStyle = new GUIStyle();
_bgStyle.margin = _bgStyle.padding = new RectOffset(0, 0, 0, 0);
}
var rect = EditorGUILayout.BeginVertical(_bgStyle);
DrawTextureTiled(rect, GetBackground());
}
public static void EndDrawBackground()
{
EditorGUILayout.EndVertical();
}
public static void DrawTextureTiled(Rect rect, Texture2D tex)
{
GUI.BeginGroup(rect);
var tilesX = Mathf.Max(1, Mathf.CeilToInt(rect.width / tex.width));
var tilesY = Mathf.Max(1, Mathf.CeilToInt(rect.height / tex.height));
for (var x = 0; x < tilesX; x++)
{
for (var y = 0; y < tilesY; y++)
{
var pos = new Rect(x * tex.width, y * tex.height, tex.width, tex.height);
pos.x += rect.x;
pos.y += rect.y;
GUI.DrawTexture(pos, tex, ScaleMode.ScaleAndCrop);
}
}
GUI.EndGroup();
}
public static bool ClickableLabel(string text, GUIStyle style)
{
var rect = EditorGUILayout.BeginVertical(Styles.NoPaddingNoMargin);
GUILayout.Label(text, style);
EditorGUILayout.EndVertical();
if (Event.current.type == EventType.MouseUp && rect.Contains(Event.current.mousePosition))
{
return true;
}
return false;
}
#if !DISABLE_SRDEBUGGER
public static void DrawLayoutPreview(Rect rect, Settings settings)
{
const int profilerWidth = 120;
const int profilerHeight = 70;
const int optionsWidth = 150;
const int optionsHeight = 36;
if (_middleAlign == null)
{
_middleAlign = new GUIStyle(EditorStyles.helpBox);
_middleAlign.alignment = TextAnchor.MiddleCenter;
}
var iconPath = "Editor/Icons/" + (EditorGUIUtility.isProSkin ? "Light" : "Dark");
const float consoleHeight = 90;
GUI.Box(rect, "", EditorStyles.helpBox);
var consoleAlignment = settings.ConsoleAlignment;
var consoleRect = new Rect(rect.x,
consoleAlignment == ConsoleAlignment.Top ? rect.y : rect.yMax - consoleHeight, rect.width,
consoleHeight);
GUI.Box(consoleRect, new GUIContent(LoadResource<Texture2D>(iconPath + "/console-25.png"), "Console"),
_middleAlign);
var workRect = rect;
if (consoleAlignment == ConsoleAlignment.Top)
{
workRect.yMin += consoleHeight;
}
else
{
workRect.yMax -= consoleHeight;
}
var opAlignment = settings.OptionsAlignment;
var proAlignment = settings.ProfilerAlignment;
GUI.Box(GetAlignedRect(profilerWidth, profilerHeight, proAlignment, workRect),
new GUIContent(LoadResource<Texture2D>(iconPath + "/profiler-25.png"), "Profiler"), _middleAlign);
var optionsRect = workRect;
if ((opAlignment == PinAlignment.TopCenter && proAlignment == PinAlignment.TopLeft) || (opAlignment == PinAlignment.BottomCenter && proAlignment == PinAlignment.BottomLeft))
{
optionsRect.x += profilerWidth;
optionsRect.width -= profilerWidth;
}
else if ((opAlignment == PinAlignment.TopCenter && proAlignment == PinAlignment.TopRight) ||
opAlignment == PinAlignment.BottomCenter && proAlignment == PinAlignment.BottomRight)
{
optionsRect.width -= profilerWidth;
}
GUI.Box(GetAlignedRect(optionsWidth, optionsHeight, opAlignment, optionsRect),
new GUIContent(LoadResource<Texture2D>(iconPath + "/options-25.png"), "Pinned Options"), _middleAlign);
if (settings.EnableTrigger != Settings.TriggerEnableModes.Off)
{
GUI.Box(GetAlignedRect(25, 25, settings.TriggerPosition, rect),
new GUIContent("", "Entry Trigger"),
_middleAlign);
}
}
private static Rect GetAlignedRect(int width, int height, PinAlignment alignment, Rect workRect)
{
var rect = new Rect(0, 0, width, height);
if (alignment == PinAlignment.BottomLeft || alignment == PinAlignment.BottomRight || alignment == PinAlignment.BottomCenter)
{
rect.position = new Vector2(0, workRect.height - rect.height);
}
else if (alignment == PinAlignment.CenterLeft || alignment == PinAlignment.CenterRight)
{
rect.position = new Vector2(0, workRect.height / 2 - rect.height / 2);
}
if (alignment == PinAlignment.TopRight || alignment == PinAlignment.BottomRight || alignment == PinAlignment.CenterRight)
{
rect.position += new Vector2(workRect.width - rect.width, 0);
}
else if (alignment == PinAlignment.TopCenter || alignment == PinAlignment.BottomCenter)
{
rect.position += new Vector2(workRect.width / 2 - rect.width / 2, 0);
}
rect.position += workRect.position;
return rect;
}
#endif
public static void RenderGif(Rect pos, Texture2D map, int frameNo, int frameWidth, int frameHeight, int perLine,
int paddingX = 0, int paddingY = 0)
{
var x = frameNo % perLine;
var y = Mathf.FloorToInt((float)frameNo / perLine);
var xCoord = x * (frameWidth + paddingX);
var yCoord = (y + 1) * (frameHeight + paddingY);
var texCoords = new Rect(
xCoord / (float)map.width,
(map.height - yCoord) / (float)map.height,
(frameWidth) / (float)map.width,
(frameHeight) / (float)map.height);
GUI.DrawTextureWithTexCoords(pos, map, texCoords);
//Debug.Log(texCoords);
//Debug.Log("x: " + x + ", y: " + y);
}
public static void DrawFooterLayout(float width)
{
EditorGUILayout.BeginHorizontal();
var margin = (EditorStyles.miniButton.padding.left) / 2f;
width = width - margin * 2;
if (GUILayout.Button("Web Site", GUILayout.Width(width / 2f - margin)))
{
Application.OpenURL(SRDebugEditorStrings.Current.SettingsWebSiteUrl);
}
if (GUILayout.Button("Asset Store Page", GUILayout.Width(width / 2f - margin)))
{
Application.OpenURL(SRDebugEditorStrings.Current.SettingsAssetStoreUrl);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Documentation", GUILayout.Width(width / 2f - margin)))
{
Application.OpenURL(SRDebugEditorStrings.Current.SettingsDocumentationUrl);
}
if (GUILayout.Button("Support", GUILayout.Width(width / 2f - margin)))
{
Application.OpenURL(
SRDebugEditorStrings.Current.SettingsSupportUrl);
}
EditorGUILayout.EndHorizontal();
}
public static class Styles
{
private static GUIStyle _inspectorHeaderStyle;
private static GUIStyle _inspectorHeaderFoldoutStyle;
private static GUIStyle _settingsHeaderBoxStyle;
private static GUIStyle _headerLabel;
private static GUIStyle _paragraphLabel;
private static GUIStyle _paragraphLabelItalic;
private static GUIStyle _radioButtonDescription;
private static GUIStyle _radioButton;
private static GUIStyle _leftToggleButton;
private static GUIStyle _noPaddingNoMargin;
private static GUIStyle _richTextLabel;
private static GUIStyle _listBulletPoint;
public static string LinkColour
{
get
{
if (EditorGUIUtility.isProSkin)
{
return "#7C8CB9";
}
return "#0032E6";
}
}
public static GUIStyle InspectorHeaderStyle
{
get
{
if (_inspectorHeaderStyle == null)
{
_inspectorHeaderStyle = new GUIStyle(EditorStyles.boldLabel);
_inspectorHeaderStyle.fontSize = 12;
}
return _inspectorHeaderStyle;
}
}
public static GUIStyle InspectorHeaderFoldoutStyle
{
get
{
if (_inspectorHeaderFoldoutStyle == null)
{
_inspectorHeaderFoldoutStyle = new GUIStyle(EditorStyles.foldout);
_inspectorHeaderFoldoutStyle.fontSize = 12;
_inspectorHeaderFoldoutStyle.fontStyle = FontStyle.Bold;
}
return _inspectorHeaderFoldoutStyle;
}
}
public static GUIStyle SettingsHeaderBoxStyle
{
get
{
if (_settingsHeaderBoxStyle == null)
{
_settingsHeaderBoxStyle = new GUIStyle("OL Title");
_settingsHeaderBoxStyle.padding = new RectOffset(0, 0, 0, 0);
_settingsHeaderBoxStyle.margin = new RectOffset(0, 0, 0, 0);
_settingsHeaderBoxStyle.clipping = TextClipping.Clip;
_settingsHeaderBoxStyle.overflow = new RectOffset(0, 0, 0, 0);
//_settingsHeaderBoxStyle.border = new RectOffset(1, 1, 1, 1);
_settingsHeaderBoxStyle.fixedHeight = 0.5f;
}
return _settingsHeaderBoxStyle;
}
}
public static GUIStyle HeaderLabel
{
get
{
if (_headerLabel == null)
{
_headerLabel = new GUIStyle(EditorStyles.largeLabel);
_headerLabel.fontSize = 18;
_headerLabel.fontStyle = FontStyle.Normal;
_headerLabel.margin = new RectOffset(5, 5, 5, 5);
}
return _headerLabel;
}
}
public static GUIStyle ParagraphLabel
{
get
{
if (_paragraphLabel == null)
{
_paragraphLabel = new GUIStyle(EditorStyles.label);
_paragraphLabel.margin = new RectOffset(5, 5, 5, 5);
_paragraphLabel.wordWrap = true;
_paragraphLabel.richText = true;
}
return _paragraphLabel;
}
}
public static GUIStyle ParagraphLabelItalic
{
get
{
if (_paragraphLabelItalic == null)
{
_paragraphLabelItalic = new GUIStyle(EditorStyles.label);
_paragraphLabelItalic.margin = new RectOffset(5, 5, 5, 5);
_paragraphLabelItalic.wordWrap = true;
_paragraphLabelItalic.richText = true;
_paragraphLabelItalic.fontStyle = FontStyle.Italic;
}
return _paragraphLabelItalic;
}
}
public static GUIStyle LeftToggleButton
{
get
{
if (_leftToggleButton == null)
{
_leftToggleButton = new GUIStyle(EditorStyles.label);
_leftToggleButton.contentOffset = new Vector2(_leftToggleButton.contentOffset.x + 5,
_leftToggleButton.contentOffset.y);
}
return _leftToggleButton;
}
}
public static GUIStyle RadioButton
{
get
{
if (_radioButton == null)
{
_radioButton = new GUIStyle(EditorStyles.radioButton);
_radioButton.contentOffset = new Vector2(_radioButton.contentOffset.x + 5,
_radioButton.contentOffset.y);
}
return _radioButton;
}
}
public static GUIStyle RadioButtonDescription
{
get
{
if (_radioButtonDescription == null)
{
_radioButtonDescription = new GUIStyle(ParagraphLabel);
_radioButtonDescription.padding.left = (int)RadioButton.contentOffset.x +
RadioButton.padding.left;
}
return _radioButtonDescription;
}
}
public static GUIStyle NoPaddingNoMargin
{
get
{
if (_noPaddingNoMargin == null)
{
_noPaddingNoMargin = new GUIStyle();
_noPaddingNoMargin.margin = new RectOffset(0, 0, 0, 0);
_noPaddingNoMargin.padding = new RectOffset(0, 0, 0, 0);
}
return _noPaddingNoMargin;
}
}
public static GUIStyle RichTextLabel
{
get
{
if (_richTextLabel == null)
{
_richTextLabel = new GUIStyle(EditorStyles.label);
_richTextLabel.richText = true;
_richTextLabel.margin = new RectOffset(2, 2, 0, 0);
}
return _richTextLabel;
}
}
public static GUIStyle ListBulletPoint
{
get
{
if (_listBulletPoint == null)
{
_listBulletPoint = new GUIStyle(EditorStyles.miniBoldLabel);
_listBulletPoint.wordWrap = true;
_listBulletPoint.margin = new RectOffset(6, 2, 0, 0);
}
return _listBulletPoint;
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f05cd73b58c9481593f90b04c3084b15
timeCreated: 1611414371

View File

@@ -0,0 +1,455 @@

namespace SRDebugger.Editor
{
using System;
using System.Collections.Generic;
using System.Linq;
using SRF;
using UnityEngine;
using UnityEditor;
using System.ComponentModel;
using SRF.Helpers;
#if !DISABLE_SRDEBUGGER
using Internal;
using SRDebugger.Services;
using UI.Controls.Data;
#endif
class SROptionsWindow : EditorWindow
{
[MenuItem(SRDebugEditorPaths.SROptionsMenuItemPath)]
public static void Open()
{
var window = GetWindow<SROptionsWindow>(false, "SROptions", true);
window.minSize = new Vector2(100, 100);
window.Show();
}
#if DISABLE_SRDEBUGGER
private bool _isWorking;
void OnGUI()
{
SRDebugEditor.DrawDisabledWindowGui(ref _isWorking);
}
#else
[Serializable]
private class CategoryState
{
public string Name;
public bool IsOpen;
}
[SerializeField]
private List<CategoryState> _categoryStates = new List<CategoryState>();
private Dictionary<Type, Action<OptionDefinition>> _typeLookup;
private Dictionary<string, List<OptionDefinition>> _options;
private Vector2 _scrollPosition;
private bool _queueRefresh;
private bool _isDirty;
[NonSerialized] private GUIStyle _divider;
[NonSerialized] private GUIStyle _foldout;
private IOptionsService _activeOptionsService;
public void OnInspectorUpdate()
{
if (EditorApplication.isPlaying && (_options == null || _isDirty))
{
Populate();
_queueRefresh = true;
_isDirty = false;
}
else if (!EditorApplication.isPlaying && _options != null)
{
Clear();
_queueRefresh = true;
}
if (_queueRefresh)
{
Repaint();
}
_queueRefresh = false;
}
private void OnDisable()
{
Clear();
}
void PopulateTypeLookup()
{
_typeLookup = new Dictionary<Type, Action<OptionDefinition>>()
{
{typeof(int), OnGUI_Int},
{typeof(float), OnGUI_Float},
{typeof(double), OnGUI_Double},
{typeof(string), OnGUI_String},
{typeof(bool), OnGUI_Boolean },
{typeof(uint), OnGUI_AnyInteger},
{typeof(ushort), OnGUI_AnyInteger},
{typeof(short), OnGUI_AnyInteger},
{typeof(sbyte), OnGUI_AnyInteger},
{typeof(byte), OnGUI_AnyInteger},
{typeof(long), OnGUI_AnyInteger},
};
}
void Clear()
{
_options = null;
_isDirty = false;
if (_activeOptionsService != null)
{
_activeOptionsService.OptionsUpdated -= OnOptionsUpdated;
}
_activeOptionsService = null;
}
void Populate()
{
if (_typeLookup == null)
{
PopulateTypeLookup();
}
if (_activeOptionsService != null)
{
_activeOptionsService.OptionsUpdated -= OnOptionsUpdated;
}
if (_options != null)
{
foreach (KeyValuePair<string, List<OptionDefinition>> kv in _options)
{
foreach (var option in kv.Value)
{
if (option.IsProperty)
{
option.Property.ValueChanged -= OnOptionPropertyValueChanged;
}
}
}
}
_options = new Dictionary<string, List<OptionDefinition>>();
foreach (var option in Service.Options.Options)
{
List<OptionDefinition> list;
if (!_options.TryGetValue(option.Category, out list))
{
list = new List<OptionDefinition>();
_options[option.Category] = list;
}
list.Add(option);
if (option.IsProperty)
{
option.Property.ValueChanged += OnOptionPropertyValueChanged;
}
}
foreach (var kv in _options)
{
kv.Value.Sort((d1, d2) => d1.SortPriority.CompareTo(d2.SortPriority));
}
_activeOptionsService = Service.Options;
_activeOptionsService.OptionsUpdated += OnOptionsUpdated;
}
private void OnOptionPropertyValueChanged(PropertyReference property)
{
_queueRefresh = true;
}
private void OnOptionsUpdated(object sender, EventArgs e)
{
_isDirty = true;
_queueRefresh = true;
}
void OnGUI()
{
EditorGUILayout.Space();
if (!EditorApplication.isPlayingOrWillChangePlaymode || _options == null)
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Label("SROptions can only be edited in play-mode.");
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
return;
}
if (_divider == null)
{
_divider = new GUIStyle(GUI.skin.box);
_divider.stretchWidth = true;
_divider.fixedHeight = 2;
}
if (_foldout == null)
{
_foldout = new GUIStyle(EditorStyles.foldout);
_foldout.fontStyle = FontStyle.Bold;
}
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
foreach (var kv in _options)
{
var state = _categoryStates.FirstOrDefault(p => p.Name == kv.Key);
if (state == null)
{
state = new CategoryState()
{
Name = kv.Key,
IsOpen = true
};
_categoryStates.Add(state);
}
state.IsOpen = EditorGUILayout.Foldout(state.IsOpen, kv.Key, _foldout);
if (!state.IsOpen)
continue;
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
OnGUI_Category(kv.Value);
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
void OnGUI_Category(List<OptionDefinition> options)
{
for (var i = 0; i < options.Count; i++)
{
var op = options[i];
if (op.Property != null)
{
OnGUI_Property(op);
} else if (op.Method != null)
{
OnGUI_Method(op);
}
}
}
void OnGUI_Method(OptionDefinition op)
{
if (GUILayout.Button(op.Name))
{
op.Method.Invoke(null);
}
}
void OnGUI_Property(OptionDefinition op)
{
Action<OptionDefinition> method;
if (op.Property.PropertyType.IsEnum)
{
method = OnGUI_Enum;
}
else if (!_typeLookup.TryGetValue(op.Property.PropertyType, out method))
{
OnGUI_Unsupported(op);
return;
}
if (!op.Property.CanWrite)
{
EditorGUI.BeginDisabledGroup(true);
}
method(op);
if (!op.Property.CanWrite)
{
EditorGUI.EndDisabledGroup();
}
}
void OnGUI_String(OptionDefinition op)
{
EditorGUI.BeginChangeCheck();
var newValue = EditorGUILayout.TextField(op.Name, (string) op.Property.GetValue());
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Boolean(OptionDefinition op)
{
EditorGUI.BeginChangeCheck();
var newValue = EditorGUILayout.Toggle(op.Name, (bool) op.Property.GetValue());
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Enum(OptionDefinition op)
{
EditorGUI.BeginChangeCheck();
var newValue = EditorGUILayout.EnumPopup(op.Name, (Enum)op.Property.GetValue());
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Int(OptionDefinition op)
{
var range = op.Property.GetAttribute<NumberRangeAttribute>();
int newValue;
EditorGUI.BeginChangeCheck();
if (range != null)
{
newValue = EditorGUILayout.IntSlider(op.Name, (int)op.Property.GetValue(), (int)range.Min, (int)range.Max);
}
else
{
newValue = EditorGUILayout.IntField(op.Name, (int) op.Property.GetValue());
}
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Float(OptionDefinition op)
{
var range = op.Property.GetAttribute<NumberRangeAttribute>();
float newValue;
EditorGUI.BeginChangeCheck();
if (range != null)
{
newValue = EditorGUILayout.Slider(op.Name, (float)op.Property.GetValue(), (float)range.Min, (float)range.Max);
}
else
{
newValue = EditorGUILayout.FloatField(op.Name, (float) op.Property.GetValue());
}
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Double(OptionDefinition op)
{
var range = op.Property.GetAttribute<NumberRangeAttribute>();
double newValue;
EditorGUI.BeginChangeCheck();
if (range != null && range.Min > float.MinValue && range.Max < float.MaxValue)
{
newValue = EditorGUILayout.Slider(op.Name, (float)op.Property.GetValue(), (float)range.Min, (float)range.Max);
}
else
{
newValue = EditorGUILayout.DoubleField(op.Name, (double) op.Property.GetValue());
if (range != null)
{
if (newValue > range.Max)
{
newValue = range.Max;
} else if (newValue < range.Min)
{
newValue = range.Min;
}
}
}
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_AnyInteger(OptionDefinition op)
{
NumberControl.ValueRange range;
if (!NumberControl.ValueRanges.TryGetValue(op.Property.PropertyType, out range))
{
Debug.LogError("Unknown integer type: " + op.Property.PropertyType);
return;
}
var userRange = op.Property.GetAttribute<NumberRangeAttribute>();
EditorGUI.BeginChangeCheck();
var oldValue = (long)Convert.ChangeType(op.Property.GetValue(), typeof(long));
var newValue = EditorGUILayout.LongField(op.Name, oldValue);
if (newValue > range.MaxValue)
{
newValue = (long)range.MaxValue;
} else if (newValue < range.MinValue)
{
newValue = (long)range.MinValue;
}
if (userRange != null)
{
if (newValue > userRange.Max)
{
newValue = (long)userRange.Max;
} else if (newValue < userRange.Min)
{
newValue = (long) userRange.Min;
}
}
if (EditorGUI.EndChangeCheck())
{
op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
}
}
void OnGUI_Unsupported(OptionDefinition op)
{
EditorGUILayout.PrefixLabel(op.Name);
EditorGUILayout.LabelField("Unsupported Type: {0}".Fmt(op.Property.PropertyType));
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c10ec5c3e9f1e404ba655cbc07340b94
timeCreated: 1465649914
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
#if !DISABLE_SRDEBUGGER
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
[CustomEditor(typeof (Settings))]
class SettingsEditor : UnityEditor.Editor
{
private bool _override;
public override void OnInspectorGUI()
{
SRInternalEditorUtil.DrawLogo(SRInternalEditorUtil.GetLogo());
GUILayout.Label(
"This asset contains the runtime settings used by SRDebugger. It is recommended that this asset be edited only via the SRDebugger Settings window.",
EditorStyles.wordWrappedLabel);
EditorGUILayout.Separator();
if (GUILayout.Button("Open SRDebugger Settings Window"))
{
SRDebuggerSettingsWindow.Open();
}
if (!_override)
{
if (GUILayout.Button("Override Warning"))
{
_override = true;
}
}
else
{
GUILayout.Label(
"You have been warned...",
EditorStyles.wordWrappedLabel);
}
EditorGUILayout.Separator();
if (_override)
{
base.OnInspectorGUI();
}
}
}
}
#endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3a6cf957f3eda7b429dd0eae2cfbb40c
timeCreated: 1441902550
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
{
"name": "StompyRobot.SRDebugger.Editor",
"references": [
"StompyRobot.SRDebugger",
"StompyRobot.SRF"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 07ab30a82c1032d45ad726f7216628e8
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,194 @@
#if !DISABLE_SRDEBUGGER
using SRF;
using UnityEditor;
using UnityEngine;
namespace SRDebugger.Editor
{
[InitializeOnLoad]
class WelcomeWindow : EditorWindow
{
private const string WelcomeWindowPlayerPrefsKey = "SRDEBUGGER_WELCOME_SHOWN_VERSION";
private Texture2D _demoSprite;
private Vector2 _scrollPosition;
static WelcomeWindow()
{
EditorApplication.update += OpenUpdate;
}
private static void OpenUpdate()
{
if (ShouldOpen())
{
Open();
}
EditorApplication.update -= OpenUpdate;
}
[MenuItem(SRDebugEditorPaths.WelcomeItemPath)]
public static void Open()
{
GetWindowWithRect<WelcomeWindow>(new Rect(0, 0, 449, 500), true, "SRDebugger - Welcome", true);
}
private static bool ShouldOpen()
{
var settings = Settings.GetInstance();
if (settings != null)
{
if (settings.DisableWelcomePopup)
{
return false;
}
}
var hasKey = EditorPrefs.HasKey(WelcomeWindowPlayerPrefsKey);
if (!hasKey)
{
return true;
}
var value = EditorPrefs.GetString(WelcomeWindowPlayerPrefsKey);
if (value != SRDebug.Version)
{
return true;
}
return false;
}
private void OnEnable()
{
EditorPrefs.SetString(WelcomeWindowPlayerPrefsKey, SRDebug.Version);
}
private void OnGUI()
{
// Draw header area
SRInternalEditorUtil.BeginDrawBackground();
SRInternalEditorUtil.DrawLogo(SRInternalEditorUtil.GetWelcomeLogo());
SRInternalEditorUtil.EndDrawBackground();
// Draw header/content divider
EditorGUILayout.BeginVertical(SRInternalEditorUtil.Styles.SettingsHeaderBoxStyle);
EditorGUILayout.EndVertical();
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
GUILayout.Label("Welcome", SRInternalEditorUtil.Styles.HeaderLabel);
GUILayout.Label(
"Thank you for purchasing SRDebugger, your support is very much appreciated and we hope you find it useful for your project. " +
"This window contains a quick guide to get to help get you started with SRDebugger.",
SRInternalEditorUtil.Styles.ParagraphLabel);
if (SRInternalEditorUtil.ClickableLabel(
"Note: For more detailed information <color={0}>click here</color> to visit the online documentation."
.Fmt(SRInternalEditorUtil.Styles.LinkColour),
SRInternalEditorUtil.Styles.ParagraphLabel))
{
Application.OpenURL(SRDebugEditorStrings.Current.SettingsDocumentationUrl);
}
GUILayout.Label("Quick Start", SRInternalEditorUtil.Styles.HeaderLabel);
GUILayout.Label(
"Now that you have imported the package, you should find the trigger available in the top-left of your game window when in play mode. " +
"Triple-clicking this trigger will bring up the debug panel. The trigger is hidden until clicked.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label(
"By default, SRDebugger loads automatically when your game starts. " +
"You can change this behaviour from the SRDebugger Settings window.",
SRInternalEditorUtil.Styles.ParagraphLabel);
DrawVideo();
EditorGUILayout.Space();
GUILayout.Label("Customization", SRInternalEditorUtil.Styles.HeaderLabel);
if (SRInternalEditorUtil.ClickableLabel(
"Many features of SRDebugger can be configured from the <color={0}>SRDebugger Settings</color> window."
.Fmt(
SRInternalEditorUtil.Styles.LinkColour), SRInternalEditorUtil.Styles.ParagraphLabel))
{
SRDebuggerSettingsWindow.Open();
}
GUILayout.Label(
"From the settings window you can configure loading behaviour, trigger position, docked tools layout, and more. " +
"You can enable the bug reporter service by using the sign-up form to get a free API key.",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label("What Next?", SRInternalEditorUtil.Styles.HeaderLabel);
if (SRInternalEditorUtil.ClickableLabel(
"For more detailed information about SRDebugger's features or details about the Options Tab and script API, check the <color={0}>online documentation</color>."
.Fmt(SRInternalEditorUtil.Styles.LinkColour), SRInternalEditorUtil.Styles.ParagraphLabel))
{
Application.OpenURL(SRDebugEditorStrings.Current.SettingsDocumentationUrl);
}
GUILayout.Label(
"Thanks again for purchasing SRDebugger. " +
"If you find it useful please consider leaving a rating or review on the Asset Store page as this helps us continue to provide updates and support to our users. ",
SRInternalEditorUtil.Styles.ParagraphLabel);
GUILayout.Label(
"If you have any questions or concerns please do not hesitate to get in touch with us via email or the Unity forums.",
SRInternalEditorUtil.Styles.ParagraphLabel);
SRInternalEditorUtil.DrawFooterLayout(position.width - 15);
EditorGUILayout.EndScrollView();
Repaint();
}
private void DrawVideo()
{
if (_demoSprite == null)
{
_demoSprite = SRInternalEditorUtil.LoadResource<Texture2D>("Editor/DemoSprite.png");
}
if (_demoSprite == null)
return;
var frameWidth = 400;
var frameHeight = 300;
var framePadding = 0;
var extraFramesStart = 5;
var extraFramesEnd = 20;
var totalFrames = 29;
var fps = 16f;
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var rect = GUILayoutUtility.GetRect(400*0.75f, 300*0.75f, GUILayout.ExpandHeight(false),
GUILayout.ExpandWidth(false));
var frame = ((int) (EditorApplication.timeSinceStartup*fps))%
(totalFrames + extraFramesStart + extraFramesEnd);
frame -= extraFramesStart;
var actualFrame = Mathf.Clamp(frame, 0, totalFrames);
SRInternalEditorUtil.RenderGif(rect, _demoSprite, actualFrame, frameWidth, frameHeight, 5, framePadding,
framePadding);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1e35f7047e81c2247ae9a5bfd682e156
timeCreated: 1442417928
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using SRDebugger.Services;
namespace SRDebugger
{
public class BugReport
{
public List<ConsoleEntry> ConsoleLog;
/// <summary>
/// User-entered email address
/// </summary>
public string Email;
/// <summary>
/// Raw data of the captured screenshot (png)
/// </summary>
public byte[] ScreenshotData;
public Dictionary<string, Dictionary<string, object>> SystemInformation;
public string UserDescription;
}
public sealed class BugReportSubmitResult
{
public static BugReportSubmitResult Success
{
get { return new BugReportSubmitResult(true, null); }
}
public static BugReportSubmitResult Error(string errorMessage)
{
return new BugReportSubmitResult(false, errorMessage);
}
public bool IsSuccessful { get; }
public string ErrorMessage { get; }
private BugReportSubmitResult(bool successful, string error)
{
IsSuccessful = successful;
ErrorMessage = error;
}
}
public interface IBugReporterHandler
{
/// <summary>
/// Returns true if this bug reporter handler is able to be used.
/// If false, the bug reporter tab will be hidden.
/// </summary>
bool IsUsable { get; }
/// <summary>
/// Submit a new bug report to the handler.
/// </summary>
/// <param name="report">The report to be submitted.</param>
/// <param name="onComplete">Action to be invoked when the bug report is completed.</param>
/// <param name="progress">Callback to set the current submit progress.</param>
void Submit(BugReport report, Action<BugReportSubmitResult> onComplete, IProgress<float> progress);
}
}

View File

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

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
namespace SRDebugger
{
/// <summary>
/// You can implement this interface to create a dynamic "options container".
/// Add the container to SRDebugger via the SRDebug API.
/// </summary>
/// <remarks>
/// When the container is added via the API, the initial set of options will be fetched via <see cref="GetOptions"/>.
/// Options that are added or removed after this point must fire the <see cref="OptionAdded"/> and <see cref="OptionRemoved"/> events in order
/// for those options to be added/removed from the debug panel.
/// If you do not intend to fire any events (i.e. this is a static container) then <see cref="IsDynamic"/> should return false.
/// </remarks>
public interface IOptionContainer
{
/// <summary>
/// Get the initial set of options contained in this object.
/// </summary>
IEnumerable<OptionDefinition> GetOptions();
/// <summary>
/// Will the options collection be changed via events?
/// If true, changes to the option set can be provided via the events <see cref="OptionAdded"/> and <see cref="OptionRemoved"/>.
/// If false, the events will be ignored.
/// </summary>
bool IsDynamic { get; }
event Action<OptionDefinition> OptionAdded;
event Action<OptionDefinition> OptionRemoved;
}
public sealed class DynamicOptionContainer : IOptionContainer
{
public IList<OptionDefinition> Options
{
get { return _optionsReadOnly; }
}
private readonly List<OptionDefinition> _options = new List<OptionDefinition>();
private readonly IList<OptionDefinition> _optionsReadOnly;
public DynamicOptionContainer()
{
_optionsReadOnly = _options.AsReadOnly();
}
public void AddOption(OptionDefinition option)
{
_options.Add(option);
if (OptionAdded != null)
{
OptionAdded(option);
}
}
public bool RemoveOption(OptionDefinition option)
{
if (_options.Remove(option))
{
if (OptionRemoved != null)
{
OptionRemoved(option);
}
return true;
}
return false;
}
IEnumerable<OptionDefinition> IOptionContainer.GetOptions()
{
return _options;
}
public bool IsDynamic
{
get { return true; }
}
public event Action<OptionDefinition> OptionAdded;
public event Action<OptionDefinition> OptionRemoved;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d4ed420ab79c4165a7b6422bd1ee91d5
timeCreated: 1609338293

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 719f45e4748815645b1df26fc728ed66
folderAsset: yes
DefaultImporter:
userData:

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:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: bca87b44250eab3469ec77e0848afc2e
folderAsset: yes
DefaultImporter:
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: 3172283e0204f3e47968b9f791c5a2df
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,22 @@
namespace SRDebugger.Profiler
{
using System;
using UnityEngine;
/// <summary>
/// The profiler has a separate monobehaviour to listen for LateUpdate, and is placed
/// at the end of the script execution order.
/// </summary>
public class ProfilerLateUpdateListener : MonoBehaviour
{
public Action OnLateUpdate;
private void LateUpdate()
{
if (OnLateUpdate != null)
{
OnLateUpdate();
}
}
}
}

View File

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

View File

@@ -0,0 +1,128 @@
namespace SRDebugger.Profiler
{
using System.Diagnostics;
using Services;
using SRF;
using SRF.Service;
using UnityEngine;
public class ProfilerServiceImpl : SRServiceBase<IProfilerService>, IProfilerService
{
public float AverageFrameTime { get; private set; }
public float LastFrameTime { get; private set; }
public CircularBuffer<ProfilerFrame> FrameBuffer
{
get { return _frameBuffer; }
}
private const int FrameBufferSize = 400;
private readonly CircularBuffer<ProfilerFrame>
_frameBuffer = new CircularBuffer<ProfilerFrame>(FrameBufferSize);
private ProfilerLateUpdateListener _lateUpdateListener;
private readonly Stopwatch _stopwatch = new Stopwatch();
// Time between first Update() and last LateUpdate().
private double _updateDuration;
// Time that first camera rendered.
private double _renderStartTime;
// Time between first camera prerender and last camera postrender.
private double _renderDuration;
private int _camerasThisFrame;
protected override void Awake()
{
base.Awake();
_lateUpdateListener = gameObject.AddComponent<ProfilerLateUpdateListener>();
_lateUpdateListener.OnLateUpdate = OnLateUpdate;
CachedGameObject.hideFlags = HideFlags.NotEditable;
CachedTransform.SetParent(Hierarchy.Get("SRDebugger"), true);
Camera.onPreRender += OnCameraPreRender;
Camera.onPostRender += OnCameraPostRender;
}
protected override void Update()
{
base.Update();
_camerasThisFrame = 0;
EndFrame();
// Set the frame time for the last frame
if (FrameBuffer.Count > 0)
{
var frame = FrameBuffer.Back();
frame.FrameTime = Time.unscaledDeltaTime;
FrameBuffer[FrameBuffer.Count - 1] = frame;
}
LastFrameTime = Time.unscaledDeltaTime;
var frameCount = Mathf.Min(20, FrameBuffer.Count);
var f = 0d;
for (var i = 0; i < frameCount; i++)
{
f += FrameBuffer[FrameBuffer.Count - 1 - i].FrameTime;
}
AverageFrameTime = (float) f / frameCount;
_stopwatch.Start();
}
protected void PushFrame(double totalTime, double updateTime, double renderTime)
{
//UnityEngine.Debug.Log("Frame: u: {0} r: {1}".Fmt(updateTime, renderTime));
_frameBuffer.PushBack(new ProfilerFrame
{
OtherTime = totalTime - updateTime - renderTime,
UpdateTime = updateTime,
RenderTime = renderTime
});
}
private void OnLateUpdate()
{
_updateDuration = _stopwatch.Elapsed.TotalSeconds;
}
private void OnCameraPreRender(Camera cam)
{
if (_camerasThisFrame == 0)
{
_renderStartTime = _stopwatch.Elapsed.TotalSeconds;
}
_camerasThisFrame++;
}
private void OnCameraPostRender(Camera cam)
{
_renderDuration = _stopwatch.Elapsed.TotalSeconds - _renderStartTime;
}
private void EndFrame()
{
if (_stopwatch.IsRunning)
{
PushFrame(_stopwatch.Elapsed.TotalSeconds, _updateDuration, _renderDuration);
_stopwatch.Reset();
_stopwatch.Start();
}
_updateDuration = _renderDuration = 0;
}
}
}

View File

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

View File

@@ -0,0 +1,139 @@
#if UNITY_2018_1_OR_NEWER
namespace SRDebugger.Profiler
{
using System.Collections;
using System.Diagnostics;
using SRDebugger.Services;
using SRF;
using SRF.Service;
using UnityEngine;
#if UNITY_2019_3_OR_NEWER
using UnityEngine.Rendering;
#else
using UnityEngine.Experimental.Rendering;
#endif
public class SRPProfilerService : SRServiceBase<IProfilerService>, IProfilerService
{
public float AverageFrameTime { get; private set; }
public float LastFrameTime { get; private set; }
public CircularBuffer<ProfilerFrame> FrameBuffer
{
get { return _frameBuffer; }
}
private const int FrameBufferSize = 400;
private readonly CircularBuffer<ProfilerFrame> _frameBuffer = new CircularBuffer<ProfilerFrame>(FrameBufferSize);
private ProfilerLateUpdateListener _lateUpdateListener;
// Time between first Update() and last LateUpdate()
private double _updateDuration;
// Time that render pipeline starts
private double _renderStartTime;
// Time between scripted render pipeline starts + EndOfFrame
private double _renderDuration;
private readonly Stopwatch _stopwatch = new Stopwatch();
protected override void Awake()
{
base.Awake();
_lateUpdateListener = gameObject.AddComponent<ProfilerLateUpdateListener>();
_lateUpdateListener.OnLateUpdate = OnLateUpdate;
CachedGameObject.hideFlags = HideFlags.NotEditable;
CachedTransform.SetParent(Hierarchy.Get("SRDebugger"), true);
#if UNITY_2019_3_OR_NEWER
RenderPipelineManager.beginFrameRendering += RenderPipelineOnBeginFrameRendering;
#else
RenderPipeline.beginFrameRendering += RenderPipelineOnBeginFrameRendering;
#endif
StartCoroutine(EndOfFrameCoroutine());
}
protected override void Update()
{
base.Update();
EndFrame();
// Set the frame time for the last frame
if (FrameBuffer.Count > 0)
{
var frame = FrameBuffer.Back();
frame.FrameTime = Time.unscaledDeltaTime;
FrameBuffer[FrameBuffer.Count - 1] = frame;
}
LastFrameTime = Time.unscaledDeltaTime;
var frameCount = Mathf.Min(20, FrameBuffer.Count);
var f = 0d;
for (var i = 0; i < frameCount; i++)
{
f += FrameBuffer[FrameBuffer.Count - 1 - i].FrameTime;
}
AverageFrameTime = (float)f / frameCount;
_stopwatch.Start();
}
IEnumerator EndOfFrameCoroutine()
{
var endOfFrame = new WaitForEndOfFrame();
while (true)
{
yield return endOfFrame;
_renderDuration = _stopwatch.Elapsed.TotalSeconds - _renderStartTime;
}
}
protected void PushFrame(double totalTime, double updateTime, double renderTime)
{
_frameBuffer.PushBack(new ProfilerFrame
{
OtherTime = totalTime - updateTime - renderTime,
UpdateTime = updateTime,
RenderTime = renderTime
});
}
private void OnLateUpdate()
{
_updateDuration = _stopwatch.Elapsed.TotalSeconds;
}
#if UNITY_2019_3_OR_NEWER
private void RenderPipelineOnBeginFrameRendering(ScriptableRenderContext context, Camera[] cameras)
#else
private void RenderPipelineOnBeginFrameRendering(Camera[] obj)
#endif
{
_renderStartTime = _stopwatch.Elapsed.TotalSeconds;
}
private void EndFrame()
{
if (_stopwatch.IsRunning)
{
PushFrame(_stopwatch.Elapsed.TotalSeconds, _updateDuration, _renderDuration);
_stopwatch.Reset();
_stopwatch.Start();
}
_updateDuration = _renderDuration = 0;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,59 @@
#if UNITY_ANDROID || UNITY_IOS || UNITY_EDITOR || UNITY_STANDALONE
#define COPY_TO_CLIPBOARD_SUPPORTED
#endif
using System;
using System.Runtime.CompilerServices;
using SRDebugger.Services;
using SRF.Service;
using UnityEngine;
[assembly: InternalsVisibleTo("StompyRobot.SRDebugger.Editor")]
public static class SRDebug
{
public const string Version = SRDebugger.VersionInfo.Version;
public static bool IsInitialized { get; private set; }
public static IDebugService Instance
{
get { return SRServiceManager.GetService<IDebugService>(); }
}
/// <summary>
/// Action to be invoked whenever the user selects "copy" in the console window.
/// If null, copy/paste will not be available.
/// </summary>
public static Action<ConsoleEntry> CopyConsoleItemCallback = GetDefaultCopyConsoleItemCallback();
public static void Init()
{
IsInitialized = true;
SRServiceManager.RegisterAssembly<IDebugService>();
// Initialize console if it hasn't already initialized.
SRServiceManager.GetService<IConsoleService>();
// Load the debug service
SRServiceManager.GetService<IDebugService>();
#if UNITY_EDITOR
SRDebugger.Scripts.Internal.SRScriptRecompileHelper.SetHasInitialized();
#endif
}
public static Action<ConsoleEntry> GetDefaultCopyConsoleItemCallback()
{
#if COPY_TO_CLIPBOARD_SUPPORTED
return entry =>
{
GUIUtility.systemCopyBuffer =
string.Format("{0}: {1}\n\r\n\r{2}", entry.LogType, entry.Message, entry.StackTrace);
};
#else
return null;
#endif
}
}

View File

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

View File

@@ -0,0 +1,19 @@
using System;
namespace SRDebugger
{
using SRF;
using UnityEngine;
/// <summary>
/// Add this component somewhere in your scene to automatically load SRDebugger when the scene is loaded.
/// By default, SRDebugger will defer loading any UI except the corner-trigger until the user requests it.
/// It is recommended to add this to the very first scene in your game. This will ensure the console log
/// will hold useful information about your game initialization.
/// </summary>
[AddComponentMenu("")]
[Obsolete("No longer required, use Automatic initialization mode or call SRDebug.Init() manually.")]
public class SRDebuggerInit : SRMonoBehaviourEx
{
}
}

View File

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

View File

@@ -0,0 +1,39 @@
using System;
namespace SRDebugger
{
[AttributeUsage(AttributeTargets.Property)]
public class NumberRangeAttribute : Attribute
{
public readonly double Max;
public readonly double Min;
public NumberRangeAttribute(double min, double max)
{
Min = min;
Max = max;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class IncrementAttribute : Attribute
{
public readonly double Increment;
public IncrementAttribute(double increment)
{
Increment = increment;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public class SortAttribute : Attribute
{
public readonly int SortPriority;
public SortAttribute(int priority)
{
SortPriority = priority;
}
}
}

View File

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

View File

@@ -0,0 +1,4 @@
/*
* This file has been moved to StoryRobot/SROptions/SROptions.Test.cs
* 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: 3f7d209d96c186e488b96fa4cd40c808
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,4 @@
/*
* This file has been moved to StoryRobot/SROptions/SROptions.cs
* 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: 93395ab8f46722d4eb96829e8d652a66
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: d056f36651c5f924abf693d27338ded5
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,58 @@
using System;
using UnityEngine;
namespace SRDebugger.Services
{
public sealed class ConsoleFilterStateService
{
public event ConsoleStateChangedEventHandler FilterStateChange;
private bool[] _states;
public ConsoleFilterStateService()
{
_states = new bool[Enum.GetValues(typeof(LogType)).Length];
for (var i = 0; i < _states.Length; i++)
{
_states[i] = true;
}
}
/// <summary>
/// Set whether log messages with <paramref name="logType"/> severity
/// should be displayed in the SRDebugger console.
/// </summary>
/// <param name="logType">Type of message (only Error/Warning/Log are used. <see cref="LogType.Exception"/> and <see cref="LogType.Assert"/> will redirect to <see cref="LogType.Error"/></param>
/// <param name="enabled">True to display the log type, false to hide.</param>
public void SetState(LogType type, bool newState)
{
type = GetType(type);
if (_states[(int)type] == newState)
{
return;
}
_states[(int)type] = newState;
FilterStateChange?.Invoke(type, newState);
}
public bool GetState(LogType type)
{
type = GetType(type);
return _states[(int)type];
}
private static LogType GetType(LogType type)
{
switch (type)
{
case LogType.Error:
case LogType.Assert:
case LogType.Exception:
return LogType.Error;
default:
return type;
}
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using System;
namespace SRDebugger.Services
{
public delegate void BugReportCompleteCallback(bool didSucceed, string errorMessage);
public delegate void BugReportProgressCallback(float progress);
public interface IBugReportService
{
/// <summary>
/// Whether the bug reporter is available for use right now.
/// </summary>
bool IsUsable { get; }
/// <summary>
/// Set the handler that will submit bug reports.
/// </summary>
void SetHandler(IBugReporterHandler handler);
/// <summary>
/// Submit a bug report.
/// completeHandler can be invoked any time after the method is called
/// (even before the method has returned in case of no internet).
/// </summary>
/// <param name="report">Bug report to send</param>
/// <param name="completeHandler">Delegate to call once bug report is submitted successfully</param>
/// <param name="progressCallback">Optionally provide a callback for when progress % is known</param>
void SendBugReport(BugReport report, BugReportCompleteCallback completeHandler,
IProgress<float> progressCallback = null);
}
}

View File

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

View File

@@ -0,0 +1,27 @@
using UnityEngine;
namespace SRDebugger.Services
{
public delegate void ConsoleStateChangedEventHandler(LogType logType, bool newState);
public interface IConsoleFilterState
{
event ConsoleStateChangedEventHandler FilterStateChange;
/// <summary>
/// Set whether log messages with <paramref name="logType"/> severity
/// should be displayed in the SRDebugger console.
/// </summary>
/// <param name="logType">Type of message (only Error/Warning/Log are used. <see cref="LogType.Exception"/> and <see cref="LogType.Assert"/> will redirect to <see cref="LogType.Error"/></param>
/// <param name="enabled">True to display the log type, false to hide.</param>
void SetConsoleFilterState(LogType logType, bool enabled);
/// <summary>
/// Get whether log messages with <paramref name="logType"/> severity are
/// being displayed in the SRDebugger console.
/// </summary>
/// <param name="logType">Type of message (only Error/Warning/Log are used. <see cref="LogType.Exception"/> and <see cref="LogType.Assert"/> will redirect to <see cref="LogType.Error"/></param>
/// <returns>True if the log type is displayed.</returns>
bool GetConsoleFilterState(LogType logType);
}
}

View File

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

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
namespace SRDebugger.Services
{
using UnityEngine;
public delegate void ConsoleUpdatedEventHandler(IConsoleService console);
public interface IConsoleService
{
int ErrorCount { get; }
int WarningCount { get; }
int InfoCount { get; }
/// <summary>
/// List of ConsoleEntry objects since the last clear.
/// </summary>
IReadOnlyList<ConsoleEntry> Entries { get; }
/// <summary>
/// List of all ConsoleEntry objects, regardless of clear.
/// </summary>
IReadOnlyList<ConsoleEntry> AllEntries { get; }
event ConsoleUpdatedEventHandler Updated;
event ConsoleUpdatedEventHandler Error;
bool LoggingEnabled { get; set; }
bool LogHandlerIsOverriden { get; }
void Clear();
}
public class ConsoleEntry
{
private const int MessagePreviewLength = 180;
private const int StackTracePreviewLength = 120;
private string _messagePreview;
private string _stackTracePreview;
/// <summary>
/// Number of times this log entry has occured (if collapsing is enabled)
/// </summary>
public int Count = 1;
public LogType LogType;
public string Message;
public string StackTrace;
public ConsoleEntry() {}
public ConsoleEntry(ConsoleEntry other)
{
Message = other.Message;
StackTrace = other.StackTrace;
LogType = other.LogType;
Count = other.Count;
}
public string MessagePreview
{
get
{
if (_messagePreview != null)
{
return _messagePreview;
}
if (string.IsNullOrEmpty(Message))
{
return "";
}
_messagePreview = Message.Split('\n')[0];
_messagePreview = _messagePreview.Substring(0, Mathf.Min(_messagePreview.Length, MessagePreviewLength));
return _messagePreview;
}
}
public string StackTracePreview
{
get
{
if (_stackTracePreview != null)
{
return _stackTracePreview;
}
if (string.IsNullOrEmpty(StackTrace))
{
return "";
}
_stackTracePreview = StackTrace.Split('\n')[0];
_stackTracePreview = _stackTracePreview.Substring(0,
Mathf.Min(_stackTracePreview.Length, StackTracePreviewLength));
return _stackTracePreview;
}
}
public bool Matches(ConsoleEntry other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return string.Equals(Message, other.Message) && string.Equals(StackTrace, other.StackTrace) &&
LogType == other.LogType;
}
}
}

View File

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

Some files were not shown because too many files have changed in this diff Show More