Decompiling and using Harmony

## Why Harmony

When making a mod you have to tell the game when to execute certain functions. That can be done by inheriting from `Game.Mod.AbstractMod` (or `Game.Mod.IGameMod` directly) and listening to events, but that is not always enough. If you want to change more than you can reach from the resources you're given by the game or want to do it at a time when no events are invoked, you will have to edit the in-game methods and properties themselves. This is where Harmony comes in. Harmony is a complex library created by Andreas Pardeike that lets you add code before and after in-game methods or even rewrite them entirely at runtime. In this tutorial you will use Harmony to give every node (tracks/signals/tunnels/etc) a random color. You should also definitely read the Harmony documentation. It is also recommended that you try Making your first mod first if you haven't yet.

## Decompiling

> *I am fine with decompiling, is needed to mod anything* - Angel
>
>

First, you will need to know which method or property controls node colors. To find out, we will use ILSpy to decompile the Rail Route assembly. While decompiling is not always legal and may not even be useful if the assembly is obfuscated, in this case it will work and is tolerated. Of course, you may still not distribute the decompiled assembly. To start, create a new C# project in Visual Studio (call it 'RandomColors' or something) and go to `Extensions > Manage Extensions`. Search for ILSpy and install it. Add a `RailRoute.dll` reference, then rightclick the reference and select 'Open in ILSpy'. A new window will open. Let's search for 'color'. You will see a lot of results from Unity assemblies:

We can filter for just Rail Route results by searching for `color inassembly:RailRoute` instead. Because we know we're looking for something node-related, we can ignore a bunch of the top results. The first thing we find that is related to nodes is `Game.BoardNode.Colors`. If you rightclick it and select 'analyze' you can see where this field is used:

I happen to know from Heatmap that the `CurrentColor` property is the thing we are interested in here (you can also deduce it from the code of course, that's how I originally found it). Doubleclick it (or `rightclick > Decompile to new tab`) and we see that the getter of this property indeed does color things based on the node's state. We are going to change what this getter returns with Harmony.

## Harmony

Get a copy of Harmony from https://github.com/pardeike/Harmony/releases or by installing the Lib.Harmony NuGet package. To change the return value of `BoardNode.CurrentColor` we must create so-called postfix patch. First create a new `ColorPatch.cs`, with a ColorPatch class. Make it internal, because no other assemblies should have access to this class. To make it a Harmony patch we must use the HarmonyPatch attribute: `[HarmonyPatch(typeof(BoardNode), "get_CurrentColor")]`. Using `nameof` for the second argument is a good idea, but unless you want to use `typeof(BoardNode).GetProperty(nameof(BoardNode.CurrentColor)).GetGetMethod().Name` that's not really an option here. Just in case skipping the original code creates annoying bugs because of side-effects, we will change the result after the original has run. If you can see that skipping the original does not create a problem you should do that instead. Create a `private static void Postfix(ref Color __result)`. It has to be called Postfix because Harmony looks for that specific method name - but you can call it something else if you use the `HarmonyPostfix` attribute. The `__result` argument is how we will change the return value, so in the Postfix method we will assign a new randomly generated color to it. Here's the full `ColorPatch.cs`:

```
using Game;
using HarmonyLib;
using UnityEngine;

namespace RandomColors
{
 [HarmonyPatch(typeof(BoardNode), "get\_CurrentColor")]
 internal class ColorPatch
 {
 private static void Postfix(ref Color \_\_result)
 {
 \_\_result = Random.ColorHSV();
 }
 }
}

```

Your IDE may complain about the unused private method, you can ignore it or suppress it if you want. To apply the patch we must call HarmonyLib.PatchAll, so let's create a `RandomColors.cs` with the usual AbstractMod stuff and apply the patches when the mod is loaded:

```
using System.Threading.Tasks;
using HarmonyLib;
using Game.Mod;
using Utils;

namespace RandomColors
{
 public class RandomColors : AbstractMod
 {
 public override CachedLocalizedString Title => "RandomColors";

 public override CachedLocalizedString Description => "Randomizes node colors";

 private const string \_id = "mod.creatorname.modname";

 private readonly Harmony \_harmony = new Harmony(\_id);

 public async override Task OnEnable()
 {
 // Apply patch when the mod is started
 \_harmony.PatchAll();
 await Task.Yield();
 }

 public async override Task OnDisable()
 {
 // Remove patch when the mod is disabled
 \_harmony.UnpatchAll(\_id);
 await Task.Yield();
 }
 }
}

```

The id string can be anything you want as long as it is unique. Harmony recommends a reverse domain notation, so I personally like to use something like 'mod.asdfcyber.heatmap' ('mod' is not a valid tld but I think it looks nice). We're now ready to build and test the mod (don't forget to also place the 0Harmony.dll file in your mod folder).

As you can see, Harmony and ILSpy can be incredibly powerful modding tools. Here are some other things you can do with Harmony: access and edit the arguments and results of original methods, skip the original method altogether, change the IL code itself, read/write to fields and properties, add new methods to the original assembly... Don't forget to read the documentation at https://harmony.pardeike.net/articles/intro.html, because there's no way I am going to be able to explain everything. If you are going to release your mod you should include Harmony's copyright notice and permission notice, the MIT license it's released under tells you to do so.

Note: since this example and Heatmap both patch CurrentColor they're not compatible. They won't break, but Heatmap won't be able to color the tracks because it gets overridden by RandomColors. I'm not entirely sure what determines which patch comes last (I think it's by mod loading order in which case it's alphabetical), but I'm sure it's explained somewhere.