From 4a29cceca73ef8560bb1f783613d00018d3b235f Mon Sep 17 00:00:00 2001 From: ZaneYork Date: Mon, 1 Jun 2020 18:22:47 +0800 Subject: [PATCH] Make patch logic more clearly --- src/SMAPI/Constants.cs | 2 +- .../ModLoading/RewriteFacades/Game1Methods.cs | 45 +----- .../RewriteFacades/GameLocationMethods.cs | 1 + .../TypeFieldToAnotherTypeFieldRewriter.cs | 145 +++++++++++------- .../TypeFieldToAnotherTypePropertyRewriter.cs | 34 +++- src/SMAPI/Metadata/InstructionMetadata.cs | 20 +-- src/SMAPI/Patches/JunimoHarvesterPatch.cs | 1 - src/SMAPI/SMAPI.csproj | 2 +- src/SMAPI/SMainActivity.cs | 21 ++- 9 files changed, 157 insertions(+), 114 deletions(-) diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 0da8abed..0d7c1587 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -21,7 +21,7 @@ namespace StardewModdingAPI ** Public ****/ /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.5.0.1", true); + public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.6.0"); /// The minimum supported version of Stardew Valley. public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5"); diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/Game1Methods.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/Game1Methods.cs index 8abb3ac1..c7b6d283 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/Game1Methods.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/Game1Methods.cs @@ -10,45 +10,16 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { public class Game1Methods : Game1 { - public static RainDrop[] rainDrops => (typeof(RainManager).GetField("_rainDropList", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(RainManager.Instance) as List).ToArray(); + public static RainDrop[] RainDropsProp + { + get + { + return (typeof(RainManager).GetField("_rainDropList", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(RainManager.Instance) as List).ToArray(); + } + } + public static new IList onScreenMenus => Game1.onScreenMenus; - public static bool IsRainingProp - { - get - { - return RainManager.Instance.isRaining; - } - set - { - RainManager.Instance.isRaining = value; - } - } - - public static bool IsSnowingProp - { - get - { - return WeatherDebrisManager.Instance.isSnowing; - } - set - { - WeatherDebrisManager.Instance.isSnowing = value; - } - } - - public static bool IsDebrisWeatherProp - { - get - { - return WeatherDebrisManager.Instance.isDebrisWeather; - } - set - { - WeatherDebrisManager.Instance.isDebrisWeather = value; - } - } - public static void updateDebrisWeatherForMovement(List debris) { WeatherDebrisManager.Instance.UpdateDebrisWeatherForMovement(); diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/GameLocationMethods.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/GameLocationMethods.cs index ac7477cd..71176198 100644 --- a/src/SMAPI/Framework/ModLoading/RewriteFacades/GameLocationMethods.cs +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/GameLocationMethods.cs @@ -4,6 +4,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades { public class GameLocationMethods : GameLocation { + public void playSound(string audioName) { base.playSound(audioName, StardewValley.Network.NetAudio.SoundContext.Default); diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs index b319e460..ef3b6745 100644 --- a/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs +++ b/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypeFieldRewriter.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using StardewModdingAPI.Framework.ModLoading.Finders; @@ -10,52 +11,38 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /********* ** Fields *********/ - /// The type whose field to which references should be rewritten. - private readonly Type Type; - /// The type whose field to which references should be rewritten to. private readonly Type ToType; - /// The field name. - private readonly string FieldName; - /// The property name. - private readonly string PropertyName; + private readonly string InstancePropertyName; - private readonly string TestName; - - private readonly IMonitor Monitor; - - private readonly bool UsingInstance; - - private readonly bool RainDropFix; + private readonly string TargetFieldName; /********* ** Public methods *********/ /// Construct an instance. /// The type whose field to which references should be rewritten. + /// /// The field name to rewrite. - /// The property name (if different). - public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, string propertyName, IMonitor monitor, string testName = null, bool usingInstance = true, bool rainDropFix = false) + /// + /// The property name (if different). + public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, string targetFieldName = null, string instancePropertyName = null) : base(type.FullName, fieldName, InstructionHandleResult.None) { - this.Monitor = monitor; - this.Type = type; this.ToType = toType; - this.FieldName = fieldName; - this.PropertyName = propertyName; - this.TestName = testName; - this.UsingInstance = usingInstance; - this.RainDropFix = rainDropFix; + this.InstancePropertyName = instancePropertyName; + if (targetFieldName == null) + { + this.TargetFieldName = fieldName; + } + else + { + this.TargetFieldName = targetFieldName; + } } - /// Construct an instance. - /// The type whose field to which references should be rewritten. - /// The field name to rewrite. - public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, IMonitor monitor, string testName = null, bool usingInstance = true, bool rainDropFix = false) - : this(type, toType, fieldName, fieldName, monitor, testName, usingInstance, rainDropFix) { } - /// Perform the predefined logic for an instruction if applicable. /// The assembly module containing the instruction. /// The CIL processor. @@ -67,44 +54,90 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters if (!this.IsMatch(instruction)) return false; - try + FieldInfo targetFieldInfo = this.ToType.GetField(this.TargetFieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + PropertyInfo targetPropertyInfo = null; + if (targetFieldInfo == null) { - if (this.TestName == null && !this.RainDropFix) + targetPropertyInfo = this.ToType.GetProperty(this.TargetFieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } + if (this.InstancePropertyName != null) + { + MethodReference instanceMethod = module.ImportReference(this.ToType.GetMethod($"get_{this.InstancePropertyName}")); + if (targetFieldInfo != null) { - MethodReference method = module.ImportReference(this.ToType.GetMethod($"get_{this.PropertyName}")); - FieldReference field = module.ImportReference(this.ToType.GetField(this.FieldName)); - - cil.InsertAfter(instruction, cil.Create(OpCodes.Ldfld, field)); - replaceWith.Invoke(cil.Create(OpCodes.Call, method)); + FieldReference targetField = module.ImportReference(targetFieldInfo); + if (instruction.OpCode == OpCodes.Ldfld) + { + cil.Replace(instruction.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(instruction.OpCode, targetField)); + } + else if (instruction.OpCode == OpCodes.Stfld) + { + cil.Replace(instruction.Previous.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(instruction.OpCode, targetField)); + } + else if(instruction.OpCode == OpCodes.Ldsfld) + { + cil.InsertAfter(instruction, cil.Create(instruction.OpCode, targetField)); + replaceWith.Invoke(cil.Create(OpCodes.Call, instanceMethod)); + } + else if (instruction.OpCode == OpCodes.Stsfld) + { + cil.InsertBefore(instruction.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(instruction.OpCode, targetField)); + } } - else if (this.TestName != null && this.UsingInstance && !this.RainDropFix) + else if(targetPropertyInfo != null) { - MethodReference method = module.ImportReference(this.ToType.GetMethod($"get_{this.PropertyName}")); - MethodReference field = module.ImportReference(this.ToType.GetMethod($"get_{this.TestName}")); - - cil.InsertAfter(instruction, cil.Create(OpCodes.Callvirt, field)); - replaceWith.Invoke(cil.Create(OpCodes.Call, method)); - } - else if (this.RainDropFix && !this.UsingInstance) - { - MethodReference getter = module.ImportReference(this.ToType.GetMethod($"get_{this.FieldName}")); - replaceWith.Invoke(cil.Create(OpCodes.Call, getter)); + if (instruction.OpCode == OpCodes.Ldfld) + { + cil.Replace(instruction.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetGetMethod()))); + } + else if (instruction.OpCode == OpCodes.Stfld) + { + cil.Replace(instruction.Previous.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetSetMethod()))); + } + else if(instruction.OpCode == OpCodes.Ldsfld) + { + cil.InsertAfter(instruction, cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetGetMethod()))); + replaceWith.Invoke(cil.Create(OpCodes.Call, instanceMethod)); + } + else if (instruction.OpCode == OpCodes.Stsfld) + { + cil.InsertBefore(instruction.Previous, cil.Create(OpCodes.Call, instanceMethod)); + replaceWith.Invoke(cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetSetMethod()))); + } } else { - MethodReference method = module.ImportReference(this.Type.GetMethod($"get_{this.FieldName}")); - MethodReference field = module.ImportReference(this.ToType.GetMethod($"get_{this.TestName}")); - - cil.InsertAfter(instruction, cil.Create(OpCodes.Callvirt, field)); - replaceWith.Invoke(cil.Create(OpCodes.Call, method)); + return false; } } - catch (Exception e) + else { - this.Monitor.Log(e.Message); - this.Monitor.Log(e.StackTrace); + if (targetFieldInfo != null) + { + FieldReference targetField = module.ImportReference(targetFieldInfo); + replaceWith.Invoke(cil.Create(instruction.OpCode, targetField)); + } + else if (targetPropertyInfo != null) + { + if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld) + { + replaceWith.Invoke(cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetGetMethod()))); + } + else + { + replaceWith.Invoke(cil.Create(OpCodes.Call, module.ImportReference(targetPropertyInfo.GetSetMethod()))); + } + } + else + { + return false; + } } - return this.MarkRewritten(); } } diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypePropertyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypePropertyRewriter.cs index e55e82df..6f4f58b7 100644 --- a/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypePropertyRewriter.cs +++ b/src/SMAPI/Framework/ModLoading/Rewriters/TypeFieldToAnotherTypePropertyRewriter.cs @@ -16,6 +16,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /// The property name. private readonly string PropertyName; + /// The property name. + private readonly string InstancePropertyName; + /********* ** Public methods *********/ @@ -23,11 +26,12 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters /// The type whose field to which references should be rewritten. /// The field name to rewrite. /// The property name (if different). - public TypeFieldToAnotherTypePropertyRewriter(Type type, Type toType, string fieldName, string propertyName) + public TypeFieldToAnotherTypePropertyRewriter(Type type, Type toType, string fieldName, string propertyName, string instancePropertyName = null) : base(type.FullName, fieldName, InstructionHandleResult.None) { this.ToType = toType; this.PropertyName = propertyName; + this.InstancePropertyName = instancePropertyName; } /// Perform the predefined logic for an instruction if applicable. @@ -43,13 +47,33 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld) { - MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetGetMethod()); - replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef)); + if (this.InstancePropertyName == null) + { + MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetGetMethod()); + replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef)); + } + else + { + MethodReference instanceMethodRef = module.ImportReference(this.ToType.GetProperty(this.InstancePropertyName).GetGetMethod()); + MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetGetMethod()); + cil.InsertAfter(instruction, cil.Create(OpCodes.Call, methodRef)); + replaceWith.Invoke(cil.Create(OpCodes.Call, instanceMethodRef)); + } } else if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) { - MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetSetMethod()); - replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef)); + if (this.InstancePropertyName == null) + { + MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetSetMethod()); + replaceWith.Invoke(cil.Create(OpCodes.Call, methodRef)); + } + else + { + MethodReference instanceMethodRef = module.ImportReference(this.ToType.GetProperty(this.InstancePropertyName).GetGetMethod()); + MethodReference methodRef = module.ImportReference(this.ToType.GetProperty(this.PropertyName).GetSetMethod()); + cil.InsertAfter(instruction, cil.Create(OpCodes.Call, methodRef)); + replaceWith.Invoke(cil.Create(OpCodes.Call, instanceMethodRef)); + } } return this.MarkRewritten(); } diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 015a4446..8e77dcdb 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -46,18 +46,14 @@ namespace StardewModdingAPI.Metadata if (platformChanged) yield return new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchFacade)); - //isRaining and isDebrisWeather fix done. - yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isRaining", "IsRainingProp"); - yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isSnowing", "IsSnowingProp"); - yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "isDebrisWeather", "IsDebrisWeatherProp"); - - // Cause of System.Security.VerificationException : Invalid instruction target - //yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(RainManager), "isRaining", "Instance", this.Monitor); - //yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(WeatherDebrisManager), "isDebrisWeather", "Instance", this.Monitor); - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(GameLocation), typeof(DebrisManager), "debris", "Instance", this.Monitor, "debrisNetCollection", false); - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(WeatherDebrisManager), "debrisWeather", "Instance", this.Monitor, "weatherDebrisList"); - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(Game1Methods), "rainDrops", "Instance", this.Monitor, null, false, true); - yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(Game1Methods), "onScreenMenus", "", this.Monitor, null, false, true); + // Redirect reference + yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(RainManager), "isRaining", "isRaining", "Instance"); + yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(WeatherDebrisManager), "isSnowing", "isSnowing", "Instance"); + yield return new TypeFieldToAnotherTypeFieldRewriter(typeof(Game1), typeof(WeatherDebrisManager), "isDebrisWeather", "isDebrisWeather", "Instance"); + yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "rainDrops", "RainDropsProp"); + yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(GameLocation), typeof(DebrisManager), "debris", "debrisNetCollection", "Instance"); + yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(WeatherDebrisManager), "debrisWeather","weatherDebrisList", "Instance"); + yield return new TypeFieldToAnotherTypePropertyRewriter(typeof(Game1), typeof(Game1Methods), "onScreenMenus", "onScreenMenus"); yield return new PropertyToFieldRewriter(typeof(Game1), "toolSpriteSheet", "toolSpriteSheet"); diff --git a/src/SMAPI/Patches/JunimoHarvesterPatch.cs b/src/SMAPI/Patches/JunimoHarvesterPatch.cs index 16b22261..c47fe084 100644 --- a/src/SMAPI/Patches/JunimoHarvesterPatch.cs +++ b/src/SMAPI/Patches/JunimoHarvesterPatch.cs @@ -69,7 +69,6 @@ namespace StardewModdingAPI.Patches try { Netcode.NetGuid guid = new Netcode.NetGuid(Game1.getFarm().buildings.GuidOf(myHome)); - Netcode.INetSerializable netFields = guid.NetFields; AccessTools.Field(typeof(JunimoHarvester), "netHome").SetValue(__instance, guid); } catch (TargetInvocationException ex) diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj index 9443b248..23fdb037 100644 --- a/src/SMAPI/SMAPI.csproj +++ b/src/SMAPI/SMAPI.csproj @@ -122,7 +122,7 @@ - ..\..\..\..\..\..\..\SteamLibrary\steamapps\common\Stardew Valley\smapi-internal\TMXTile.dll + ..\Loader\libs\TMXTile.dll ..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\xTile.dll diff --git a/src/SMAPI/SMainActivity.cs b/src/SMAPI/SMainActivity.cs index e1521d51..b3833145 100644 --- a/src/SMAPI/SMainActivity.cs +++ b/src/SMAPI/SMainActivity.cs @@ -42,6 +42,8 @@ namespace StardewModdingAPI public static SMainActivity Instance; + private static bool ErrorDetected; + public new bool HasPermissions { get @@ -102,7 +104,14 @@ namespace StardewModdingAPI try { File errorLog = this.FilesDir.ListFiles().FirstOrDefault(f => f.IsDirectory && f.Name == "error")?.ListFiles().FirstOrDefault(f => f.Name.EndsWith(".dat")); - SAlertDialogUtil.AlertMessage(System.IO.File.ReadAllText(errorLog.AbsolutePath), "Crash Detected"); + if (errorLog != null) + { + SMainActivity.ErrorDetected = true; + SAlertDialogUtil.AlertMessage(System.IO.File.ReadAllText(errorLog.AbsolutePath), "Crash Detected", callback: (type => + { + SMainActivity.ErrorDetected = false; + })); + } Type[] services = {typeof(Microsoft.AppCenter.Analytics.Analytics), typeof(Microsoft.AppCenter.Crashes.Crashes)}; AppCenter.Start(Constants.MicrosoftAppSecret, services); AppCenter.SetUserId(Constants.ApiVersion.ToString()); @@ -124,6 +133,16 @@ namespace StardewModdingAPI { game1.Exit(); } + + if (SMainActivity.ErrorDetected) + { + new Thread(() => + { + Thread.Sleep(500); + SMainActivity.Instance.RunOnUiThread(() => this.OnCreatePartTwo()); + }).Start(); + return; + } new SGameConsole(); Program.Main(null);