Skip to content

feat: Add C# support#629

Open
limbonaut wants to merge 104 commits intomainfrom
feat/dotnet-support
Open

feat: Add C# support#629
limbonaut wants to merge 104 commits intomainfrom
feat/dotnet-support

Conversation

@limbonaut
Copy link
Copy Markdown
Collaborator

@limbonaut limbonaut commented Apr 3, 2026

Adds .NET support to the SDK. Godot games written in C# can now use Sentry on par with GDScript projects, capturing exceptions, scopes, breadcrumbs, and everything else Sentry's .NET SDK supports. The .NET layer ships as a Sentry.Godot library inside the addon and can be wired into the user's .csproj via a single <Import> of Sentry.Godot.props.

How it works

The native C++ layer handles errors from GDScript and the engine, reports native crashes, and submits its own events. This PR adds a .NET layer that does the same for C# code, using Sentry's .NET SDK with its own transport, scopes, and event submission. The two layers are mostly independent - they connect for initialization, coordination and to share options at startup.

The native layer initializes during engine startup, before any .NET assembly loads. On auto-init, options are already resolved on the native side by the time .NET wakes up, so the .NET layer mirrors them across the native/managed boundary instead of re-resolving from scratch.

Initialization works from either side: whichever layer reaches init first signals the other, and a recursion guard makes the second call a no-op. Both auto-init (via [ModuleInitializer]) and manual SentrySdk.Init(callback) end up in the same state.

When Godot invokes managed user code, its managed bridge wraps the call in a try/catch and silently swallows any exception thrown. Godot logs an error, but the exception itself is lost. This PR adds first-chance exception handlers that detect those bridge catches and report them with handled: false.

Two handlers cover the cases: LoggerExceptionHandler is the default and works on all runtimes; CoreClrExceptionHandler is used when the Godot debugger is attached (which suppresses the logger path) and works on CoreCLR only.

On Godot API: The .NET layer doesn't depend on GodotSharp - calling into it early in the application lifecycle caused crashes on Android. Instead, the managed layer reaches Godot engine APIs through P/Invoke and native layer.

PR limitations

Sentry.Godot.SentrySdk does not expose other SentrySdk methods

Will be added in a separate PR. To address this, I want to try a code generation approach, which would make this PR even bigger than it has to be.

Sentry.SentrySdk is not internal

We add Sentry.Godot.SentrySdk, but this creates a footgun situation for users - the original Sentry.SentrySdk remains visible. Initializing Sentry through Sentry.SentrySdk will result in partial initialization. I plan to address this in a separate PR by adding a code analyzer which would produce a compilation warning if Sentry.SentrySdk is used directly.

Follow-ups needed

  • Generate SentrySdk.Dotnet.cs to delegate all upstream Sentry.SentrySdk public methods
  • Add code analyzer to discourage direct Sentry.SentrySdk usage
  • Synchronize scope mutations between layers
  • Synchronize default attachments -- screenshot, view hierarchy, log file
  • Share trace ID between .NET and GDScript errors
  • Auto-import Sentry.Godot.props into user's .csproj
  • Add unit tests for the .NET layer
  • Add integration tests to verify native/managed cooperation
  • Add end-to-end tests
  • And likely more... we'll need to identify GA blockers

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (editor) Add iOS export warning for minimum version by limbonaut in #628
  • Add C# support by limbonaut in #629

Bug Fixes 🐛

  • (build) Set default Apple deployment targets via SConstruct by limbonaut in #627
  • Guard against accessing view hierarchy before scene tree is ready by limbonaut in #630

Internal Changes 🔧

Deps

  • Update Sentry JavaScript to v10.48.0 by github-actions in #638
  • Update Sentry Android to v8.38.0 by github-actions in #636
  • Update Cocoa SDK to v9.10.0 by github-actions in #635
  • Bump addressable from 2.8.8 to 2.9.0 by dependabot in #634
  • Update Native SDK to v0.13.5 by github-actions in #632
  • Update Cocoa SDK to v9.9.0 by github-actions in #631
  • Update Sentry JavaScript to v10.47.0 by github-actions in #624
  • Update Native SDK to v0.13.4 by github-actions in #621
  • Update Sentry Android to v8.37.1 by github-actions in #616
  • Update Sentry JavaScript to v10.46.0 by github-actions in #615
  • Update Sentry Android to v8.37.0 by github-actions in #614
  • Update gdUnit 4 to v6.1.2 by github-actions in #610
  • Bump json from 2.18.0 to 2.19.2 by dependabot in #604
  • Update Cocoa SDK to v9.8.0 by github-actions in #605
  • Update Sentry JavaScript to v10.45.0 by github-actions in #607
  • Update Native SDK to v0.13.3 by github-actions in #606

Other

  • (demo) Replace auto-sending metrics with a tool button by limbonaut in #623
  • (deps-dev) Bump picomatch from 2.3.1 to 2.3.2 in /src/sentry/javascript/bridge by dependabot in #617
  • (web) Adapt and run E2E tests on Web platform by limbonaut in #611
  • Remove leftover uid file by limbonaut in #633
  • Update validate-pr workflow by stephanie-anderson in #625
  • Add PR validation workflow by stephanie-anderson in #618
  • Pin GitHub Actions to full-length commit SHAs by joshuarli in #612
  • Add E2E tests for structured logs and metrics by limbonaut in #602
  • Stabilize flaky SauceLabs integration tests by limbonaut in #601

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against bf75781

limbonaut added 28 commits April 3, 2026 13:21
…wrapper, barebones SentrySdk entry point, move

NativeBridge to Interop namespace, ExampleDotnet demo with buttons
@limbonaut limbonaut force-pushed the feat/dotnet-support branch from bab556b to 9bc4c11 Compare April 8, 2026 12:10
@limbonaut limbonaut changed the title feat: Support for .NET feat: Add C# support Apr 9, 2026
@limbonaut limbonaut requested review from Flash0ver and jpnurmi April 9, 2026 14:12
@limbonaut
Copy link
Copy Markdown
Collaborator Author

limbonaut commented Apr 9, 2026

Testing two cases:

  1. Exception handled in Godot bridge: Godot catches exceptions in try-catch, logs as errors, and the app continues running.
  2. Unhandled exception from worker thread: the Godot game simply crashes.

Linux

Android

macOS

iOS

Windows

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 742a0ab. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant