Make patch logic more clearly

This commit is contained in:
ZaneYork 2020-06-01 18:22:47 +08:00
parent aac5f74f40
commit 4a29cceca7
9 changed files with 157 additions and 114 deletions

View File

@ -21,7 +21,7 @@ namespace StardewModdingAPI
** Public
****/
/// <summary>SMAPI's current semantic version.</summary>
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.5.0.1", true);
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.6.0");
/// <summary>The minimum supported version of Stardew Valley.</summary>
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5");

View File

@ -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<RainDrop>).ToArray();
public static RainDrop[] RainDropsProp
{
get
{
return (typeof(RainManager).GetField("_rainDropList", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(RainManager.Instance) as List<RainDrop>).ToArray();
}
}
public static new IList<IClickableMenu> 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<WeatherDebris> debris)
{
WeatherDebrisManager.Instance.UpdateDebrisWeatherForMovement();

View File

@ -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);

View File

@ -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
*********/
/// <summary>The type whose field to which references should be rewritten.</summary>
private readonly Type Type;
/// <summary>The type whose field to which references should be rewritten to.</summary>
private readonly Type ToType;
/// <summary>The field name.</summary>
private readonly string FieldName;
/// <summary>The property name.</summary>
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
*********/
/// <summary>Construct an instance.</summary>
/// <param name="type">The type whose field to which references should be rewritten.</param>
/// <param name="toType"></param>
/// <param name="fieldName">The field name to rewrite.</param>
/// <param name="propertyName">The property name (if different).</param>
public TypeFieldToAnotherTypeFieldRewriter(Type type, Type toType, string fieldName, string propertyName, IMonitor monitor, string testName = null, bool usingInstance = true, bool rainDropFix = false)
/// <param name="targetFieldName"></param>
/// <param name="instancePropertyName">The property name (if different).</param>
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;
}
}
/// <summary>Construct an instance.</summary>
/// <param name="type">The type whose field to which references should be rewritten.</param>
/// <param name="fieldName">The field name to rewrite.</param>
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) { }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
@ -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();
}
}

View File

@ -16,6 +16,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <summary>The property name.</summary>
private readonly string PropertyName;
/// <summary>The property name.</summary>
private readonly string InstancePropertyName;
/*********
** Public methods
*********/
@ -23,11 +26,12 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="type">The type whose field to which references should be rewritten.</param>
/// <param name="fieldName">The field name to rewrite.</param>
/// <param name="propertyName">The property name (if different).</param>
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;
}
/// <summary>Perform the predefined logic for an instruction if applicable.</summary>
@ -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();
}

View File

@ -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");

View File

@ -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)

View File

@ -122,7 +122,7 @@
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
<Reference Include="TMXTile">
<HintPath>..\..\..\..\..\..\..\SteamLibrary\steamapps\common\Stardew Valley\smapi-internal\TMXTile.dll</HintPath>
<HintPath>..\Loader\libs\TMXTile.dll</HintPath>
</Reference>
<Reference Include="xTile">
<HintPath>..\..\..\..\..\Downloads\StardewValleyAndroidStuff\base_1.4.5.145\assemblies\xTile.dll</HintPath>

View File

@ -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);