Line 1: |
Line 1: |
| ←[[Modding:Index|Index]] | | ←[[Modding:Index|Index]] |
| | | |
− | {{SMAPI upcoming|3.''x''}} | + | {{Modder compatibility header}} |
| | | |
− | '''This page is for modders. Players: see [[Modding:Mod compatibility]] instead.''' | + | SMAPI 3.12 updates from Harmony 1.2.0.1 to Harmony 2.1. That only affects mods which use Harmony directly; that's [[Modding:Modder Guide/APIs/Harmony|discouraged in most cases]], isn't officially part of SMAPI's public API, and isn't subject to SMAPI's normal versioning policy. If you use Harmony in your mods, this page explains how the update affects them. |
| | | |
− | This page explains how to update your mods for compatibility with Harmony 2.0. This only applies to mods which use Harmony directly; [[Modding:Modder Guide/APIs/Harmony|this is discouraged]] in most cases, isn't officially part of SMAPI's public API, and isn't subject to SMAPI's normal versioning policy.
| + | __TOC__ |
| + | ==Overview== |
| + | ===What's changing?=== |
| + | [https://github.com/pardeike/Harmony/releases/tag/v2.0.0 Harmony 2.0] and [https://github.com/pardeike/Harmony/releases/tag/v2.1.0.0 2.1] have many changes that benefit SMAPI and mods. Some notable changes: |
| + | * Added [https://harmony.pardeike.net/articles/patching-finalizer.html finalizers] and [https://harmony.pardeike.net/articles/reverse-patching.html reverse patches]. |
| + | * Added [https://harmony.pardeike.net/articles/patching-postfix.html pass-through postfixes]. |
| + | * Added [https://harmony.pardeike.net/api/HarmonyLib.Transpilers.html <samp>Manipulator</samp>] utility, <samp>CodeInstruction</samp> extensions, and other improvements for transpilers. |
| + | * Added more <samp>AccessTools.Is*</samp> methods. |
| + | * Added support for .NET 5. |
| + | * Transpilers can now default to the original input by returning <samp>null</samp>. |
| + | * Improved compatibility with Android modding. |
| + | * Improved exception messages. |
| + | * Improved validation for invalid patches. |
| + | * Fixed cases where methods were inlined and unpatchable on Linux/Mac. |
| + | * Fixed methods with struct return types being unpatchable. |
| + | * Various other improvements and fixes; see the [https://github.com/pardeike/Harmony/releases/tag/v2.0.0 Harmony 2.0 release notes], [https://github.com/pardeike/Harmony/releases/tag/v2.1.0.0 Harmony 2.1 release notes], and [https://harmony.pardeike.net new Harmony documentation] for more info. |
| | | |
− | __TOC__
| + | After waiting over a year to make sure the release is stable, SMAPI is transitioning to Harmony 2.1. |
| + | |
| + | ===Is this the modapocalypse?=== |
| + | Nope. Although this is a major change, significant efforts were undertaken to minimise the impact: |
| + | * mods which don't use Harmony directly aren't affected; |
| + | * most mods are rewritten automatically for compatibility, so they'll work without an update; |
| + | * pull requests were submitted to update affected open-source mods; |
| + | * unofficial updates were created as needed for mods which hadn't updated officially; |
| + | * the changes were actively communicated and documented to modders. |
| | | |
− | ==What's new?==
| + | In addition, the target was ''at least'' 90% compatibility for open-source mods before SMAPI migrates. All of this means that the release should have minimal impact on mod compatibility, despite the scope of the changes. |
− | See the [https://github.com/pardeike/Harmony/releases/tag/v2.0.0 Harmony 2.0 release notes] and [https://harmony.pardeike.net Harmony 2.0 documentation] for more info.
| |
| | | |
− | ==Migration steps== | + | ===How to update your mod=== |
− | # Make sure you follow best practices outlined in the [[Modding:Modder Guide/APIs/Harmony|Harmony guide]]. In particular, use the <code>EnableHarmony</code> option (don't reference the Harmony DLL directly) and use the code API. | + | # Make sure you follow best practices outlined in the [[Modding:Modder Guide/APIs/Harmony|Harmony guide]]. In particular, use the <code>EnableHarmony</code> option (don't reference the Harmony DLL directly). |
| # Change <code>using Harmony;</code> to <code>using HarmonyLib;</code>. | | # Change <code>using Harmony;</code> to <code>using HarmonyLib;</code>. |
| # Change <code>HarmonyInstance harmony = HarmonyInstance.Create("your mod id");</code> to <code>Harmony harmony = new Harmony("your mod id");</code>. | | # Change <code>HarmonyInstance harmony = HarmonyInstance.Create("your mod id");</code> to <code>Harmony harmony = new Harmony("your mod id");</code>. |
| + | # Check if any [[#Breaking changes|breaking changes]] listed below apply to your mod. |
| # Recompile the mod. | | # Recompile the mod. |
| | | |
− | That's it! Otherwise usage should be identical.
| + | ==Breaking changes== |
| + | ===API changes=== |
| + | See ''[[#How to update your mod|how to update your mod]]''. |
| + | |
| + | ===Stricter validation=== |
| + | Harmony 2.x has stricter validation in general, so invalid patches that would previously somewhat work (''e.g.,'' setting <samp>__result</samp> to the wrong type) will now cause errors. See the exception messages for help fixing these. |
| | | |
− | ==Troubleshooting== | + | ===Patching static constructors=== |
− | ==="You can only patch implemented methods/constructors"=== | + | The <samp>AccessTools</samp> methods for constructors (<samp>Constructor</samp>, <samp>DeclaredConstructor</samp>, and <samp>GetDeclaredConstructors</samp>) no longer match static constructors by default. Use the new <samp>searchForStatic</samp> argument if you need to match them: |
− | Consider this example:
| + | |
− | <source lang="C#"> | + | <syntaxhighlight lang="c#"> |
| + | // match static constructors only |
| + | var method = AccessTools.Constructor(typeof(ExampleType), searchForStatic: true); |
| + | |
| + | // match static *or* instance constructors |
| + | var method = |
| + | AccessTools.Constructor(typeof(ExampleType), searchForStatic: true) |
| + | ?? AccessTools.Constructor(typeof(ExampleType)); |
| + | </syntaxhighlight> |
| + | |
| + | Note that Harmony no longer matches static constructors for a good reason — they're only called once for the type, so often static constructor patches won't work correctly. |
| + | |
| + | ===Patching virtual methods=== |
| + | When patching a virtual method, you must now patch the specific type which implements it. Patching the wrong type now results in the error "''You can only patch implemented methods/constructors''". |
| + | |
| + | For example, consider this code: |
| + | <syntaxhighlight lang="c#"> |
| public class GameLocation | | public class GameLocation |
| { | | { |
Line 30: |
Line 74: |
| | | |
| public class Farm : GameLocation {} | | public class Farm : GameLocation {} |
− | </source> | + | </syntaxhighlight> |
| + | |
| + | <samp>Farm.cleanupBeforePlayerExit</samp> doesn't exist, so it's inherited from <samp>GameLocation</samp>. Harmony 1.x would let you patch <samp>Farm.cleanupBeforePlayerExit</samp>, but in Harmony 2.x you must target the actual method (<samp>GameLocation.cleanupBeforePlayerExit</samp> in this example). |
| + | |
| + | ===<samp>HarmonyMethod</samp> no longer allows null=== |
| + | Harmony 1.x allowed <code>new HarmonyMethod(null)</code>, so you could safely use it with methods that might not exist. Harmony 2.x now throws an exception in that case, so you should check if you're not sure it exists: |
| + | <syntaxhighlight lang="c#"> |
| + | MethodInfo prefix = AccessTools.Method(this.GetType(), "Prefix"); |
| + | if (prefix != null) |
| + | harmony.Patch(original, new HarmonyMethod(prefix)); |
| + | </syntaxhighlight> |
| | | |
− | <tt>Farm.cleanupBeforePlayerExit</tt> doesn't exist, so <tt>Farm</tt> inherits it from <tt>GameLocation</tt>. Harmony 1.x would let you patch <tt>Farm.cleanupBeforePlayerExit</tt>, but in Harmony 2.x you must target the actual method (<tt>GameLocation.cleanupBeforePlayerExit</tt> in this example).
| + | ===Transpiler changes=== |
| + | Harmony 2.x uses a new engine ({{github|MonoMod/MonoMod|MonoMod}}) under the hood, so there may be unexpected changes in the way transpiler patches work. For example, short-form branches may become long-form. If your mod uses transpilers, you should test each one to make sure it's working as you expect. |
| | | |
| [[Category:Modding]] | | [[Category:Modding]] |