Skip to content

Free patching

Zetrith edited this page Mar 24, 2024 · 3 revisions

Free patching will likely get removed in the future in favor of more structured options.

Prepatcher calls all static methods annotated [FreePatch] having a single Mono.Cecil.ModuleDefinition parameter after it finishes applying its normal patches.

The method has to be in a static class.

An example of a free patch from Prepatcher's test suite:

public static class FreePatching
{
    [FreePatch]
    static void RewriteAssembly(ModuleDefinition module)
    {
        var type = module.GetType($"{nameof(TestAssemblyTarget)}.{nameof(RewriteTarget)}");
        var method = type.FindMethod(nameof(RewriteTarget.Method));

        foreach (var inst in method.Body.Instructions)
            if (inst.OpCode == OpCodes.Ldc_I4_0)
                inst.OpCode = OpCodes.Ldc_I4_1;
    }
}

Note that the Mono.Cecil.ModuleDefinition used here is currently the one bundled internally inside 0Harmony (you might have to publicize it).

Each method is called once and the parameter is supplied with RimWorld's AssemblyCSharp.dll about to be reloaded. This allows mods to mutate the game's assembly freely applying any patches.

The methods are called in the order of the mod list.

This is an advanced feature, you are on your own and no support is provided. Creating incompatibilities with other mods and breaking the game is very easy to achieve using this tool but at the same time it is very powerful.

It exists because Prepatcher's current API is fairly minimal. It is meant to be used mostly for experimentation.

FreePatchAll attribute

In addition to the FreePatch attribute, a FreePatchAll attribute is available which works the same way but gets called with every patchable assembly that Prepatcher processes.

The patchable assemblies are: all mod assemblies and RimWorld's Assembly-CSharp.dll.

System assemblies (Unity and .NET assemblies from RimWorldWin64_Data/Managed) are not patchable.

An example:

[FreePatchAll]
static bool RewriteAssembly(ModuleDefinition module)
{
    if (module.Name != "Assembly-CSharp.dll")
        return false;
    // Do patches on Assembly-CSharp.dll...
    return true;
}

All free patches can optionally return a bool signifying whether the free patch modified the given assembly. A void return type is treated like returning true.

This is a performance hint: if an assembly is not modified Prepatcher doesn't have to serialize it which speeds up loading.

Clone this wiki locally