Setting up a Resonite Headless and getting metrics from it

As you may know, I made a mod for the platform Resonite1 that allows you to export metrics from the game as OpenMetrics2 data readable by Prometheus3 amongst other software.

In this post, we're gonna see:

  1. How to setup a basic Resonite headless on Windows
  2. How to install the Resonite mod loader
  3. How to install the Headless Prometheus Exporter mod
  4. How to install and configure Grafana and Prometheus to scrape the metrics

First, some pre-requisites:

Setting up a headless in Windows is really easy. To first get the headless files, there are two ways that all begin the same way, sending /headlessCode to the Resonite Bot while logged in-game. This will give you the code needed to activate the beta branch for the headless.

Now, to download the headless, you have two ways:

  1. Use the Steam client
  2. Use SteamCMD

Using the Steam client is the easiest. Just right-click on Resonite, hit "Properties", then "Betas", enter the code you previously got into the field and click on "Check code".
You should now be able to select the Headless branch in the small dropdown and will download it automatically to your Steam game folder.

When using SteamCMD, unpack the zip file it comes in in a directory, hold SHIFT and right-click, then select "Open PowerShell window here". Once the PowerShell open, you can use the following command to download the headless (replace your account name, password and headless code):

.\steamcmd.exe +force_install_dir ./resonite +login <account name> <account password> +app_license_request 2519830 +app_update 2519830 -beta headless -betapassword <headless code> validate +quit

You should now be able to find the headless within the resonite\Headless directory near where SteamCMD is unpacked.

Now, to run the mod itself, the headless is not enough, we need to extend it via the Resonite Mod Loader. Its installation is straightforward, as outlined by their README file:

Download ResoniteModLoader.dll to Resonite's Libraries folder (C:\Program Files (x86)\Steam\steamapps\common\Resonite\Libraries). You may need to create this folder if it's missing.
Place 0Harmony.dll into a rml_libs folder under your Resonite install directory (C:\Program Files (x86)\Steam\steamapps\common\Resonite\rml_libs). You will need to create this folder.
Add the following to Resonite's launch options: -LoadAssembly Libraries/ResoniteModLoader.dll. If you put ResoniteModLoader.dll somewhere else you will need to change the path.
Optionally add mod DLL files to a rml_mods folder under your Resonite install directory (C:\Program Files (x86)\Steam\steamapps\common\Resonite\rml_mods). You can create the folder if it's missing, or launch Resonite once with ResoniteModLoader installed and it will be created automatically.
Start the game. If you want to verify that ResoniteModLoader is working you can check the Resonite logs. (C:\Program Files (x86)\Steam\steamapps\common\Resonite\Logs). The modloader adds some very obvious logs on startup, and if they're missing something has gone wrong. Here is an example log file where everything worked correctly.

Those same instructions also apply to the headless software. On certain Windows version, you might want to right click and open the properties of ResoniteModLoader.dll and 0Harmony.dll then check the "unblock" checkbox as it will prevent the mod loader from functioning correctly.

Once this is done, head over to the releases tab of the Headless Prometheus Exporter mod and download the HeadlessPrometheusExporter.dll file. You will need to move this file in the rml_mods folder that should be located in the headless directory. If this folder isn't there, you can create it. Also check the properties of this dll as well for the unblock checkmark.

Now that the mod installation is done, we have one last step: configuring our headless. This step is also incredibly easy, being documented on the Resonite Wiki.
I can recommend going in the Config folder then copying the example configuration file provided to fit your needs. It is not recommended to start out with a "minimal" configuration file, that might lack some essential settings and result in the headless not working as intended or not starting at all. Once you are familiar with what goes where and does what, feel free to trim the configuration file following your needs.

If you still have your PowerShell window open, you can type cd resonite\Headless to navigate to where the executable is and then use the following to start it with the mods:

.\Resonite.exe -LoadAssembly Libraries\ResoniteModLoader.dll

After waiting a bit for it to start, you should be able to visit https://localhost:9000 in a web browser and see some metrics being displayed such as some world stats, engine FPS and many others.

Now we can tackle the last issue: how to display those metrics. For this, we're going to use Prometheus in combination with Grafana, which are hands-on probably the best solution for this.

We're gonna use my pre-made minimal Grafana setup for this. You can obtain the files by either using git clone https://g.j4.lc/general-stuff/configuration/grafana-minimal-setup.git or by downloading a ZIP or tarball of the source.

Getting started with it is extremely easy, but first, let's go through each configuration file and what it does.

First, let's open prometheus/prometheus.yml:

  - job_name: 'resonite-headless'
    scrape_interval: 15s
      - targets: ['host.docker.internal:9000']

This one is fairly simple. This configures Prometheus, whose job is to aggregate the metrics the mod is exposing. In this particular configuration, we are telling it to scrape our headless at the address host.docker.internal:9000 every 15 seconds.

Note that in most cases, you would need to use localhost:9000 or container_name:9000; host.docker.internal is only used because the headless is not in a container and on the host machine.

This leads us to our docker-compose.yml:

        image: grafana/grafana
            - 3000:3000
            - ./grafana:/var/lib/grafana

        image: prom/prometheus
            - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
            - ./prometheus/data:/prometheus
            - '--config.file=/etc/prometheus/prometheus.yml'
            - '--storage.tsdb.path=/prometheus'
            - '--log.level=debug'
            - 'host.docker.internal:host-gateway'

It basically defines two services:

  • Grafana, which:
    ; Will store all of its data in the directory ./grafana
    ; Will be accessible on the port 3000
  • Prometheus, which:
    ; Will store all of its data in the directory ./prometheus/data
    ; Have access to the configuration file mentioned earlier
    ; Pass the localhost of the machine inside (as well as passing some command-line configuration arguments defining the configuration file path and the storage path)

Now that this is out of the way, open a PowerShell window in the directory where the docker-compose.yml file is located and make sure Docker is launched. There, just write docker-compose up -d and watch Docker automatically pull and start the images. If you are curious, you can use docker compose logs -f to see all the logs in real time.

After waiting for a minute or two, you can visit http://localhost:3000 in a web browser to setup Grafana. The default login should be admin and admin for both user and password.

Once in Grafana, head to the menu on the left, go in "Connections" then "Data sources". There, select "Add new data source" and select Prometheus. You will only need to set the URL of said source to http://prometheus:9090, you can then go on the bottom and click on "Save & Test".

You can now either select to go to the explore view or create a brand new dashboard on the top right of the screen. I can recommend playing with the Explore view for a bit before starting to build dashboards, as it will teach you the different types of visualisation as well as some useful queries.

Some metrics you can query include:

totalPlayers 1
totalWorlds 1
world_users{label="S-09444920-caab-4c3d-a242-a50b028c33e6"} 1
world_maxusers{label="S-09444920-caab-4c3d-a242-a50b028c33e6"} 16
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalCorrections"} 82
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalProcessedMessages"} 4049
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedConfirmations"} 0
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedControls"} 3
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedDeltas"} 554
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedFulls"} 0
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedStreams"} 3491
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentConfirmations"} 554
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentDeltas"} 2410
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentControls"} 3
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentFulls"} 1
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentStreams"} 106
engineFps 59.98274
completedGatherJobs 0
startedGatherJobs 0
failedGatherJobs 0
engineUpdateTime 0.0004873

Noting that the S-09444920-caab-4c3d-a242-a50b028c33e6 is not a set variable as it is a session ID.

Now that you've done all of this, you can now enjoy having a nice dashboard displaying metrics about your headless!

You can also expand on the subject by reading further:

Thoughts? Leave a comment