Skip to content

Modder guide : Call replication

6opoDuJIo edited this page Nov 23, 2018 · 19 revisions

The entire mod networking is built around feature that enforces certain methods to be invoked at every client no matter what. Lets call this 'call replication' and call that a day. It's done via Harmony patching shenanigans (yes, mod patches itself). Here's an example of such patch :

ParrotWrapper.ParrotPatchExpressiontarget<Action<Bill_Production, int>>((Bill_Production bill, int count) => BillRepeatModeUtilityPatch.SetBillTargetCount(bill, count));

BillRepeatModeUtilityPatch.SetBillTargetCount is a static method, that does very simple thing - assigns a value to variable :

[MethodImpl(MethodImplOptions.NoInlining)]
public static void SetBillRepeatType(Bill_Production bill, BillRepeatModeDef repeatMode)
{
    bill.repeatMode = repeatMode;
}

If you leave this method untouched, it would work as specified : it would just move repeatMode to a specified field. Nothing exciting, huh? But IF you ParrotPatch it, things gonna change drastically. After patch, calling that method would serialize it's arguments and it's body would be magically called at every client in session, once it's time will come. A little notes, tho :

  1. You have to make sure that very small methods, like that one, won't be inlined. This is why [MethodImpl(MethodImplOptions.NoInlining)] is applied.

  2. You have to make sure that complex objects, like pawns, items, etc, specified in method arguments, already exists at other clients too, including Defs. Stuff like numbers, vectors, strings, etc, straigly serialized as their values. More info about that you will find in Serialization section(WIP).

  3. If method is non-static, you should specify __instance argument and patch should look like that :
    ParrotWrapper.ParrotPatchExpressiontarget<Action<ExampleClass>>((ExampleClass __instance) => __instance.Do()); In this case, calling Do on any instance of ExampleClass, will call ExampleClass.Do method on all clients by serializing ExampleClass and sending method index to call.

  4. ALL clients have to call ParrotPatchExpressionTarget during initialization to entirely the same set of methods, in exact same order. Shouldn't be a big deal, tho.

  5. ThingFilters are very special beasts. Refer to their own section in this wiki (WIP)