diff --git a/Editor/BugSplatMenu.cs b/Editor/BugSplatMenu.cs new file mode 100644 index 0000000..36a858d --- /dev/null +++ b/Editor/BugSplatMenu.cs @@ -0,0 +1,42 @@ +using UnityEditor; +using UnityEngine; +using BugSplatUnity.Runtime.Client; + +namespace BugSplatUnity.Editor +{ + public static class BugSplatMenu + { + private const string AssetPath = "Assets/BugSplat/Resources/BugSplatOptions.asset"; + + [MenuItem("Tools/BugSplat/Options", priority = 100)] + public static void OpenOptions() + { + var options = AssetDatabase.LoadAssetAtPath(AssetPath); + if (options == null) + { + CreateOptionsAsset(); + options = AssetDatabase.LoadAssetAtPath(AssetPath); + } + + Selection.activeObject = options; + EditorGUIUtility.PingObject(options); + } + + private static void CreateOptionsAsset() + { + var dir = System.IO.Path.GetDirectoryName(AssetPath); + if (!AssetDatabase.IsValidFolder("Assets/BugSplat")) + { + AssetDatabase.CreateFolder("Assets", "BugSplat"); + } + if (!AssetDatabase.IsValidFolder("Assets/BugSplat/Resources")) + { + AssetDatabase.CreateFolder("Assets/BugSplat", "Resources"); + } + + var options = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(options, AssetPath); + AssetDatabase.SaveAssets(); + } + } +} diff --git a/Editor/BugSplatMenu.cs.meta b/Editor/BugSplatMenu.cs.meta new file mode 100644 index 0000000..a6e3362 --- /dev/null +++ b/Editor/BugSplatMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f5882cd09494fa4876fbf9bc6a64bec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index 3480bb1..f693cbf 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ To import the sample, click the carrot next to **Samples** to reveal the **my-un In the Project Assets browser, open the **Sample** scene from `Samples > BugSplat > Version > my-unity-crasher > Scenes`. -Next, select `Samples > BugSplat > Version > my-unity-crasher` to reveal the **BugSplatOptions** object. Click BugSplatOptions and replace the database value with your BugSplat database. +Next, open **Tools > BugSplat > Options** and replace the database value with your BugSplat database. ![Finding the Sample](https://github.com/BugSplat-Git/bugsplat-unity/assets/2646053/ba9aa64a-1d85-45a8-b11f-565520c30bcf) @@ -72,7 +72,7 @@ BugSplat's Unity integration is flexible and can be used in various ways. The ea ![BugSplat Manager](https://github.com/BugSplat-Git/bugsplat-unity/assets/2646053/ef5240a6-9676-43c6-a482-51216cb34401) -`BugSplatManager` needs to be initialized with a `BugSplatOptions` serialized object. A new instance of `BugSplatOptions` can be created through the Asset Create menu. +`BugSplatManager` will automatically use the options configured via **Tools > BugSplat > Options**. ![BugSplat Create Options](https://github.com/BugSplat-Git/bugsplat-unity/assets/2646053/9ec402d1-4b8a-49cf-96e9-00d951717771) @@ -80,20 +80,20 @@ Configure fields as appropriate. Note that if Application or Version are left em ![BugSplat Options](https://github.com/BugSplat-Git/bugsplat-unity/assets/2646053/be7ee217-9170-48b4-b780-fcb47e221f77) -Finally, provide a valid `BugSplatOptions` to `BugSplatManager`. - ![BugSplat Manager Configured](https://github.com/BugSplat-Git/bugsplat-unity/assets/2646053/67bed7b5-e2a9-4f52-b5bb-bdc8eebd35a0) ## ⌨️ Usage -If you're using `BugSplatOptions` and `BugSplatManager`, BugSplat automatically configures an `Application.logMessageReceived` handler that will post reports when it encounters a log message of type `Exception`. You can also extend your BugSplat integration and [customize report metadata](#adding-metadata), [report exceptions in try/catch blocks](#trycatch-reporting), [prevent repeated reports](#preventing-repeated-reports), and [upload windows minidumps](#windows) from native crashes. +If you're using `BugSplatManager`, BugSplat automatically configures an `Application.logMessageReceived` handler that will post reports when it encounters a log message of type `Exception`. You can also extend your BugSplat integration and [customize report metadata](#adding-metadata), [report exceptions in try/catch blocks](#trycatch-reporting), [prevent repeated reports](#preventing-repeated-reports), and [upload windows minidumps](#windows) from native crashes. + +The options set in **Tools > BugSplat > Options** are loaded automatically at startup and used to create the global `BugSplat.Instance`. ### Adding Metadata -First, find your instance of `BugSplat`. The following is an example of how to find an instance of `BugSplat` via `BugSplatManager`: +First, access the global `BugSplat` instance: ```cs -var bugsplat = FindObjectOfType().BugSplat; +var bugsplat = BugSplat.Instance; ``` You can extend `BugSplat` by setting the following properties: @@ -115,7 +115,7 @@ You can use the `Notes` field to capture arbitrary data such as system informati ```cs void Start() { - bugsplat = FindObjectOfType().BugSplat; + bugsplat = BugSplat.Instance; bugsplat.Notes = GetSystemInfo(); } @@ -205,7 +205,7 @@ The methods `PostCrash`, `PostMostRecentCrash`, and `PostAllCrashes` can be used ```cs void Start() { - bugsplat = FindObjectOfType().BugSplat; + bugsplat = BugSplat.Instance; StartCoroutine(bugsplat.PostAllCrashes()); } @@ -224,7 +224,7 @@ Utils.ForceCrash(ForcedCrashCategory.PureVirtualFunction); ### Windows Symbols -To enable the uploading of plugin symbols, generate an OAuth2 Client ID and Client Secret on the BugSplat [Integrations](https://app.bugsplat.com/v2/settings/database/integrations) page. Add your Client ID and Client Secret to the `BugSplatOptions` object you generated in the [Configuration](#⚙️-configuration) section. If your game contains Native Windows C++ plugins, `.dll` and `.pdb` files in the `Assets/Plugins/x86` and `Assets/Plugins/x86_64` folders will be uploaded by BugSplat's PostBuild script and used in symbolication. +To enable the uploading of plugin symbols, generate an OAuth2 Client ID and Client Secret on the BugSplat [Integrations](https://app.bugsplat.com/v2/settings/database/integrations) page. Add your Client ID and Client Secret via **Tools > BugSplat > Options**. If your game contains Native Windows C++ plugins, `.dll` and `.pdb` files in the `Assets/Plugins/x86` and `Assets/Plugins/x86_64` folders will be uploaded by BugSplat's PostBuild script and used in symbolication. ### Support Response diff --git a/Runtime/BugSplat.cs b/Runtime/BugSplat.cs index c5f7c88..9bceff7 100644 --- a/Runtime/BugSplat.cs +++ b/Runtime/BugSplat.cs @@ -21,6 +21,11 @@ namespace BugSplatUnity /// public class BugSplat { + /// + /// Global instance of BugSplat. + /// + public static BugSplat Instance { get; internal set; } + /// /// A list of files to be uploaded every time Post is called /// @@ -250,6 +255,8 @@ bool useNativeLibAndroid #else UseDotNetHandler(database, application, version); #endif + + Instance = this; } private void UseDotNetHandler(string database, string application, string version) @@ -298,16 +305,18 @@ public static BugSplat CreateFromOptions(BugSplatOptions options) }; if (options.PersistentDataFileAttachmentPaths != null) - { + { foreach (var filePath in options.PersistentDataFileAttachmentPaths) { var trimmedFilePath = filePath.TrimStart('/', '\\'); - var fullFilePath = Path.Combine(Application.persistentDataPath, trimmedFilePath); + var fullFilePath = Path.Combine(Application.persistentDataPath, trimmedFilePath); var fileInfo = new FileInfo(fullFilePath); bugSplat.Attachments.Add(fileInfo); } } + Instance = bugSplat; + return bugSplat; } diff --git a/Runtime/Manager/BugSplatInitializer.cs b/Runtime/Manager/BugSplatInitializer.cs new file mode 100644 index 0000000..f8f007e --- /dev/null +++ b/Runtime/Manager/BugSplatInitializer.cs @@ -0,0 +1,26 @@ +using BugSplatUnity.Runtime.Client; +using UnityEngine; + +namespace BugSplatUnity.Runtime.Manager +{ + /// + /// Loads BugSplat configuration and creates the global instance at startup. + /// + internal static class BugSplatInitializer + { + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void Initialize() + { + if (BugSplat.Instance != null) + { + return; + } + + var options = Resources.Load("BugSplatOptions"); + if (options != null) + { + BugSplat.Instance = BugSplat.CreateFromOptions(options); + } + } + } +} diff --git a/Runtime/Manager/BugSplatInitializer.cs.meta b/Runtime/Manager/BugSplatInitializer.cs.meta new file mode 100644 index 0000000..512c88a --- /dev/null +++ b/Runtime/Manager/BugSplatInitializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81574bc30a144fe1b65b21d370e74d66 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Manager/BugSplatManager.cs b/Runtime/Manager/BugSplatManager.cs index df3c3ca..26311fa 100644 --- a/Runtime/Manager/BugSplatManager.cs +++ b/Runtime/Manager/BugSplatManager.cs @@ -28,8 +28,9 @@ private void Awake() throw new ArgumentException("BugSplat error: BugSplatOptions is null! BugSplat will not be initialized."); } - var bugsplat = BugSplat.CreateFromOptions(bugSplatOptions); - bugsplatRef = new BugSplatRef(bugsplat); + var bugsplat = BugSplat.CreateFromOptions(bugSplatOptions); + BugSplat.Instance = bugsplat; + bugsplatRef = new BugSplatRef(bugsplat); if (registerLogMessageReceived) { diff --git a/Samples~/my-unity-crasher/Scripts/BugSplatSettings.cs b/Samples~/my-unity-crasher/Scripts/BugSplatSettings.cs index 9323db4..77a1bfc 100644 --- a/Samples~/my-unity-crasher/Scripts/BugSplatSettings.cs +++ b/Samples~/my-unity-crasher/Scripts/BugSplatSettings.cs @@ -1,5 +1,4 @@ using BugSplatUnity; -using BugSplatUnity.Runtime.Manager; using System; using System.Collections.Generic; using System.Linq; @@ -12,7 +11,7 @@ public class BugSplatSettings : MonoBehaviour // Start is called before the first frame update void Start() { - bugsplat = FindObjectOfType().BugSplat; + bugsplat = BugSplat.Instance; bugsplat.Attributes.Add("OS", SystemInfo.operatingSystem); bugsplat.Attributes.Add("CPU", SystemInfo.processorType); bugsplat.Attributes.Add("MEMORY", $"{SystemInfo.systemMemorySize} MB"); diff --git a/Samples~/my-unity-crasher/Scripts/ErrorGenerator.cs b/Samples~/my-unity-crasher/Scripts/ErrorGenerator.cs index f882aac..ee801ff 100644 --- a/Samples~/my-unity-crasher/Scripts/ErrorGenerator.cs +++ b/Samples~/my-unity-crasher/Scripts/ErrorGenerator.cs @@ -2,7 +2,6 @@ using UnityEngine; using UnityEngine.Diagnostics; using BugSplat = BugSplatUnity.BugSplat; -using BugSplatUnity.Runtime.Manager; using BugSplatUnity; using BugSplatUnity.Runtime.Reporter; using System.Diagnostics; @@ -19,7 +18,7 @@ public class ErrorGenerator : MonoBehaviour void Start() { - bugsplat = FindObjectOfType().BugSplat; + bugsplat = BugSplat.Instance; Application.SetStackTraceLogType(LogType.Warning, StackTraceLogType.Full); #if UNITY_STANDALONE_WIN StartCoroutine(bugsplat.PostMostRecentCrash()); diff --git a/Tests/Runtime/Manager/BugSplatInstanceTests.cs b/Tests/Runtime/Manager/BugSplatInstanceTests.cs new file mode 100644 index 0000000..beea830 --- /dev/null +++ b/Tests/Runtime/Manager/BugSplatInstanceTests.cs @@ -0,0 +1,16 @@ +using BugSplatUnity; +using NUnit.Framework; + +namespace BugSplatUnity.RuntimeTests.Manager +{ + public class BugSplatInstanceTests + { + [Test] + public void Constructor_ShouldSetInstance() + { + BugSplat.Instance = null; + var bugsplat = new BugSplat("database", "application", "version", false, false); + Assert.AreEqual(bugsplat, BugSplat.Instance); + } + } +} diff --git a/Tests/Runtime/Manager/BugSplatInstanceTests.cs.meta b/Tests/Runtime/Manager/BugSplatInstanceTests.cs.meta new file mode 100644 index 0000000..3987874 --- /dev/null +++ b/Tests/Runtime/Manager/BugSplatInstanceTests.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5c7a550a713b468f9c63cf6e0c91657a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +