I’m a big fan of the Godot game engine, and lately, since players have been testing the game more often, I’ve decided to add some monitoring solution to get a better view of what’s failing players as they try to play.

In this post I’ll show how to integrate Sentry’s .Net SDK in Godot and use it to receive usage reports and error monitoring.

What’s Sentry?

Sentry is an open-source error monitoring tool I picked up on a while ago.

I initially started using it because it was open-source and I was quite the cheapskate, but, it really is an awesome and visually delightful tool.

Aside from collecting error reports and alerting you when the application fails, it makes it simple to integrate information and events you care about in those reports. Also, it’s fairly easy to integrate and get started collecting data with, even if you’re not that much of a friend to coding.

Sentry

So, if you want a dashboard filled with reports about how your game crashed, to who, why and what were they doing leading to the crash, keep reading.

Dependencies

First, let’s grab the required binaries. Installing the Sentry SDK collects a few other assemblies so I added all of them to one packages.config for convenience:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Sentry" version="2.1.0" targetFramework="net45" />
  <package id="Sentry.Protocol" version="2.1.0" targetFramework="net45" />
  <package id="Sentry.PlatformAbstractions" version="1.1.0.0" targetFramework="net45" />
  <package id="System.Collections.Immutable" version="1.5.0" targetFramework="net45" />
</packages>

Run NuGet to fetch all those into your project’s ./packages/ folder.

$ mono nuget.exe restore

If you don’t have NuGet yet you can grab it here.
The Windows x86 Commandline version is runnable using mono.

Add references to the Sentry assemblies to your project:

<ItemGroup>

  <Reference Include="Sentry, Version=2.1.0.0">
    <HintPath>$(ProjectDir)/packages/Sentry.2.1.0/lib/net461/Sentry.dll</HintPath>
    <Private>True</Private>
  </Reference>

  <Reference Include="Sentry.Protocol, Version=2.1.0.0">
    <HintPath>$(ProjectDir)/packages/Sentry.Protocol.2.1.0/lib/net46/Sentry.Protocol.dll</HintPath>
    <Private>True</Private>
  </Reference>

  <Reference Include="Sentry.PlatformAbstractions, Version=1.1.0.0">
    <HintPath>$(ProjectDir)/packages/Sentry.PlatformAbstractions.1.1.0/lib/net471/Sentry.PlatformAbstractions.dll</HintPath>
    <Private>True</Private>
  </Reference>

  <Reference Include="System.Collections.Immutable, Version=1.2.3.0">
    <HintPath>$(ProjectDir)/packages/System.Collections.Immutable.1.5.0/lib/portable-net45+win8+wp8+wpa81/System.Collections.Immutable.dll</HintPath>
    <Private>True</Private>
  </Reference>

</ItemGroup>

Initialization

Using Godot we don’t really have an initialization section so I created a dedicated node (here as SentryController) to hold the Sentry initialization logic.

Sentry Controller Node

Pay close attention to the node placement in the scene tree.

The Sentry controller node must come before any application nodes. Godot invokes their _Ready() methods according to their ordering on the tree, and any exception thrown before SDK init will not be reported.

After creating an account and a project Sentry will suggest an initialization clause with your project’s Dsn, similar to this:

using (SentrySdk.Init("https://451cf9b0fd9e4f0294a0d15b6c36bce2@sentry.io/5170198")) {
    // App code
}

After creating a project this page will contain code examples with your project’s Dsn in place.

So after adding it to your node it should look somewhat like this.

public class SentryController : Node {

    public override void _Ready() {

        SentrySdk.Init(o => {
            o.Dsn = new Dsn("https://451cf9b0fd9e4f0294a0d15b6c36bce2@sentry.io/5170198");
        });
    }
}

Error reporting

Now that the client is set up, any unhandled exceptions thrown will get reported to the sentry API, and appear on your dashboard with a full trace.

For example, lets set a button that throws an exception:

public class App : Node2D {

	private void _on_CrashButton_pressed() {
		throw new Exception("Oh lord jesus it's a fire!");
	}
}

Which after getting pressed will cause an error to appear on your issue dashboard.

Error Issue

A neat feature of Sentry that I had to cover.

This allows you to log the user’s behavior, potentially, leading to the error. Giving you a better view into what exactly happened before the crash.

The code is fairly simple, for example, lets log a button click (exciting, I know :P).

public class App : Node2D {

    ...

    private void _on_AwesomeButton_pressed() {
        SentrySdk.AddBreadcrumb("Awesome button clicked");
    }
}

After a few clicks on this button, and then, a hit to the ol’ crashing button, we’ll get this neat report:

Sentry breadcrumbs

Example Project

I’ve prepared an example project demonstrating the Sentry integration here:

https://github.com/mastern2k3/godot-sentry-example

There is a small caveat for people looking to export for Android, read ahead.

Android

Although making all this goodness to work on Android should’ve been an export prest away, things turned out to be trickier.

The Android prest kept failing the mono assembly preparation phase because of an apparent missing assembly named System.Web.

ERROR: get_assembly_dependencies: Cannot load assembly (refonly): 'System.Web'.
   At: modules/mono/editor/godotsharp_export.cpp:93.
ERROR: get_assembly_dependencies: Cannot load one of the dependencies for the assembly: 'Sentry'.
   At: modules/mono/editor/godotsharp_export.cpp:98.

Finding the answer to this little bugger took me a good two days of research, trial and error, so I’ll cut to the chase.

After digging through the .Net Sentry SDK I found out that the System.Web assembly (required by the net461 variant of the Sentry.dll assembly) is not a strict requirement, and that on other framework outputs its reference is omitted.

So, on the last trial, I ended up forking the SDK in an attempt to produce a Sentry.dll that did not reference System.Web.

You can download a compiled version of it here.

Important: The example project references the compiled fork version so it appears as such in the .csproj file:

<Reference Include="Sentry, Version=2.1.0.0">
  <HintPath>$(ProjectDir)/../forks/sentry-dotnet/src/Sentry/bin/Release/net461/Sentry.dll</HintPath>
  <!-- <HintPath>$(ProjectDir)/packages/Sentry.2.1.0/lib/net461/Sentry.dll</HintPath> -->
  <Private>True</Private>
</Reference>

If you’re not interested in any of this Android nonsense you can comment / uncomment whichever line fits your needs.