Migrate to Harmony2

This commit is contained in:
ZaneYork 2020-05-29 14:12:08 +08:00
parent 797a8e5485
commit c67506cd69
47 changed files with 239 additions and 185 deletions

View File

@ -7,6 +7,7 @@ using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.ModLoading;
using StardewModdingAPI.Toolkit.Utilities;
using StardewValley;
using HarmonyLib;
namespace StardewModdingAPI
{
@ -203,7 +204,7 @@ namespace StardewModdingAPI
typeof(Microsoft.Xna.Framework.Vector2).Assembly,
typeof(MonoMod.RuntimeDetour.HarmonyDetourBridge).Assembly,
typeof(MonoMod.Utils.Platform).Assembly,
typeof(Harmony.HarmonyPatch).Assembly,
typeof(HarmonyLib.Harmony).Assembly,
typeof(Mono.Cecil.MethodDefinition).Assembly,
typeof(StardewModdingAPI.IManifest).Assembly,
};

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.ModLoading.Framework;
using StardewModdingAPI.Metadata;
@ -321,7 +322,7 @@ namespace StardewModdingAPI.Framework.ModLoading
rewritten |= handler.Handle(module, type, replaceWith);
return rewritten;
},
rewriteInstruction: (instruction, cil, replaceWith) =>
rewriteInstruction: (ref Instruction instruction, ILProcessor cil, Action<Instruction> replaceWith) =>
{
bool rewritten = false;
foreach (IInstructionHandler handler in handlers)

View File

@ -49,5 +49,19 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
return false;
}
/*********
** Protected methods
*********/
/// <summary>Get whether a CIL instruction matches.</summary>
/// <param name="instruction">The IL instruction.</param>
protected bool IsMatch(Instruction instruction)
{
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
return
fieldRef != null
&& fieldRef.DeclaringType.FullName == this.FullTypeName
&& fieldRef.Name == this.FieldName;
}
}
}

View File

@ -23,7 +23,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <param name="cil">The CIL instruction processor.</param>
/// <param name="replaceWith">Replaces the CIL instruction with the given instruction.</param>
/// <returns>Returns whether the instruction was changed.</returns>
public delegate bool RewriteInstructionDelegate(Instruction instruction, ILProcessor cil, Action<Instruction> replaceWith);
public delegate bool RewriteInstructionDelegate(ref Instruction instruction, ILProcessor cil, Action<Instruction> replaceWith);
/*********
@ -144,7 +144,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
// instruction itself
// (should be done after the above type rewrites to ensure valid types)
rewritten |= this.RewriteInstructionImpl(instruction, cil, newInstruction =>
rewritten |= this.RewriteInstructionImpl(ref instruction, cil, newInstruction =>
{
rewritten = true;
cil.Replace(instruction, newInstruction);

View File

@ -6,7 +6,7 @@ using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class AnimalQueryMenuMethods : AnimalQueryMenu
{

View File

@ -6,7 +6,7 @@ using StardewValley;
using StardewValley.Menus;
using StardewValley.Objects;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class CraftingPageMobileMethods : CraftingPageMobile
{

View File

@ -6,7 +6,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class DayTimeMoneyBoxMethods : DayTimeMoneyBox
{

View File

@ -5,7 +5,7 @@ using System.Text;
using Microsoft.Xna.Framework;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class DebrisMethods : Debris
{

View File

@ -1,6 +1,6 @@
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class DialogueBoxMethods : DialogueBox
{

View File

@ -5,7 +5,7 @@ using System.Text;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class DiscreteColorPickerMethods : DiscreteColorPicker
{

View File

@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class FarmerMethods : Farmer
{

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class FarmerRendererMethods : FarmerRenderer
{

View File

@ -6,7 +6,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class Game1Methods : Game1
{

View File

@ -1,6 +1,6 @@
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class GameLocationMethods : GameLocation
{

View File

@ -1,7 +1,7 @@
using System.Reflection;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class GameMenuMethods : GameMenu
{

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class HUDMessageMethods : HUDMessage
{

View File

@ -0,0 +1,33 @@
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class HarmonyInstanceMethods
{
public static MethodInfo Patch(
Harmony instance,
MethodBase original,
HarmonyMethod prefix = null,
HarmonyMethod postfix = null,
HarmonyMethod transpiler = null,
HarmonyMethod finalizer = null)
{
if (Constants.MonoModInit)
return instance.Patch(original, prefix, postfix, transpiler, finalizer);
else
return null;
}
public static void PatchAll(Harmony instance)
{
if (Constants.MonoModInit)
instance.PatchAll();
}
public static void PatchAllToAssembly(Harmony instance, Assembly assembly)
{
if (Constants.MonoModInit)
instance.PatchAll(assembly);
}
}
}

View File

@ -5,7 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class IClickableMenuMethods : IClickableMenu
{

View File

@ -5,7 +5,7 @@ using System.Text;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class InventoryMenuMethods : InventoryMenu
{

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class ItemGrabMenuMethods : ItemGrabMenu
{

View File

@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class MapPageMethods : MapPage
{

View File

@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class MenuWithInventoryMethods : MenuWithInventory
{

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class NPCMethods : NPC
{

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class SaveGameMethods : SaveGame
{

View File

@ -4,7 +4,7 @@ using System.Reflection;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class ShopMenuMethods : ShopMenu
{

View File

@ -5,7 +5,7 @@ using System.Text;
using Microsoft.Xna.Framework.Graphics;
using StardewValley.BellsAndWhistles;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class SpriteTextMethods : SpriteText
{

View File

@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewValley.Menus;
#pragma warning disable 1591 // missing documentation
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class TextBoxMethods : TextBox
{

View File

@ -5,7 +5,7 @@ using System.Reflection;
using StardewValley;
using StardewValley.Locations;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class UtilityMethods : Utility
{

View File

@ -5,7 +5,7 @@ using System.Text;
using Microsoft.Xna.Framework;
using StardewValley;
namespace StardewModdingAPI.Framework.RewriteFacades
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
public class WeatherDebrisMethods : WeatherDebris
{

View File

@ -1,11 +1,12 @@
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
{
/// <summary>Rewrites method references from one parent type to another if the signatures match.</summary>
internal class MethodToAnotherStaticMethodRewriter : IInstructionHandler
internal class MethodToAnotherStaticMethodRewriter : BaseInstructionHandler
{
/*********
** Fields
@ -22,13 +23,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <summary>The method to map to.</summary>
private readonly string ToMethod;
/*********
** Accessors
*********/
/// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
public string NounPhrase { get; }
/*********
** Public methods
*********/
@ -37,20 +31,20 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="toType">The type with methods to map to.</param>
/// <param name="onlyIfPlatformChanged">Whether to only rewrite references if loading the assembly on a different platform than it was compiled on.</param>
public MethodToAnotherStaticMethodRewriter(Type fromType, Predicate<MethodReference> fromMethodSelector, Type toType, string toMethod)
: base( $"{fromType.Name} methods")
{
this.FromType = fromType;
this.FromMethodSelector = fromMethodSelector;
this.ToType = toType;
this.ToMethod = toMethod;
this.NounPhrase = $"{fromType.Name} methods";
}
/// <summary>Perform the predefined logic for a method if applicable.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="method">The method definition containing the instruction.</param>
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
public InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the type reference with a new one.</param>
/// <returns>Returns whether the type was changed.</returns>
public InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, Action<Instruction> replaceWith)
{
return InstructionHandleResult.None;
}
@ -59,15 +53,15 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
/// <param name="instruction">The instruction to handle.</param>
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the type reference with a new one.</param>
/// <returns>Returns whether the type was changed.</returns>
public bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
if (!this.IsMatch(instruction, platformChanged))
return InstructionHandleResult.None;
if (!this.IsMatch(instruction))
return false;
instruction.Operand = module.ImportReference(this.ToType.GetMethod(this.ToMethod));
return InstructionHandleResult.Rewritten;
return this.MarkRewritten();
}
@ -76,8 +70,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
*********/
/// <summary>Get whether a CIL instruction matches.</summary>
/// <param name="instruction">The IL instruction.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
protected bool IsMatch(Instruction instruction, bool platformChanged)
protected bool IsMatch(Instruction instruction)
{
MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
return

View File

@ -12,6 +12,7 @@ using Android.Widget;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Finders;
using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
{
@ -51,24 +52,23 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="module"></param>
/// <param name="cil"></param>
/// <param name="instruction"></param>
/// <param name="assemblyMap"></param>
/// <param name="platformChanged"></param>
/// <returns></returns>
public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
/// <returns>Returns whether the instruction was changed.</returns>
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
if (!this.IsMatch(instruction))
return InstructionHandleResult.None;
return false;
MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
TypeReference typeRef = module.ImportReference(this.Type);
FieldReference fieldRef = module.ImportReference(new FieldReference(this.FieldName, methodRef.ReturnType, typeRef));
if (methodRef.Name.StartsWith("get_")) {
cil.Replace(instruction, cil.Create(methodRef.HasThis ? OpCodes.Ldfld : OpCodes.Ldsfld, fieldRef));
replaceWith.Invoke(cil.Create(methodRef.HasThis ? OpCodes.Ldfld : OpCodes.Ldsfld, fieldRef));
}
else
{
cil.Replace(instruction, cil.Create(methodRef.HasThis ? OpCodes.Stfld : OpCodes.Stsfld, fieldRef));
replaceWith.Invoke(cil.Create(methodRef.HasThis ? OpCodes.Stfld : OpCodes.Stsfld, fieldRef));
}
return InstructionHandleResult.Rewritten;
return this.MarkRewritten();
}
}
}

View File

@ -60,12 +60,12 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
/// <param name="instruction">The instruction to handle.</param>
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
/// <returns>Returns whether the instruction was changed.</returns>
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
if (!this.IsMatch(instruction))
return InstructionHandleResult.None;
return false;
try
{
@ -75,7 +75,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
FieldReference field = module.ImportReference(this.ToType.GetField(this.FieldName));
cil.InsertAfter(instruction, cil.Create(OpCodes.Ldfld, field));
cil.Replace(instruction, cil.Create(OpCodes.Call, method));
replaceWith.Invoke(cil.Create(OpCodes.Call, method));
}
else if (this.TestName != null && this.UsingInstance && !this.RainDropFix)
{
@ -83,13 +83,12 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
MethodReference field = module.ImportReference(this.ToType.GetMethod($"get_{this.TestName}"));
cil.InsertAfter(instruction, cil.Create(OpCodes.Callvirt, field));
cil.Replace(instruction, cil.Create(OpCodes.Call, method));
replaceWith.Invoke(cil.Create(OpCodes.Call, method));
}
else if (this.RainDropFix && !this.UsingInstance)
{
MethodReference getter = module.ImportReference(this.ToType.GetMethod($"get_{this.FieldName}"));
cil.Replace(instruction, cil.Create(OpCodes.Call, getter));
replaceWith.Invoke(cil.Create(OpCodes.Call, getter));
}
else
{
@ -97,9 +96,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
MethodReference field = module.ImportReference(this.ToType.GetMethod($"get_{this.TestName}"));
cil.InsertAfter(instruction, cil.Create(OpCodes.Callvirt, field));
cil.Replace(instruction, cil.Create(OpCodes.Call, method));
replaceWith.Invoke(cil.Create(OpCodes.Call, method));
}
}
catch (Exception e)
{
@ -107,7 +105,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
this.Monitor.Log(e.StackTrace);
}
return InstructionHandleResult.Rewritten;
return this.MarkRewritten();
}
}
}

View File

@ -34,24 +34,24 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
/// <param name="instruction">The instruction to handle.</param>
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
/// <returns>Returns whether the instruction was changed.</returns>
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
if (!this.IsMatch(instruction))
return InstructionHandleResult.None;
return false;
if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld)
{
MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetGetMethod());
cil.Replace(instruction, cil.Create(OpCodes.Call, methodRef));
replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef));
}
else if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld)
{
MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetSetMethod());
cil.Replace(instruction, cil.Create(OpCodes.Call, methodRef));
replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef));
}
return InstructionHandleResult.Rewritten;
return this.MarkRewritten();
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Finders;
using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
{
@ -40,27 +41,27 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
/// <param name="instruction">The instruction to handle.</param>
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
/// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
/// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
/// <returns>Returns whether the instruction was changed.</returns>
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
if (!this.IsMatch(instruction))
return InstructionHandleResult.None;
return false;
MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
if (this.GetterName != null && methodRef.Name == "get_" + this.PropertyName)
{
methodRef = module.ImportReference(this.ToType.GetMethod(this.GetterName));
cil.Replace(instruction, cil.Create(OpCodes.Callvirt, methodRef));
return InstructionHandleResult.Rewritten;
replaceWith.Invoke(cil.Create(OpCodes.Callvirt, methodRef));
return true;
}
if(this.SetterName != null && methodRef.Name == "set_" + this.PropertyName)
{
methodRef = module.ImportReference(this.ToType.GetMethod(this.SetterName));
cil.Replace(instruction, cil.Create(OpCodes.Callvirt, methodRef));
return InstructionHandleResult.Rewritten;
replaceWith.Invoke(cil.Create(OpCodes.Callvirt, methodRef));
return true;
}
return InstructionHandleResult.None;
return this.MarkRewritten();
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
namespace StardewModdingAPI.Framework.Patching
{
/// <summary>Provides generic methods for implementing Harmony patches.</summary>
internal class PatchHelper
{
/*********
** Fields
*********/
/// <summary>The interception keys currently being intercepted.</summary>
private static readonly HashSet<string> InterceptingKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
/*********
** Public methods
*********/
/// <summary>Track a method that will be intercepted.</summary>
/// <param name="key">The intercept key.</param>
/// <returns>Returns false if the method was already marked for interception, else true.</returns>
public static bool StartIntercept(string key)
{
return PatchHelper.InterceptingKeys.Add(key);
}
/// <summary>Track a method as no longer being intercepted.</summary>
/// <param name="key">The intercept key.</param>
public static void StopIntercept(string key)
{
PatchHelper.InterceptingKeys.Remove(key);
}
}
}

View File

@ -1,27 +0,0 @@
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
namespace StardewModdingAPI.Framework.RewriteFacades
{
public class HarmonyInstanceMethods
{
public static DynamicMethod Patch(HarmonyInstance instance, MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
{
if (Constants.MonoModInit)
return instance.Patch(original, prefix, postfix, transpiler);
else
return null;
}
public static void PatchAll(HarmonyInstance instance)
{
if (Constants.MonoModInit)
instance.PatchAll();
}
public static void PatchAllToAssembly(HarmonyInstance instance, Assembly assembly)
{
if (Constants.MonoModInit)
instance.PatchAll(assembly);
}
}
}

View File

@ -485,10 +485,12 @@ namespace StardewModdingAPI.Framework
this.CheckForUpdatesAsync(mods);
}
// prepare console since the console loop is disabled
// prepare console
this.Monitor.Log("Type 'help' for help, or 'help <cmd>' for a command's usage", LogLevel.Info);
this.GameInstance.CommandManager.Add(null, "help", "Lists command documentation.\n\nUsage: help\nLists all available commands.\n\nUsage: help <cmd>\n- cmd: The name of a command whose documentation to display.", this.HandleCommand);
this.GameInstance.CommandManager.Add(null, "reload_i18n", "Reloads translation files for all mods.\n\nUsage: reload_i18n", this.HandleCommand);
this.GameInstance.CommandManager
.Add(new HelpCommand(this.GameInstance.CommandManager), this.Monitor)
.Add(new HarmonySummaryCommand(), this.Monitor)
.Add(new ReloadI18nCommand(this.ReloadTranslations), this.Monitor);
// update window titles
int modsLoaded = this.ModRegistry.GetAll().Count();

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Harmony;
using HarmonyLib;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Events;
@ -94,14 +94,6 @@ namespace StardewModdingAPI.Metadata
yield return new MethodParentRewriter(typeof(SaveGame), typeof(SaveGameMethods));
yield return new MethodParentRewriter(typeof(GameLocation), typeof(GameLocationMethods));
// MonoMod fix
if (!Constants.MonoModInit)
{
yield return new MethodToAnotherStaticMethodRewriter(typeof(HarmonyInstance), (method) => method.Name == "Patch", typeof(HarmonyInstanceMethods), "Patch");
yield return new MethodToAnotherStaticMethodRewriter(typeof(HarmonyInstance), (method) => method.Name == "PatchAll" && method.Parameters.Count == 0, typeof(HarmonyInstanceMethods), "PatchAll");
yield return new MethodToAnotherStaticMethodRewriter(typeof(HarmonyInstance), (method) => method.Name == "PatchAll" && method.Parameters.Count == 1, typeof(HarmonyInstanceMethods), "PatchAllToAssembly");
}
//Constructor Rewrites
yield return new MethodParentRewriter(typeof(HUDMessage), typeof(HUDMessageMethods));
yield return new MethodParentRewriter(typeof(MapPage), typeof(MapPageMethods));
@ -133,6 +125,14 @@ namespace StardewModdingAPI.Metadata
// rewrite for SMAPI 3.6 (Harmony 1.x => 2.0 update)
yield return new Harmony1AssemblyRewriter();
// MonoMod fix
if (!Constants.MonoModInit)
{
yield return new MethodToAnotherStaticMethodRewriter(typeof(Harmony), (method) => method.Name == "Patch", typeof(HarmonyInstanceMethods), "Patch");
yield return new MethodToAnotherStaticMethodRewriter(typeof(Harmony), (method) => method.Name == "PatchAll" && method.Parameters.Count == 0, typeof(HarmonyInstanceMethods), "PatchAll");
yield return new MethodToAnotherStaticMethodRewriter(typeof(Harmony), (method) => method.Name == "PatchAll" && method.Parameters.Count == 1, typeof(HarmonyInstanceMethods), "PatchAllToAssembly");
}
/****
** detect mod issues
****/

View File

@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
using HarmonyLib;
using Microsoft.Xna.Framework;
using StardewModdingAPI.Framework.Patching;
using StardewValley;
@ -47,7 +47,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(
original: AccessTools.DeclaredConstructor(typeof(JunimoHarvester), new System.Type[] { typeof(Vector2), typeof(JunimoHut), typeof(int), typeof(Color?)}),

View File

@ -1,6 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Harmony;
using HarmonyLib;
using StardewModdingAPI.Framework.Patching;
using StardewValley;
using StardewValley.Characters;
@ -42,7 +42,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(
original: AccessTools.Property(typeof(Game1), "currentLocation").SetMethod,

View File

@ -1,7 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Harmony;
using HarmonyLib;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Patching;
using StardewValley;
@ -32,7 +32,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(AccessTools.Method(typeof(Game1), nameof(Game1.OnAppPause)),
new HarmonyMethod(AccessTools.Method(this.GetType(), nameof(OnAppPausePatch.Game_OnAppPausePrefix))));

View File

@ -1,7 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Harmony;
using HarmonyLib;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
using StardewModdingAPI.Framework.Patching;
@ -39,7 +39,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
MethodInfo makeFullBackup = AccessTools.Method(typeof(Game1), nameof(Game1.MakeFullBackup));
MethodInfo saveWholeBackup = AccessTools.Method(typeof(Game1), nameof(Game1.saveWholeBackup));

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Harmony;
using HarmonyLib;
using Microsoft.AppCenter.Crashes;
using Microsoft.Xna.Framework;
using StardewModdingAPI.Framework;
@ -42,7 +42,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(
original: AccessTools.Method(typeof(SaveGame), "HandleLoadError"),

View File

@ -1,5 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Harmony;
using HarmonyLib;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Patching;
using StardewValley.Characters;
@ -41,7 +41,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(
original: AccessTools.Method(typeof(SpriteFont), "MeasureString", new System.Type[] { typeof(string)}),

View File

@ -1,7 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Harmony;
using HarmonyLib;
using StardewModdingAPI.Framework.Patching;
namespace StardewModdingAPI.Patches
@ -41,7 +41,7 @@ namespace StardewModdingAPI.Patches
/// <summary>Apply the Harmony patch.</summary>
/// <param name="harmony">The Harmony instance.</param>
public void Apply(HarmonyInstance harmony)
public void Apply(Harmony harmony)
{
harmony.Patch(
original: AccessTools.Method(Type.GetType("System.Threading.ThreadHelper"), "ThreadStart_Context"),

View File

@ -36,7 +36,7 @@
<OutputPath>bin\Release\</OutputPath>
<!-- <DefineConstants>TRACE;DEBUG;ANDROID_TARGET_GOOGLE</DefineConstants>-->
<!-- <DefineConstants>TRACE;DEBUG;ANDROID_TARGET_SAMSUNG</DefineConstants>-->
<DefineConstants>TRACE;DEBUG;ANDROID_TARGET_AMAZON</DefineConstants>
<DefineConstants>TRACE;DEBUG;ANDROID_TARGET_GOOGLE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>8.0</LangVersion>
@ -66,10 +66,8 @@
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="LargeAddressAware" Version="1.0.4" />
<PackageReference Include="Lib.Harmony" Version="2.0.0.10" />
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Platonymous.TMXTile" Version="1.3.4" />
</ItemGroup>
<ItemGroup Condition="$(DefineConstants.Contains('ANDROID_TARGET_GOOGLE'))">
<!-- <Reference Include="MonoGame.Framework">-->
@ -105,6 +103,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>..\Loader\libs\0Harmony.dll</HintPath>
</Reference>
<Reference Include="MonoGame.Framework">
<HintPath>..\Loader\libs\MonoGame.Framework.dll</HintPath>
</Reference>
@ -126,6 +127,9 @@
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
<Reference Include="TMXTile">
<HintPath>..\..\..\..\..\..\..\SteamLibrary\steamapps\common\Stardew Valley\smapi-internal\TMXTile.dll</HintPath>
</Reference>
<Reference Include="xTile">
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\xTile.dll</HintPath>
</Reference>
@ -167,6 +171,7 @@
<Compile Include="Events\ObjectListChangedEventArgs.cs" />
<Compile Include="Events\OneSecondUpdateTickedEventArgs.cs" />
<Compile Include="Events\OneSecondUpdateTickingEventArgs.cs" />
<Compile Include="Events\PeerConnectedEventArgs.cs" />
<Compile Include="Events\PeerContextReceivedEventArgs.cs" />
<Compile Include="Events\PeerDisconnectedEventArgs.cs" />
<Compile Include="Events\RenderedActiveMenuEventArgs.cs" />
@ -193,6 +198,10 @@
<Compile Include="Events\WindowResizedEventArgs.cs" />
<Compile Include="Framework\Command.cs" />
<Compile Include="Framework\CommandManager.cs" />
<Compile Include="Framework\Commands\HarmonySummaryCommand.cs" />
<Compile Include="Framework\Commands\HelpCommand.cs" />
<Compile Include="Framework\Commands\IInternalCommand.cs" />
<Compile Include="Framework\Commands\ReloadI18nCommand.cs" />
<Compile Include="Framework\ContentCoordinator.cs" />
<Compile Include="Framework\ContentManagers\BaseContentManager.cs" />
<Compile Include="Framework\ContentManagers\GameContentManager.cs" />
@ -260,6 +269,9 @@
<Compile Include="Framework\ModLoading\Finders\ReferenceToMemberWithUnexpectedTypeFinder.cs" />
<Compile Include="Framework\ModLoading\Finders\ReferenceToMissingMemberFinder.cs" />
<Compile Include="Framework\ModLoading\Finders\TypeFinder.cs" />
<Compile Include="Framework\ModLoading\Framework\BaseInstructionHandler.cs" />
<Compile Include="Framework\ModLoading\Framework\RecursiveRewriter.cs" />
<Compile Include="Framework\ModLoading\Framework\RewriteHelper.cs" />
<Compile Include="Framework\ModLoading\IInstructionHandler.cs" />
<Compile Include="Framework\ModLoading\IncompatibleInstructionException.cs" />
<Compile Include="Framework\ModLoading\InstructionHandleResult.cs" />
@ -269,9 +281,38 @@
<Compile Include="Framework\ModLoading\ModMetadataStatus.cs" />
<Compile Include="Framework\ModLoading\ModResolver.cs" />
<Compile Include="Framework\ModLoading\PlatformAssemblyMap.cs" />
<Compile Include="Framework\ModLoading\RewriteHelper.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\AccessToolsFacade.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\AnimalQueryMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\CraftingPageMobileMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\DayTimeMoneyBoxMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\DebrisMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\DialogueBoxMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\DiscreteColorPickerMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\FarmerMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\FarmerRenderMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\Game1Methods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\GameLocationMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\GameMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\HarmonyInstanceFacade.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\HarmonyInstanceMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\HarmonyMethodFacade.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\HUDMessageMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\IClickableMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\InventoryMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\ItemGrabMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\MapPageMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\MenuWithInventoryMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\NPCMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\SaveGameMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\ShopMenuMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\SpriteBatchFacade.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\SpriteTextMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\TextBoxMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\UtilityMethods.cs" />
<Compile Include="Framework\ModLoading\RewriteFacades\WeatherDebrisMethods.cs" />
<Compile Include="Framework\ModLoading\Rewriters\FieldReplaceRewriter.cs" />
<Compile Include="Framework\ModLoading\Rewriters\FieldToPropertyRewriter.cs" />
<Compile Include="Framework\ModLoading\Rewriters\Harmony1AssemblyRewriter.cs" />
<Compile Include="Framework\ModLoading\Rewriters\MethodToAnotherStaticMethodRewriter.cs" />
<Compile Include="Framework\ModLoading\Rewriters\MethodParentRewriter.cs" />
<Compile Include="Framework\ModLoading\Rewriters\PropertyToFieldRewriter.cs" />
@ -309,32 +350,6 @@
<Compile Include="Framework\Rendering\SDisplayDevice.cs" />
<Compile Include="Framework\Rendering\SXnaDisplayDevice.cs" />
<Compile Include="Framework\RequestExitDelegate.cs" />
<Compile Include="Framework\RewriteFacades\DebrisMethods.cs" />
<Compile Include="Framework\RewriteFacades\GameMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\HarmonyInstanceMethods.cs" />
<Compile Include="Framework\RewriteFacades\InventoryMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\DiscreteColorPickerMethods.cs" />
<Compile Include="Framework\RewriteFacades\CraftingPageMobileMethods.cs" />
<Compile Include="Framework\RewriteFacades\DialogueBoxMethods.cs" />
<Compile Include="Framework\RewriteFacades\DayTimeMoneyBoxMethods.cs" />
<Compile Include="Framework\RewriteFacades\AnimalQueryMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\ShopMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\FarmerMethods.cs" />
<Compile Include="Framework\RewriteFacades\FarmerRenderMethods.cs" />
<Compile Include="Framework\RewriteFacades\Game1Methods.cs" />
<Compile Include="Framework\RewriteFacades\GameLocationMethods.cs" />
<Compile Include="Framework\RewriteFacades\HUDMessageMethods.cs" />
<Compile Include="Framework\RewriteFacades\IClickableMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\MenuWithInventoryMethods.cs" />
<Compile Include="Framework\RewriteFacades\ItemGrabMenuMethods.cs" />
<Compile Include="Framework\RewriteFacades\MapPageMethods.cs" />
<Compile Include="Framework\RewriteFacades\NPCMethods.cs" />
<Compile Include="Framework\RewriteFacades\SpriteBatchMethods.cs" />
<Compile Include="Framework\RewriteFacades\SpriteTextMethods.cs" />
<Compile Include="Framework\RewriteFacades\TextBoxMethods.cs" />
<Compile Include="Framework\RewriteFacades\UtilityMethods.cs" />
<Compile Include="Framework\RewriteFacades\SaveGameMethods.cs" />
<Compile Include="Framework\RewriteFacades\WeatherDebrisMethods.cs" />
<Compile Include="Framework\SCore.cs" />
<Compile Include="Framework\Serialization\ColorConverter.cs" />
<Compile Include="Framework\Serialization\PointConverter.cs" />

View File

@ -102,18 +102,7 @@ namespace StardewModdingAPI
try
{
File errorLog = this.FilesDir.ListFiles().FirstOrDefault(f => f.IsDirectory && f.Name == "error")?.ListFiles().FirstOrDefault(f => f.Name.EndsWith(".dat"));
try
{
Handler handler = new Handler((msg) => throw new RuntimeException());
SAlertDialogUtil.AlertMessage(System.IO.File.ReadAllText(errorLog.AbsolutePath), "Crash Detected",
callback: type => { handler.SendEmptyMessage(0); });
Looper.Loop();
}
catch (Exception)
{
// ignored
}
SAlertDialogUtil.AlertMessage(System.IO.File.ReadAllText(errorLog.AbsolutePath), "Crash Detected");
Type[] services = {typeof(Microsoft.AppCenter.Analytics.Analytics), typeof(Microsoft.AppCenter.Crashes.Crashes)};
AppCenter.Start(Constants.MicrosoftAppSecret, services);
AppCenter.SetUserId(Constants.ApiVersion.ToString());
@ -130,6 +119,11 @@ namespace StardewModdingAPI
{
try
{
Game1 game1 = (Game1) typeof(MainActivity).GetField("_game1", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(this);
if (game1 != null)
{
game1.Exit();
}
new SGameConsole();
Program.Main(null);
@ -148,11 +142,6 @@ namespace StardewModdingAPI
this.core = new SCore(Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, modPath), false);
this.core.RunInteractively();
Game1 game1 = (Game1) typeof(MainActivity).GetField("_game1", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(this);
if (game1 != null)
{
game1.Exit();
}
typeof(MainActivity).GetField("_game1", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(this, this.core.GameInstance);
this.SetContentView((View) this.core.GameInstance.Services.GetService(typeof(View)));