rwmt / Multiplayer

Zetrith's Multiplayer mod for RimWorld

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

(Mod conflict) General Multiplayer patches can fail on generic types

SokyranTheDragon opened this issue · comments

The issue is only relevant in case of modded games, as none of the vanilla types we end up patching are generic. The only known conflict to date is Project Rimfactory Revived - specifically, patches on all subtypes of Thing.

The type (and its subtypes) which is causing issues with MP: https://github.com/zymex22/Project-RimFactory-Revived/blob/d4f35fda244a1e6359a918a7bda9a6b3a84eef1e/Source/ProjectRimFactory/AutoMachineTool/Building_Base.cs

A possible solution would be to check if the type is generic and call Type.MakeGenericType(). However, this is not a straightforward solution, as we'd have to provide arguments to the method which fulfills all the generic type constraints.

The following patches are (likely) capable of failing due to attempting to patch methods on non-generic types:
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/MultiplayerStatic.cs#L308-L324
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/MultiplayerStatic.cs#L384-L404
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/MultiplayerStatic.cs#L418-L433
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/MultiplayerStatic.cs#L457-L470
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/Syncing/Game/SyncMethods.cs#L64-L82
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/Syncing/Game/SyncActions.cs#L17-L21

The error which happens during launch with Project Rimfactory Revived:

Error in static constructor of Multiplayer.Client.MultiplayerStatic: System.TypeInitializationException: The type initializer for 'Multiplayer.Client.MultiplayerStatic' threw an exception. ---> System.FormatException: Method virtual System.Void ProjectRimFactory.AutoMachineTool.Building_Base`1<ProjectRimFactory.AutoMachineTool.T>::SpawnSetup(Verse.Map map, System.Boolean respawningAfterLoad) cannot be patched. Reason: Could not execute the method because the containing type is not fully instantiated.
  at HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) [0x0008c] in <255414689d344b1385ae719f6cce8c55>:0 
  at HarmonyLib.PatchProcessor.Patch () [0x000fc] in <255414689d344b1385ae719f6cce8c55>:0 
  at HarmonyLib.Harmony.Patch (System.Reflection.MethodBase original, HarmonyLib.HarmonyMethod prefix, HarmonyLib.HarmonyMethod postfix, HarmonyLib.HarmonyMethod transpiler, HarmonyLib.HarmonyMethod finalizer) [0x00028] in <255414689d344b1385ae719f6cce8c55>:0 
  at Multiplayer.Client.Extensions.PatchMeasure (HarmonyLib.Harmony harmony, System.Reflection.MethodBase original, HarmonyLib.HarmonyMethod prefix, HarmonyLib.HarmonyMethod postfix, HarmonyLib.HarmonyMethod transpiler, HarmonyLib.HarmonyMethod finalizer) [0x00012] in <eeab71685d9f4a769b786d9dc34b45cf>:0 
  at Multiplayer.Client.MultiplayerStatic.DoPatches () [0x005e4] in <eeab71685d9f4a769b786d9dc34b45cf>:0 
  at Multiplayer.Client.MultiplayerStatic..cctor () [0x001bd] in <eeab71685d9f4a769b786d9dc34b45cf>:0 
   --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (System.RuntimeTypeHandle type) [0x0002a] in <eae584ce26bc40229c1b1aa476bfa589>:0 
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00025] in <cd7169108ea74757aa50c5b33d275c15>:0 

I think I may have an idea on how to handle this... I'll try to get something up soon. Hopefully my attempts will end better than 2 years ago when I tried and gave up with no idea on how to do it.

The solution I thought of won't work due to issues with Harmony and generics. The Harmony documentation made me believe it would work, which was not the case.

I suppose the simplest fix for startup errors would be to skip patching generics (and leaving an appropriate warning/error?)... Which for obvious reason is not a good solution, but would at least stop everything from breaking fully on startup.

At least unless Prepatcher is capable of handling generics better than Harmony, or Zetrith has some way to handle it which will work. I myself am out of ideas here.

The SpawnSetup patch was missing a try/catch so I fixed it. This should do for now

Still need to change 2 places to use try/catch, as they could technically cause the same issues. Not a big issue at the moment.
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/Syncing/Game/SyncMethods.cs#L64-L82
https://github.com/rwmt/Multiplayer/blob/7429f3a8870a8696e5829de31a831c8d0f16ce4b/Source/Client/Syncing/Game/SyncActions.cs#L17-L21

Still, this doesn't really fix the issue, as it'll just display a big red X next to the Multiplayer button (with an irrelevant message). This could technically be worked around by the mod itself calling the MP prefix and postfix/finalizer, but again - we'll be left with errors and the X informing people that Multiplayer errored out. Sadly, for the time being - this basically means that some mods (like Project Rimfactory) will remain incompatible with MP and no easy fix...