Skip to content

v5: Upgrade HarmonyX to version 2.13.0 #902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: v5-lts
Choose a base branch
from

Conversation

aldelaro5
Copy link
Contributor

@aldelaro5 aldelaro5 commented Jun 13, 2024

Description

This upgrades the version of HarmonyX to be 2.13.0 alongside matching MonoMod upgrades. This is a MAJOR breaking change, particularly for plugins that uses MonoMod's API's directly as it underwent a full rewrite that, while offering equivalent APIs, majorly breaks compatibility. This also includes some breaking changes on the HarmonyX side, but it seems they are much more minimal.

Due to BepInEx being affected by some breaking changes, some adaptations were needed, but they are relatively minor and non breaking:

  • EntryPoint.LocalResolve: We now check for the existence of the patcher and plugin directory because it seems that it now can resolve an assembly before they are guaranteed to be created
  • The platforms utilities of the preloader are removed because MonoMod.Utils offers a much better, equivalent API. As such, all platforms detection code was moved to use the new MonoMod one
  • The XTermFix no longer had the NativeDetour workaround. Not only is it not compatible with new MonoMod, the comments describing the reasons for it no longer apply because MonoMod no longer calls Process.Start as part of its platform detection on POSIX (it now uses syscalls to get the same informations)
  • The UnixStreamHelper was adapted to the new Dyn import API which is no longer attributes based. After checking the implementation of new MonoMod, equivalent codes could be achieved
  • Some delegate helpers apis were adapted
  • The test project had to be retargeted to net462 because it was causing a dependency problem due to MonoMod. Additionally, I added its matching Mono.Cecil version which was needed due to the retargetting or Nuget would get an error. I am not sure how to avoid this one, but it's also a test project and the tests were all sucessfull on my end

Additionally, BepinEx.Harmony's submodule was changed to point to my fork which received similar upgrades. THIS MEANS THIS PR CANNOT BE MERGED AS IS! There will be a PR opened on the BepinEx.Harmony's side (BepInEx/BepInEx.Harmony#3) which should we decide to go forward with this pr, BepinEx.Harmony's pr needs to be merged first, then I change the submodule to point to it and only then it can be merged. This is however enough at the moment to perform testing and it does build.

Motivation and Context

BepinEx v6's maintenance has become uncertain since years which creates a problem for unity mono games. In these environments, v5 is more tested, more stable and is known to work which isn't the case for v6. The problem is BepinEx v5 has been stuck on HarmonyX 2.9.0 meaning it doesn't get interesting features that new HarmonyX versions provides.

Normally, it would have been fine to not upgrade it further (2.10.0 is where breaking changes starts to occur), but v6's maintenance situation changes this where it is being considered if a v5 upgrade should happen even if it is breaking. In other words, v5 seems to be stuck in a maintenance limbo where it is the best for Mono games, but also years behind in terms of patching libraries.

Recently, I helped MonoMod uncover and address a couple of old mono's runtime issues which allowed games to boot from this upgrade (see the testing section for more details).

The main breaking cases seems to be using MonoMod directly. HarmonyX's API does have breaking changes, but I checked with an API diff tool and found the changes are much less problematic than with MonoMod. Effectively, this breaks all MonoMod plugins: those plugins will need to be adapted to the new APIs. For HarmonyX, it depends, but as an example, Unity Explorer didn't need any changes and works ootb. From checking the HarmonyX changelogs, it seems the breaking changes aren't too bad, but they are still present.

How Has This Been Tested?

I validated that BepinEx installs, boots and loads my fork of UnityExplorer in the following Unity games (tested on both Windows and Linux):

  • Bug Fables public demo (unity 5.5.4, 32 bit)
  • Bug Fables 1.1.2 (old mono, unity 2018.4)
  • Bug Fables 1.1.3 (new mono, unity 2018.4)*
  • Return of the Obra Dhin (unity 2017.4.37, 32 bit)
  • Inscryption (unity 2019.4.24, 32 bit)* NOTE: it didn't boot on linux, but I found that previous BepinEx version already had this issue so it's not related
  • Lil Gator game (unity 2020.3.23)*
  • Marble It up ultra! (unity 2020.3.44) NOTE: it didn't boot on linux, but I found that previous BepinEx version already had this issue so it's not related

*: These games REQUIRED to redirect the corlibs set or a preloader crash occured. It seems to affect unity games affecting netstandard because I experienced a similar issue with UnityExplorer before and I found that it had to do with the compatibility profile selected in the Unity project. This seems worrying: netstandard can often be the default target so there's a good chance a significant amount of games now requires corlibs redirections when they didn't before and the range of affected Unity versions seems broad (at least 2018.x and most of 2019.x, but I don't know for further). Interestingly, Marble it up did NOT required this, but that's because they targeted the .net profile instead of the netstandard one. If this issue occurs, you will get this exception in the preloader log:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> HarmonyLib.HarmonyException: IL Compile Error (unknown location) ---> HarmonyLib.HarmonyException: IL Compile Error (unknown location) ---> System.TypeLoadException: Could not resolve type with token 01000096 (from typeref, class/assembly System.Threading.LockRecursionException, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
  at System.Threading.SpinLock.ContinueTryEnter (System.Int32 millisecondsTimeout, System.Boolean& lockTaken) [0x00043] in <b54d80cdf6fa45158c3f039b976a156d>:0 
  at System.Threading.SpinLock.Enter (System.Boolean& lockTaken) [0x0002e] in <b54d80cdf6fa45158c3f039b976a156d>:0 
  at MonoMod.RuntimeDetour.ILHook.Undo () [0x00008] in <b7b4fc7b42ea409a88a75b4cf02652fd>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x00027] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
   --- End of inner exception stack trace ---
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x0004c] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
  at HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) [0x00033] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
   --- End of inner exception stack trace ---
  at HarmonyLib.PatchClassProcessor.ReportException (System.Exception exception, System.Reflection.MethodBase original) [0x00045] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
  at HarmonyLib.PatchClassProcessor.Patch () [0x000a2] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
  at HarmonyLib.Harmony.PatchAll (System.Type type) [0x00008] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
  at HarmonyLib.Harmony.CreateAndPatchAll (System.Type type, System.String harmonyInstanceId) [0x00083] in <68b1ecbc3a37427ab55870d4a35bf376>:0 
  at BepInEx.Preloader.RuntimeFixes.ConsoleSetOutFix.Apply () [0x0001f] in <58160cd6f0b440de853006f6f572fc62>:0 
  at BepInEx.Preloader.PreloaderRunner.PreloaderMain () [0x00011] in <58160cd6f0b440de853006f6f572fc62>:0 
  at BepInEx.Preloader.PreloaderRunner.PreloaderPreMain () [0x00057] in <58160cd6f0b440de853006f6f572fc62>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <2fa7a6a452ca43df998f07fd1486c0df>:0 
   --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x0004b] in <2fa7a6a452ca43df998f07fd1486c0df>:0 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <2fa7a6a452ca43df998f07fd1486c0df>:0 
  at Doorstop.Entrypoint.Start () [0x000b1] in <58160cd6f0b440de853006f6f572fc62>:0 

Overall, it seems that with the exception of the corlibs problem (which I wonder if MonoMod should look into because it might affect more games than I expected), Bepinex still works. This is obviously not exhaustive and testing is encouraged, but it seems more promising than before where old mono would cause very bad native crashes.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

@aldelaro5 aldelaro5 changed the title Upgrade HarmonyX to version 2.13.0 v5: Upgrade HarmonyX to version 2.13.0 Jun 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant