Modding Grow

The following limited set of instructions is a copy of those relayed to a user who assisted in the the creation of my ConfigureLimits mod.

While a proper guide will eventually be written, I am recording this in the case it may be useful to others in the meantime. --NotAracham (talk) 02:03, 10 January 2023 (UTC)

Software Needed/Referenced

 * BepInEx - the patching tool used to insert modded code into the game on launch
 * Visual Studio 2019 Community Edition - A freely-available integrated development environment (IDE) where you can write script that can be turned into a mod file.
 * DNSpy - A tool used to explore .dll files and see the objects/methods/functions within. It can also edit these .dll files, fundamentally changing how the game operates.
 * Folderpath for critical DLL:, filename is Assembly-CSharp.dll
 * AssetStudioGUI (optional) - A tool that can explore the packed resource files which contain images/models for in-game assets like wallpapers and characters. Not covered by this guide, but good to know about.

Initial Setup of IDE
1. Like the install instructions on my mod page (ConfigureLimits on NexusMods), I downloaded BepInEx and put it into Grow's root directory in the steam library, then ran the game once to generate all the extra directories/files I'd need

2. I downloaded and installed Visual Studio community edition, which is free for everyone: https://visualstudio.microsoft.com/vs/community/

3. Launch Visual Studio and click on 'Create a New Project'

4. Choose the Class Library (.NET Framework) option, check the box to place solution and project in the same directory, then click Next

5. Adjust any project or solution names as you see fit (this will be the name attached to the .dll file you create) and make sure that the framework selected is 4.7.2 (this is the version compatible with grow)

6. Note the directory the project will be created in, then click Next

7. After a few seconds, the project will be created! Now we can move on to part two...

Dependencies + Importing References
1. Now, navigate to the directory for your mod project (usually something like C:\users\ \source\repos\ )

2. Inside this folder, we'll create a new folder called Libs. This is where we'll put all the reference files we'll need for the IDE to do what it needs to.

3. Open a new file explorer window, then head to Grow's root directory and dive down into Grow_Data > Managed. Copy the Assembly-CSharp.dll file and paste it into your newly-created Libs folder

4. Repeat this for the UnityEngine.dll and UnityEngine.CoreModule.dll files in the same directory

5. Back out to the root directory again, then dive to BepInEx > Core

6. Copy over the 0Harmony.dll and BepInEx.dll files to the Libs directory as well

7. Now, return to the Visual Studio window where we've set up your project. On the right-hand Solution explorer pane, you should see properties, references, and your class file.

8. Expand References, then right click and choose 'Add Reference'.

9. Click the 'browse' button at bottom right, then navigate to the Libs directory that's full of the .dlls you borrowed. Add each one this way, then click OK after all have been added.

Now we're ready to start actually coding in part three!

Adding References + wakeup script
1. While some 'using xxx;' lines already probably exist at the start of your project, you'll need a few more for Visual Studio to recognize all the code you're going to want to write. These lines essentially say 'bring this function of the .dll into what I'm doing'

Example: using System; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using HarmonyLib.Tools; using UnityEngine; using UnityEngine.Diagnostics; using BepInEx.Logging;

Once you've got all that sorted, it'll look something like this:

2. Next step is to set up a namespace, this is essentially 'everything that is part of the mod' now, so pick a namespace that matches your mod.

Syntax is:

namespace Mod_Name {   //more code goes here }

3. Next step is telling our mod loader (BepInEx) important locations for this mod.

This takes the syntax: [BepInPlugin("CreatorName.PatchingTool.ModName", "long name of mod to display", "x.x.x.x")], replacing those values with the actual appropriate name, long name, and version number to tell BepInEx more info about our mod

We'll add the game executable we want to target on launch in a similar way, as:

[BepInProcess("Grow.exe")]

4. The next step is creating our entry script to make BepInEx patch the game. This is fairly straightforward, but the most basic version won't do terribly much by itself.

Example to start from: namespace Grow_ConfigureLimits {   [BepInPlugin("aracham.bpx.Grow-ConfigureLimits", "Grow-ConfigureLimits", "1.1.0.0")] [BepInProcess("Grow.exe")] internal class Mod : BaseUnityPlugin {       private void Awake {           //Creating Harmony Patcher Instance var harmony = new Harmony("aracham.bpx.Grow-ConfigureLimits");

//Replacing default patch-all function for granular control over patching behavior based on config variables harmony.PatchAll; }   }

What this is doing is creating a 'class' called Mod, where we're going to put our instructions to execute.

We're also making it an internal class so it doesn't accidentally override another mod file's class of the same name and break their instructions

We're then defining a method called Awake, which will run as soon as the game launches and BepInEx starts patching stuff. Code inside Awake will always attempt to run.

We're then creating a new instance of one of BepInEx's patching tools, named Harmony.

We're then instructing harmony to try to run every patch that comes later in our file.

Right now we have no patches created, so this doesn't actually do anything. Example image of current state:



At this point, we have our IDE set up, a basic namespace and patch script, but no actual patches to apply and no options for users to change. That we can handle in part four...

Config and First Patch
First off, let's give our users something they can change. BepInEx makes it extremely easy to create and read a configuration file.

In this example, we'll extend the maximum building counts.

1. After the mod class but before the Awake function, we'll add in a record for ConfigEntry, like so:

public static ConfigEntry BuildingLimit { get; set; }

This either gets the existing value of ( or creates a new record with an expected data type of integer for) a property called Building limit

2. Now, we'll add the actual entry that should get created in the configuration file, just after the awake function but before creating a harmony intance, like so: //generates initial configuration settings BuildingLimit = Config.Bind("Build Limits - Numeric",  // The section under which the option is shown                            "Max Buildings",  // The key of the configuration option in the configuration file                            20, // The default value                            "Adjusts the per-district Buildings maximum from the native limit of 20. Accepted Values: Numbers Only."); // Description of the option to show in the config file

In order, this is telling BepInEx:


 * what category to put this option in
 * what friendly name to call this option in the config file
 * what the default value should be when the file is created
 * what explainer text should be added to the config file for the user

After you're done, it should look like this:



Great, we now have a configuration file that will get generated (location: grow root directory > BepInEx > config) but still no patch, so now we get to the more complicated part.

The first part is to figure out what method(s) we need to change, and what type of patch we need.

Harmony patcher can do a few types of patches by default:


 * Prefix: Stop an existing method from running or change what inputs go into a method when it's called
 * Postfix: All existing code remains the same, but we change what it returns when called (e.g. false becomes always true, 40 becomes 4000)
 * Transpiler: Write new code into an existing method (this one is really hard to do right and can break files)

There are more patch types, but they're complicated.

More info/documentation here: https://harmony.pardeike.net/articles/patching.html

3. In this case, from poking around with DNSpy, we know we want to alter the outcome of the get_MaxBuildings function that is part of the DistrictTemplate class. Because we're altering the outcome, this will be a Postfix. Also, because of our earlier work, we now have a configuration value that we can use to set a new maximum building limit.

4. Add a few returns just before the final closing bracket for the namespace, because we're now creating a second class in the same namespace!

Here's an example of how to write the postfix patch, with comments for each major line //Defines a harmony patch using the class DistrictTemplate and the method get_MaxBuildings [HarmonyPatch(typeof(DistrictTemplate), "get_MaxBuildings")]

//Provides the name of our patch class (PatchMaxBuildings) public class PatchMaxBuilding {   //Creates a method called Postfix_BL that will reference an integer and override the result (__result) of the original method get_MaxBuildings public static void Postfix_BL(ref int __result) {       //Checks to make sure the value of building limit we set in the class Mod earlier is greater than or equal to 20 if (Mod.BuildingLimit.Value >= 20) //If that check passes, we now set the result of get_MaxBuildings to our new value from the configuration file __result = Mod.BuildingLimit.Value; } }

At this point, our basic patch is complete and we should now have code that looks like this:

After all this is done, we're now ready to export and start testing our mod in-game! Continued in Part Five...

Building & Patching
1. Up until this point, you likely have not saved. While it isn't strictly necessary, it's recommended to save at regular intervals, especially before attempting to compile your code into a final build, which we are about to do.

2. Under the build menu, you'll have a couple options. You can either choose to build the full solution or just the project you've been working on. If you eventually have more than one project in the same 'solution', you'll eventually want to use this second option. Either is fine for now.

3. If everything is solid with the code, you'll see some activity in the output pane at bottom, followed by a message like " build: 1 succeeded". If there were any issues in the code design, you'll instead see an error message describing the line where there were problems found.

4. Navigating to your Repos folder,  you can now dive into >> bin >> Debug to find the completed build of your patch .dll file. The .dll will have the same name as the project you specified earlier.

5. Copy this .dll file only, then navigate to Grow root directory > BepInEx > plugins and paste a copy of your patch .dll file here

6. To make sure we see whether our patch is successful when the game launches, navigate to BepInEx > Config and open BepInEx.cfg in a text editor. Under Harmony/Logger, change LogChannels to All and save.

7. Now, launch Grow.

Within seconds, you should be able to open the LogOutput.log file in the main BepInEx directory and see details indicating that your patch was loaded successfully.

You should also now see a new .cfg file in the config folder matching your mod's "CreatorName.PatchingTool.ModName" name where you can change the building limit you set earlier.

Additional Resources

 * Getting started with BepInEx Modding
 * Harmony Patching Documentation
 * Additional examples of Harmony Modding from Outward