Line 1: |
Line 1: |
| ←[[Modding:Index|Index]] | | ←[[Modding:Index|Index]] |
| | | |
− | {{SMAPI upcoming|3.6}} | + | {{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 mod code for Harmony 2.0. This only applies to mods which use Harmony directly; [[Modding:Modder Guide/APIs/Harmony|that's discouraged]] in most cases, isn't officially part of SMAPI's public API, and isn't subject to SMAPI's normal versioning policy.
| |
| | | |
| __TOC__ | | __TOC__ |
| ==Overview== | | ==Overview== |
| ===What's changing?=== | | ===What's changing?=== |
− | [https://github.com/pardeike/Harmony/releases/tag/v2.0.0 Harmony 2.0] has many changes that benefit SMAPI and mods. Some notable changes: | + | [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-finalizer.html finalizers] and [https://harmony.pardeike.net/articles/reverse-patching.html reverse patches]. |
− | * Added <tt>CodeInstruction</tt> extensions, <tt>Manipulator</tt> utility, and other improvements for transpilers. | + | * Added [https://harmony.pardeike.net/articles/patching-postfix.html pass-through postfixes]. |
− | * Transpilers can now default to the original input by returning <tt>null</tt>. | + | * Added [https://harmony.pardeike.net/api/HarmonyLib.Transpilers.html <samp>Manipulator</samp>] utility, <samp>CodeInstruction</samp> extensions, and other improvements for transpilers. |
− | * Better compatibility with Android modding. | + | * Added more <samp>AccessTools.Is*</samp> methods. |
− | * Better exception messages. | + | * Added support for .NET 5. |
− | * Better validation for invalid patches. | + | * 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 cases where methods were inlined and unpatchable on Linux/Mac. |
| * Fixed methods with struct return types being unpatchable. | | * 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] and [https://harmony.pardeike.net Harmony 2.0 documentation] for more info. | + | * 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. |
| | | |
− | After waiting ≈four months to make sure the release is stable, SMAPI 3.6 will transition to Harmony 2.0. | + | After waiting over a year to make sure the release is stable, SMAPI is transitioning to Harmony 2.1. |
| | | |
| ===Is this the modapocalypse?=== | | ===Is this the modapocalypse?=== |
| Nope. Although this is a major change, significant efforts were undertaken to minimise the impact: | | Nope. Although this is a major change, significant efforts were undertaken to minimise the impact: |
− | * Mods which don't use Harmony directly are not affected. | + | * mods which don't use Harmony directly aren't affected; |
− | * SMAPI automatically rewrites most Harmony 1.''x'' code for compatibility so many mods can work without an update; | + | * most mods are rewritten automatically for compatibility, so they'll work without an update; |
− | * pull requests will be submitted to update affected open-source mods; | + | * pull requests were submitted to update affected open-source mods; |
− | * unofficial updates will be created for mods which haven't updated officially by the time SMAPI 3.6 is released; | + | * unofficial updates were created as needed for mods which hadn't updated officially; |
− | * the changes will be actively communicated and documented to modders. | + | * the changes were actively communicated and documented to modders. |
| | | |
− | In addition, the current target is ''at least'' 90% compatibility for open-source mods before SMAPI 3.6 is released. All of this means that the 3.6 release should have minimal impact on mod compatibility, despite the scope of the changes. | + | 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. |
| | | |
| ===How to update your mod=== | | ===How to update your mod=== |
Line 45: |
Line 46: |
| | | |
| ===Stricter validation=== | | ===Stricter validation=== |
− | Harmony 2.0 has stricter validation in general, so invalid patches that would previously work (e.g. setting <tt>__result</tt> to the wrong type) will now cause errors. See the exception messages for help fixing these. | + | 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. |
| | | |
| ===Patching static constructors=== | | ===Patching static constructors=== |
− | The <tt>AccessTools</tt> methods for constructors (<tt>DeclaredConstructor</tt>, <tt>Constructor</tt>, and <tt>GetDeclaredConstructors</tt>) no longer match static constructors by default. Pass <tt>searchForStatic: true</tt> to the methods if needed. | + | 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: |
| + | |
| + | <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=== | | ===Patching virtual methods=== |
− | You can no longer patch a non-implemented virtual method; doing so results in the error "''You can only patch implemented methods/constructors''".
| + | 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: | | For example, consider this code: |
− | <source lang="C#"> | + | <syntaxhighlight lang="c#"> |
| public class GameLocation | | public class GameLocation |
| { | | { |
Line 61: |
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 it's inherited 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]] |