fix: support SetNativeDescription on iOS, macOS, and Android via attribute promotion - #146
fix: support SetNativeDescription on iOS, macOS, and Android via attribute promotion#146bobbyg603 wants to merge 34 commits into
Conversation
Replaces the Unity crash-folder minidump flow with native Windows crash reporting (closes #120). Unity's crash reporter produced low-quality reports; the native BugSplat SDK captures crashes at crash time with full fidelity, matching the macOS (bugsplat-apple) integration pattern. - Vendor BugSplat.dll (C API, x64 + ARM64 Release) built from bugsplat-windows feat/c-api-dynamic-library (BugSplat-Git/bugsplat-windows#155), plus matching BugSplatMonitor.exe/BugSplatRc.dll/BugSplatWer.dll under Runtime/Plugins/Windows. Swap to official release binaries when the upstream PR ships in a release. - BugSplat.cs: new useNativeLibWin ctor param; Windows player init via BugSplat_Init P/Invoke (quiet mode on, hang detection off, Player.log attached, unsent crash retry); native setter wiring for attributes/user/email/notes/key/description; new SetWindowsCrashDialogEnabled and SetWindowsHangDetectionTimeout knobs. - BugSplatOptions: UseNativeCrashReportingForWindows, WindowsShowCrashDialog, WindowsHangDetectionTimeoutMs. - PostBuild: copy Monitor/Rc/Wer next to the built .exe (arch from PE header), copy LineNumberMappings.json for IL2CPP symbol upload. - BREAKING: WindowsReporter/INativeCrashReporter removed; PostAllCrashes/PostCrash/PostMostRecentCrash obsoleted (unsent crashes upload automatically at startup); Post(FileInfo) now works on all platforms. Works with both Mono and IL2CPP. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Observed in end-to-end testing: when the hang timeout elapses, the SDK captures and uploads a hang report, then terminates the process. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuilt from bugsplat-windows feat/c-api-dynamic-library. Unity does not call these native exports (managed feedback uses DotNetStandardClient); re-vendored only to keep the binary in sync with the upstream branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuilt from bugsplat-windows feat/c-api-dynamic-library (20 exports). Unity does not call the added natives; re-vendored to keep the binary in sync with the upstream branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sample scene was authored in a URP + Input System project, leaving two components that show as "missing script" (and break button clicks) in any project without those packages: - UniversalAdditionalLightData on the light -> removed (sample needs no URP) - InputSystemUIInputModule on the EventSystem -> replaced with the built-in UGUI StandaloneInputModule, so buttons work with only com.unity.ugui The missing input module is why the Crash Native button did nothing without the Input System package installed. Verified: clean Unity 6000.4.10f1 build loads the scene with no missing-script warnings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blank button labels are TextMeshPro with no default font asset imported. Document the Window > TextMeshPro > Import TMP Essential Resources step in the README and a new sample README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the editor the native crash button intentionally no-ops; the old
message ("not yet implemented on this platform") read like a bug. Now it
explains native crashes are captured in built players only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous change hard-coded StandaloneInputModule in the scene, which receives no input when Active Input Handling is "Input System Package (New)" - so the sample buttons did nothing on projects using the new Input System. Replace it with a SampleInputModule script that adds the correct module at runtime: InputSystemUIInputModule (+ AssignDefaultActions) when the Input System package is present, otherwise StandaloneInputModule. The scene no longer serializes any input module, so there is no package-specific component to show as a missing script either. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per project direction the sample targets the new Input System only, so the EventSystem uses the stock InputSystemUIInputModule (referencing the Input System package default actions) as the sample originally shipped. Removes the backend-agnostic SampleInputModule script. The sample requires the Input System package. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te in Unity 6) Resolves CS0618 warnings; FindAnyObjectByType is the recommended replacement and is a safe drop-in for these single-instance lookups. Also updated the README examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sample buttons highlight and select but never invoked their handlers: the prefab-instance onClick overrides set the target/method/array-size but not m_CallState, so each call defaulted to UnityEventCallState.Off and was skipped at runtime. Copied the corrected scene back from an editor session where the four buttons were re-wired (m_CallState=RuntimeOnly, m_Mode=Void). The EventSystem uses InputSystemUIInputModule with the Input System package's default actions (portable, no project-local actions asset). Note: the directional light's URP UniversalAdditionalLightData component is present again (re-added by the URP project the scene was fixed in); it is harmless and only shows a benign "missing script" note in non-URP projects. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The native handler default (exit()) can hang a standalone Windows player on CRT shutdown after the report uploads, leaving the process running. Opt into the new BugSplat_SetTerminateAfterCrash on init so the player exits cleanly once the crash report is sent. Re-vendored BugSplat.dll (x64 + ARM64, 19 exports) built from bugsplat-windows feat/c-api-dynamic-library. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follows the upstream rename from BugSplat_SetTerminateAfterCrash to the 3-way BugSplat_SetCrashCompletionBehavior. Unity still opts into Terminate (BUGSPLAT_CRASH_TERMINATE) so the player exits cleanly after a crash uploads. Re-vendored BugSplat.dll (x64 + ARM64). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace Utils.ForceCrash (faults in engine internals) with a chain of [MethodImpl(NoInlining)] C# frames ending in Marshal.WriteInt32(IntPtr.Zero, 0), a raw access violation. BugSplat's native handler captures it, and on IL2CPP the frames symbolicate to C# names + line numbers via GameAssembly.pdb + LineNumberMappings.json - so the report shows a game-code call stack instead of an engine stack. Works on Mono and IL2CPP; no unsafe code required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flip WindowsShowCrashDialog to default true and init quiet mode off, so a native Windows crash shows the BugSplat dialog out of the box (matching classic BugSplat Windows UX). Set it false for silent reporting. Sample opts in to the new default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…olication) symbol-upload skips files with no debug GUID (added in symbol-upload #189/#191 to avoid uploading Unreal's binary-encoded Linux .sym files as garbage symbols). That skip also drops LineNumberMappings.json, so IL2CPP crashes never get C# names/lines. The tool already uploads .zip files as-is via the versions path, so zip the mapping first (entry at archive root) and upload the .zip instead of the raw .json. Applied to all three IL2CPP upload paths: Windows + macOS (shared ZipForUpload helper) and iOS (zip -j in the Xcode build-phase script). Verified end to end on Windows; Mac/iOS use the identical mechanism but weren't built in this session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Call BugSplat_SetCrashType(15) on init so native crashes upload as CrashType.UnityNative. That is the crash type the BugSplat backend uses to apply LineNumberMappings.json, turning IL2CPP native frames into C# names + line numbers. Pairs with the PostBuild change that uploads the (zipped) mapping. Re-vendored BugSplat.dll (x64 + ARM64, 20 exports). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Post(FileInfo): poll the upload Task to completion instead of yielding it directly, so coroutine callers that `yield return Post(minidump)` block until the upload finishes (mirrors PostFeedback). Surface faulted/canceled results to the callback. - PostCrash / PostMostRecentCrash: invoke callback?.Invoke(null) so deprecated no-op APIs keep caller control-flow predictable (matches PostAllCrashes). - PostBuild: recognize x86 PE machine type (0x014C) and short-circuit native support-file copy with a clear warning, since native crash reporting binaries ship only for x64/ARM64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.0.0 is a breaking major release, so remove the deprecated Unity crash-folder APIs outright instead of shipping them as no-op [Obsolete] shims. Unsent native crash reports upload automatically at startup; the internal BugSplat_PostAllCrashesAsync P/Invoke (called during init) is retained. README migration note updated from "obsolete" to "removed". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendor the Win32 BugSplat.dll and support files (BugSplatMonitor.exe, BugSplatRc.dll, BugSplatWer.dll) so 32-bit standalone Windows players get native crash reporting alongside x64 and ARM64. The bugsplat-windows BugSplatDynamic project already builds Release|Win32; the 32-bit DLL exports the same 20 undecorated BugSplat_* names, so the existing P/Invoke (DllImport "BugSplat", Cdecl) resolves on x86 with no runtime change. PostBuild no longer skips x86 — it copies the Support~/x86 files next to the built .exe (arch from the PE header machine field). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….0 release All 12 vendored binaries (BugSplat.dll x86/x64/ARM64 + Monitor/Rc/Wer support trios) now come from the official BugSplat.zip v8 release: Authenticode-signed (signer: BugSplat), FileVersion 8.0.0.0, all 21 undecorated BugSplat_* C exports verified present on every arch, and BugSplatC.h in the release is identical to the header this integration was built against. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fail-fast terminations - stack buffer overrun (0xC0000409), heap corruption (0xC0000374), __fastfail - bypass every in-process exception handler, so BugSplat's crash filter never sees them. BugSplat_Init already registers BugSplatWer.dll to catch them and PostBuild already ships it next to the executable, but registration silently no-ops without an HKLM allowlist value naming that DLL. Nothing wrote it, documented it, or reported it missing, so that capture path was inert and there was no way to tell. - BugSplat.WindowsWerEnabled surfaces the SDK's real registration state via the new BugSplat_IsWerEnabled export. The call is guarded against EntryPointNotFoundException so this works against pre-8.0.1 binaries. - Init logs what is lost and how to fix it when WER is not armed. Only a warning in development builds: the value is absent on virtually every end-user machine and a player cannot act on it. - BugSplat > Windows > Register WER Handler writes the value elevated for a built player, and reads it back rather than trusting reg.exe's exit code. Both registry views are written because a 32-bit player is handled by the SysWOW64 WerFault. - The sample gains a scrollable scenario menu covering all four capture paths. Every trigger is C# plus system-DLL P/Invokes, so no crasher DLL is shipped - Unity never unloads a native plugin once loaded in the editor. Native rows are inert in the editor behind a compile-time guard, a runtime gate, and a disabled button. The background-thread managed exception scenario is labelled as a known gap: Unity only raises logMessageReceived for main-thread logs, so those produce no report today. Tracked separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Isolated in its own commit so it can be reverted independently if the scene regresses. The diff is one GameObject, one MonoBehaviour, and one Transform - no Button, no m_OnClick, no prefab modification. The menu builds its own UI in code, which is what keeps the scene free of the persistent UnityEvent wiring that silently shipped inert buttons before (fb16f58). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a Windows Error Reporting section covering which crashes need it, the two preconditions, the installer's responsibility for the HKLM value (with snippets for both add and uninstall), the editor menu item for local builds, and where to look when a report does not arrive. Also corrects two stale claims: the crash dialog is shown by default, not suppressed (changed in ee63c86), and the sample's Windows native crash has used a C# frame chain writing through a null pointer since a683891, not Utils.ForceCrash. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Picks up BugSplat_IsWerEnabled (bugsplat-windows#162), which the WER detection added in 04432a0 P/Invokes. Until now that call was resolving through the EntryPointNotFoundException fallback against v8.0.0 binaries, so WindowsWerEnabled always reported false. All 12 files verified before vendoring: Authenticode signature Valid, signer BugSplat, FileVersion 8.1.0.0; 22 undecorated BugSplat_* exports on x86, x64 and ARM64 including BugSplat_IsWerEnabled; and the release BugSplatC.h byte-identical to the header this integration was built against. Swapped as a set per architecture - BugSplat.dll and BugSplatMonitor.exe are coupled through SHARED_MEMORY_LAYOUT_VERSION. No .meta changes, so plugin GUIDs and import settings are preserved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The export shipped in bugsplat-windows 8.1.0, not 8.0.1. The guard itself is unchanged; only the comment naming the minimum version was wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sample had two overlapping UIs: a four-button grid driven by ErrorGenerator and a Crash Scenarios menu behind a launcher button, whose rows duplicated the grid. Two sources of truth for the same triggers, and the grid's buttons were scene-authored prefab instances whose onClick wiring has silently broken before. The scenario list is now the whole scene. Sections group scenarios by the mechanism expected to capture them - MANAGED, NATIVE, FAIL-FAST, HANG, FEEDBACK - because a report arriving only means something if it arrived by the path under test. Sections compile per build target, so a Windows player offers Windows' native, fail-fast, and hang scenarios while macOS, iOS, and Android each offer theirs; Linux and WebGL get managed and feedback only, and the status line says so. The per-platform native triggers move from ErrorGenerator into CrashScenarios unchanged, including the Android UI-thread dispatch needed for an ANR and the iOS/macOS __Internal externs. Native access violations now fault inside RtlMoveMemory rather than dereferencing null from C#. On the Mono backend a null write from managed code is not a crash at all: Mono's vectored handler claims faults whose instruction pointer is in JIT'd code and rethrows them as a managed NullReferenceException, so the player caught an exception and kept running while the native handler saw nothing. Faulting inside ntdll puts the exception where Mono has no unwind info, so it declines and BugSplat's filter gets it - the same reason the custom SEH scenario already worked. Stack overflow has no such workaround; Mono guards the stack and raises a managed StackOverflowException, so that row now says what to expect for the backend it was built with. FAIL-FAST rows grey out when the WER handler is not registered, replacing their description with how to register it, since running them would terminate the player and report nothing. Deleted: ErrorGenerator, BugSplatLayoutButtons, PlatformDependentObject, the button prefab, the four grid instances, the corner feedback button, and two inactive objects that carried no behaviour. The scene shrinks from 2062 lines to 929 and now holds only the menu component and its two button sprites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Post yielded a Task to Unity's coroutine scheduler, which treats unknown yield values as a single-frame wait, so the coroutine completed before the upload finished and the report could be lost if the app quit right after. The Task.Run body also parsed the response, logged, and invoked the user callback on a threadpool thread, where Unity APIs are unsafe. Keep only the network I/O inside the task, poll IsCompleted like the feedback and minidump coroutines in BugSplat.cs, and run response parsing, logging, and callbacks on the coroutine after the task completes, routing faulted tasks through the existing error path. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…etters (#128) * fix(android): enable native crash reporting flag and wire supported setters The Android init branch never set nativeCrashReportingEnabled, so every native setter early-returned and silently no-oped on Android. Set the flag after initBugSplat and route SetNativeAttribute through BugSplatBridge.setAttribute, the only post-init mutator the bridge exposes. Document that user, email, notes, and log-file attachment remain no-ops on Android. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(android): route user, email, notes, and key through reserved attributes The backend's SetAttributeOverrides promotes the reserved attribute names BugSplatUser, BugSplatEmail, BugSplatNotes, and BugSplatApplicationKey to first-class report fields for any minidump-based report, so the Android bridge's setAttribute can carry values the bridge has no dedicated setters for. Wire SetNativeUser, SetNativeEmail, SetNativeNotes, and SetNativeKey through those names and drop the no-op doc wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* ci: add a workflow that runs the test suite The suite had never run anywhere: it failed to compile on every build target (audit A1/A2), and there was no CI to notice (audit J1). The workflow runs the suite on StandaloneLinux64 and WebGL. This repo is a UPM package rather than a Unity project, so it generates a host project that embeds the package and lists it under testables. Requires a UNITY_LICENSE or UNITY_EMAIL/PASSWORD/SERIAL secret. Carries the A1/A2 compile fix as well, because without it there is no target on which the suite builds and the workflow would have nothing to run. That fix is identical to #129; whichever lands first makes the other a no-op. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: add a timeout, concurrency, and a fork guard to the test workflow A hung Unity batchmode run would otherwise burn the job's full six hours, and superseded pushes kept running. Fork PRs cannot see the license secret, so they would always report a red check the contributor has no way to fix; skip them instead. Reference the artifact path directly rather than an action output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: accept either a personal or a Pro Unity license Wiring only UNITY_LICENSE forced a workflow edit for anyone activating with a serial. Pass both sets of credentials and let GameCI pick. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: keep Unity Pro activations bounded to one at a time A Pro serial allows only a couple of concurrent activations, and each matrix job consumes one. Run the targets serially so a workflow run needs a single activation, and stop cancelling superseded runs: cancellation can skip GameCI's license return step, leaking activations until the seat is exhausted and every later run fails to activate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: always return the Unity license Every container is a new machine to Unity, so each run consumes a serial activation. A run that dies before returning it burns that slot against a machine that no longer exists, and it has to be revoked by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: move off Node 20 actions checkout, cache, and upload-artifact were all pinned to majors that still declare node20. Bump to the current majors, which run on node24. game-ci/unity-test-runner@v4 also declares node20 and stays as-is: v4.3.1 is the latest release, so there is no newer tag to move to. Their main branch has already switched to node24, so this resolves when they cut a release. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: run the suite against the Windows and macOS targets DotNetStandardExceptionReporter guards its log-capture paths on UNITY_STANDALONE_WIN and UNITY_STANDALONE_OSX without excluding the editor, so those branches only compile when the matching target is active. Linux and WebGL alone left them unbuilt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: compile the player scripts for iOS and Android Tests run inside the editor, so every path behind `&& !UNITY_EDITOR` — the whole native crash surface, plus the iOS and Android blocks in PostBuild.cs — is excluded from the assemblies they link against. No test leg can catch a break there. Compiling the player scripts does build that code. Verified locally by introducing an error inside `#elif UNITY_ANDROID && !UNITY_EDITOR`: the check exits 1 and reports it, and passes once reverted. PlayerBuildInterface.CompilePlayerScripts rather than a full player build: same compile coverage without needing scenes, build settings, or the time. Runs after the test job because only one Unity activation is available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: only return the license explicitly when a run is interrupted The explicit return took ~70s a job to repeat what GameCI's own post step had already done: that post step succeeded on the clean run and only failed on the cancelled one, matching unity-builder#538, which is specifically about failed or hung runs. Restrict it to that case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: cover the CreateFromOptions mapping CreateFromOptions copies ten fields from BugSplatOptions onto BugSplat. A dropped line there discards a user's configuration silently: nothing throws, nothing logs, and the report simply goes out without the setting. Nothing covered it. Every value in the mapping test is set away from its default, since asserting a field that already holds the value it would have had anyway passes whether or not the mapping exists. Description, Email, Key, Notes, and User were set-only, so none of them could be asserted. Added getters mirroring LogFileMaxSizeMB, which already had one - the asymmetry looks like an oversight rather than a decision, and reading back a value you set is reasonable for callers too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: apply the attributes configured on BugSplatOptions (#136) Closes #134 BugSplatOptions.Attributes had no effect. CreateFromOptions never read it, so attributes authored on the asset never reached a report, silently. It could not have worked anyway: the field was a Dictionary<string, string>, which Unity cannot serialize, so it was unset from the inspector regardless. Unity warned about this on every build (UAC1009). Authored as a list of name/value pairs instead, which does serialize, and copied onto the client in CreateFromOptions. Entries with no name are skipped and a null value becomes an empty string, so a half-filled inspector row cannot produce a malformed attribute. BREAKING: BugSplatOptions.Attributes is now List<BugSplatAttribute> rather than Dictionary<string, string>. Code assigning a dictionary to it will not compile - though that code could never have had any effect. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * test: cover the log tail truncation, and fix its null guard (#137) CopyLogTailToTempFile implements the log truncation from #110 and had no coverage. It decides how much of a log to upload, so getting the offset wrong silently ships the start of a log instead of the end - the part that actually describes the crash. The tests use position-dependent bytes rather than a repeated pattern, so a copy taken from the wrong offset fails rather than passing by coincidence. Fixed a null guard that could not fire: the null branch dereferenced the same argument it was checking, so a null FileInfo threw NullReferenceException instead of returning null. Made the method internal to test it; the test assembly already has InternalsVisibleTo. Refs #135 Co-authored-by: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ibute promotion The BugSplat backend promotes an attribute named BugSplatDescription to the report's description field, so platforms without a dedicated native description API route through the same attribute interop the sibling setters use: the bugsplat-apple attribute call on iOS and macOS, and BugSplatBridge.setAttribute on Android. The Windows path is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Superseded by #219 — same commit, cherry-picked onto This branch was cut from It also no longer merges. A test merge produces a rename/rename conflict on #219 has the identical change (9 insertions, 2 deletions in |
|
Superseded by #219 |
Note
Stacked PR. This stacks on #123 (
feat/windows-native-crash-reporting) and depends on the Android setter interop from #128, which has already merged into that branch (so the base here isfeat/windows-native-crash-reportingdirectly).Blocked by BugSplat-Git/src-backend#617 (fixes BugSplat-Git/src-backend#616) — the backend change that promotes the
BugSplatDescriptionattribute to the report's description field. Merge that and deploy before (or with) this.Fixes #139
What
SetNativeDescriptionwas Windows-only (BugSplat_SetUserDescription); on iOS, macOS, and Android it silently no-oped, sobugsplat.Description = ...never reached native crash reports on those platforms.This implements it on iOS, macOS, and Android via reserved-attribute promotion: the value is set as an attribute named
BugSplatDescriptionusing the same per-platform interop the sibling setters already use — the bugsplat-apple attribute call (_setNativeAttributeIos/_setNativeAttributeMac) on iOS/macOS, andBugSplatBridge.setAttributeon Android (the pattern #128 established forBugSplatUser,BugSplatEmail,BugSplatNotes, andBugSplatApplicationKey). The backend then promotes that attribute into the report's description field. The Windows path is unchanged.Pre-deploy behavior: until the backend change ships, the value still arrives with the crash report — it just shows as a plain visible attribute named
BugSplatDescriptioninstead of populating the description field. Harmless, and not silent data loss; once the backend deploys, promotion applies with no client change.#ifpermutation verificationHand-verified every permutation of the touched conditional block:
UNITY_IOS/UNITY_ANDROID/etc. co-define withUNITY_EDITOR): every branch carries!UNITY_EDITOR, so the whole block compiles out and the method reduces to thenativeCrashReportingEnabledguard (which can never be true in the editor — all constructor native-init paths are!UNITY_EDITOR). The matching extern blocks compile out too, so no unresolved symbols.UNITY_IOS && !UNITY_EDITOR): calls_setNativeAttributeIos("BugSplatDescription", description); the extern is declared in the same-guarded#if UNITY_IOS && !UNITY_EDITORblock, andSystem.Runtime.InteropServicesis in scope via the guarded using at the top of the file.UNITY_STANDALONE_OSX && !UNITY_EDITOR): calls_setNativeAttributeMac("BugSplatDescription", description); extern declared in the matching#elifblock.UNITY_STANDALONE_WIN && !UNITY_EDITOR): unchangedBugSplat_SetUserDescription(description)P/Invoke; extern declared in the matching#elifblock.UNITY_ANDROID && !UNITY_EDITOR):AndroidJavaClass/CallStaticcome from the unconditionalusing UnityEngine;— identical shape to the sibling Android branches from fix(android): enable native crash reporting flag and wire supported setters #128.nativeCrashReportingEnabledis false there regardless.🤖 Generated with Claude Code