Changes

Jump to navigation Jump to search
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]]
translators
8,444

edits

Navigation menu