Patching Game Code
In this guide you will learn how to patch game code.
What are patches?
Patches are a way to change the behavior of a game without directly changing the source code. In the context of WorldMachineLoader, patches allow mods to modify or extend the behavior of game methods using the [GamePatch]
annotation/attribute.
How do patches work?
The patch system in WML is based on attributes and reflection. When the loader is started:
- All methods marked with
[GamePatch]
attributes are automatically detected. - These methods are processed by the
PatchManager
class in the loader and applied to the corresponding methods. - Patches can be private or public, but they MUST be static.
- Each patch is applied once, and only on load.
How do I know what to patch?
You will need to use a decompiler to determine which parts of the game code need to be patched. For obvious reasons, it is STRICTLY forbidden to post the source code of the game publicly. So you should use a decompiler like ILSpy (recommended), dnSpy, or the built-in decompiler in Visual Studio 2022. With its help, you can analyze the game source code, identify necessary sections for modification and prepare appropriate patches.
What can be patched?
- Any public/private methods of game classes.
- Static and non-static methods.
- The same method can be patched by multiple mods.
Example of simple patch
using OneShotMG;
public class MyMod : IMod
{
[GamePatch(typeof(Game1), "Initialize", PatchType.Prefix)]
public static void GameInit_Patch()
{
Console.WriteLine("Game starts to initialize!");
}
}
In this example, we are patching the Initialize
method of the Game1
class with the Prefix patch type. Now let's look at the existing patch types.
Patch Types
There are currently 4 types of patches: Prefix
, Postfix
, Transpiler
and Finalizer
. Let's talk about what each type does.
Prefix
:- Runs before the original method. Allows you to change the input arguments or cancel the call to the original method by returning
false
.
- Runs before the original method. Allows you to change the input arguments or cancel the call to the original method by returning
Postfix
:- Runs after the original method. Gives access to the return value and allows it to be modified before passing it on.
Transpiler
:- Modifies the IL code of the method. Allows insertion, deletion, or replacement of instructions at a low level.
Finalizer
:- Called after execution of the original method and all
Postfix
. Can handle exceptions raised in the original method and perform finalizing actions.
- Called after execution of the original method and all
You will probably only need Prefix
, Postfix
, and sometimes Finalizer
to create a mod.
Patching a method with arguments
Patching a method with arguments is done a little differently. Since a method can have overloads, when creating a patch with the [GamePatch]
attribute, another argument is added - argumentTypes. Even if the method has no overloads, argumentTypes still needs to be specified.
[GamePatch(typeof(TWMFilesystem), "CreateWallpaperFile", PatchType.Prefix, typeof(string))]
public static void CreateWallpaperFile_Patch(string wallpaperId)
{
Console.WriteLine($"Wallpaper ID: {wallpaperId}");
}
In this example, we are patching the CreateWallpaperFile
method of the TWMFilesystem
class, which has an argument of type string
.