Bannersample - An example mod using Harmony

Users who are viewing this thread

schplorg

Recruit
Hey guys, I created a small example project for anyone to start their own projects/mods from.
It uses Harmony to patch existing methods from the game with your own implementations, more on that in the Readme and Harmony documentation.
https://github.com/schplorg/bannersample-mod

Download the .zip here
https://github.com/schplorg/bannersample-mod/archive/master.zip

I included my steps to get up and running with modding in the Readme.md but if you need more information I can recommend the
community-modding-documentation by user Ster.
 
Hi, I don't quite understand what you mean in this passage under the installing section of your readme

Alternatively, copy the resulting Bannersample.dll along with any other .xml you reference to your new module path. For this example the structure is <YOURGAMEPATH>\Modules\Bannersample\bin\Win64_Shipping_Client\Bannersample.dll <YOURGAMEPATH>\Modules\Bannersample\SubModule.xml

What is the resulting .dll resulting from, and where exactly am I copying it?
 
Great guide, however, there's actually a simpler/more organized way of doing this.
  1. Create a new Harmony instance called Patcher (can be anything).
  2. Call Patcher.PatchAll() in your OnSubModuleLoad() override.
  3. Create a new static class called Patches (can be anything).
  4. Create a new subclass for your Patches class that looks something like this:
    Code:
    [HarmonyPatch(typeof(YourTypeHere), "YourMethodNameHere")]
    public class YouCanNameThisWhateverYouWant
    {
    // Will be invoked after the original method stated above has run.
    static void Postfix()
    {
    InformationManager.DisplayMessage(new InformationMessage("Test Message"));
    }
    }
  5. If you want to patch something else, repeat step 4 with a new subclass.
Edit: Clarified some things.
 
Hi, I don't quite understand what you mean in this passage under the installing section of your readme



What is the resulting .dll resulting from, and where exactly am I copying it?
The .dll is resulting from building your project with your IDE, in my case Rider/MSBuild. It places builds in a folder named bin/Debug or bin/Release, VS2019 should be similar. Try to compile a regular C# console app and see where the .exe is placed.
Where to copy? Inside your Bannerlord game folder, to be exact the .dll goes to <GAMEFOLDER>/Modules/Bannersample/bin/Win64_Shipping_Client/Bannersample.dll
To actually load it you need to place the SubModule.xml here <GAMEFOLDER>/Modules/Bannersample/SubModule.xml

btw, thanks Ster - I used manual patching as a quick + dirty solution to be able to overwrite sealed classes, since you can't get their typeof()
 
so I'm new to all this c# stuff, I don't really get what the point of harmony is? can't we already make a mod that overrides only a specific thingy?
 
so I'm new to all this c# stuff, I don't really get what the point of harmony is? can't we already make a mod that overrides only a specific thingy?
Harmony is needed if you want to do really complex stuff.
C# is needed if you want to add new functionality.
XML is enough for item/character editing.
 
Is it possible to replace a segment of a method or an entire method with Harmony?
With what they call a transpiler, yes, but you'll have to learn about CIL. However dealing with stack-based languages is messy and I wouldn't recommend it. You should be able to do pretty much everything you want with prefixes and postfixes, even if it means letting the original code run.
But here's a short example of what it could look like. I'm simply multiplying two values.
C#:
private static IEnumerable<CodeInstruction> GetAttackCollisionResultsTranspiler(IEnumerable<CodeInstruction> instructions)
{
    // This is the list of instructions I want to insert.
    List<CodeInstruction> MyLittlePoney = new List<CodeInstruction>
    {
        new CodeInstruction(OpCodes.Ldarga_S, 5),
        new CodeInstruction(OpCodes.Ldarg_0),
        new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(Blow), nameof(Blow.InflictedDamage))),
        new CodeInstruction(OpCodes.Conv_R8),
        new CodeInstruction(OpCodes.Ldsfld, AccessTools.DeclaredField(typeof(SettingsManager), nameof(_settings))),
        new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(Settings), nameof(_settings.Charge))),
        new CodeInstruction(OpCodes.Callvirt, AccessTools.DeclaredPropertyGetter(typeof(Settings.ChargeConfig), nameof(_settings.Charge.Charge_Damage_Multiplier))),
        new CodeInstruction(OpCodes.Mul),
        new CodeInstruction(OpCodes.Conv_I4),
        new CodeInstruction(OpCodes.Stfld, AccessTools.DeclaredField(typeof(Blow), nameof(Blow.InflictedDamage)))
    };
    List<CodeInstruction> currentInstructions = instructions.ToList();
    List<CodeInstruction> newInstructions = new List<CodeInstruction>(instructions.Count() + MyLittlePoney.Count());
    CodeInstruction currentInstruction;

    // I'm looking for a very specific pattern where to insert my instructions.
    for (int i = 0; i < currentInstructions.Count; i++)
    {
        currentInstruction = currentInstructions[i];
        if (currentInstruction.opcode == OpCodes.Ldind_Ref)
        {
            CodeInstruction previousInstruction = currentInstructions[i - 1];
            if (previousInstruction.opcode == OpCodes.Ldarg_S && previousInstruction.operand == typeof(WeaponComponentData))
            {
                newInstructions.AddRange(MyLittlePoney);
            }
        }
        // I'm adding back all read instructions since I'm not removing anything.
        newInstructions.Add(currentInstruction);
    }

    return newInstructions;
}
If you did something wrong, at best it breaks when patching. Otherwise, well you can't really put any break point so...
 
so I'm new to all this c# stuff, I don't really get what the point of harmony is? can't we already make a mod that overrides only a specific thingy?
LOL this wasn't expected. After writing this message you created some of the most used mods with help of harmony :smile:
Now I'm trying to do same thanks to you since you are not updating no limit mods I'm trying to create mine. (Not trying to update yours, I'm trying to find another way to achieve same for more compatibility. Unfortunately road is long, I have no C# backbone... yet.)
 
For those starting out with Harmony just like me, I like to help my fellow new modders because I could find 0 forum posts or docs about this and wasted a lot of time!

If Bannerlord keeps crashing with harmony at the start, make sure the 0harmony.dll in your /bin folder is the correct one. I grabbed the dll from

and used the debug dll instead of the release one.

Finally I just grabbed a 0harmony.dll from an existing nexus mod and that worked. ?
 
You just need to use same .NET version for your mod and Harmony. In my case .NET 4.7.2. (My first trial was 4.8 but, after I learnt game created on 4.7.2 I switched to this, it's working fine.)

---

Edit: Also this template really helps a lot when starting. After you learn things, it is still useful because it allows you to create mods seamlessly and faster.
 
Back
Top Bottom