Decompiled source of StackmasterFix v1.1.0

plugins/StackmasterFix/StackmasterFix.dll

Decompiled 20 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StackmasterFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("StackmasterFix")]
[assembly: AssemblyTitle("StackmasterFix")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StackmasterFix
{
	[BepInPlugin("s6652289.StackmasterFix", "StackmasterFix", "1.1.0")]
	public class FixPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource L;

		internal static ConfigEntry<bool> FixKit;

		internal static ConfigEntry<bool> Verbose;

		private Harmony _harmony;

		private bool _installed;

		private void Awake()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			L = ((BaseUnityPlugin)this).Logger;
			FixKit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FixExpeditionKit", true, "修复「建造菜单 Alt+左键拉取远征包」不扣料(材料复制)。\n合成/升级那条路径上游 Stackmaster 1.1.5 已自行修复,本补丁不再介入,故没有对应开关。");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", true, "打印每次重定向与强制写回的细节(定位问题时很有用,确认没问题后可关)。");
			L.LogInfo((object)"=============== StackmasterFix 1.1.0 启动 ===============");
			L.LogInfo((object)"作用:把「拉取远征包」的扣减从『只读快照副本』改到『真实容器活库存』,并强制写回 ZDO。");
			L.LogInfo((object)"说明:『用箱子材料合成/升级不扣料』上游 Stackmaster 1.1.5 已自行修复 ⇒ 本补丁已移除该逻辑。");
			_harmony = new Harmony("s6652289.StackmasterFix");
			((MonoBehaviour)this).StartCoroutine(InstallWhenReady());
		}

		private IEnumerator InstallWhenReady()
		{
			for (int attempt = 0; attempt < 180; attempt++)
			{
				if (_installed)
				{
					break;
				}
				try
				{
					TryInstall();
				}
				catch (Exception ex)
				{
					L.LogError((object)("[Fix] 安装异常:" + ex));
				}
				yield return (object)new WaitForSecondsRealtime(1f);
			}
			if (!_installed)
			{
				L.LogError((object)"[Fix] 180 秒内没等到 Stackmaster 类型,补丁未安装(模组不存在?)。");
			}
		}

		private void TryInstall()
		{
			if (!FixKit.Value)
			{
				L.LogInfo((object)"[Fix] 配置里已关闭「远征包修复」⇒ 本补丁不挂载任何补丁(等同于未安装)。");
				_installed = true;
				return;
			}
			Type type = FindType("Stackmaster.ExpeditionKitAction");
			Type type2 = FindType("Stackmaster.RuntimeResourceStack");
			Type type3 = FindType("Stackmaster.ContainerHandle");
			if (!(type == null) && !(type2 == null) && !(type3 == null))
			{
				if (!LiveResolver.Init(type2, type3))
				{
					L.LogError((object)"[Fix] LiveResolver 初始化失败:反射句柄拿不全,拒绝打补丁。");
					_installed = true;
				}
				else if (!Patch(FindMethod(type, "ExecuteAtomic", 6), "ExpeditionKitAction.ExecuteAtomic"))
				{
					L.LogError((object)"[Fix] 目标方法没挂上 —— Stackmaster 版本可能不匹配,补丁未生效。");
					_installed = true;
				}
				else
				{
					_installed = true;
					L.LogInfo((object)"[Fix] 安装完成。transpiler 在方法首次被 JIT 时执行,届时会打印替换处数。");
				}
			}
		}

		private bool Patch(MethodInfo target, string label)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			if (target == null)
			{
				L.LogError((object)("[Fix] 找不到目标方法:" + label));
				return false;
			}
			try
			{
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(FixPlugin), "Transpile", (Type[])null, (Type[])null));
				HarmonyMethod val2 = new HarmonyMethod(AccessTools.Method(typeof(FixPlugin), "AfterMutation", (Type[])null, (Type[])null));
				Harmony harmony = _harmony;
				HarmonyMethod val3 = val;
				harmony.Patch((MethodBase)target, (HarmonyMethod)null, val2, val3, (HarmonyMethod)null, (HarmonyMethod)null);
				L.LogInfo((object)("[Fix] 已打补丁:" + label + "(transpiler + 写回 postfix)"));
				return true;
			}
			catch (Exception ex)
			{
				L.LogError((object)("[Fix] 打补丁失败 " + label + " : " + ex));
				return false;
			}
		}

		private static Type FindType(string fullName)
		{
			Type type = AccessTools.TypeByName(fullName);
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					if (assembly.GetName().Name.IndexOf("Stackmaster", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						type = assembly.GetType(fullName, throwOnError: false);
						if (type != null)
						{
							return type;
						}
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static MethodInfo FindMethod(Type type, string name, int paramCount)
		{
			if (type == null)
			{
				return null;
			}
			return type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == name && m.GetParameters().Length == paramCount);
		}

		internal static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			MethodInfo inventoryGetter = LiveResolver.InventoryGetter;
			MethodInfo itemGetter = LiveResolver.ItemGetter;
			MethodInfo operand = AccessTools.Method(typeof(LiveResolver), "ResolveLive", (Type[])null, (Type[])null);
			MethodInfo operand2 = AccessTools.Method(typeof(LiveResolver), "ResolveItem", (Type[])null, (Type[])null);
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			int num2 = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				MethodInfo methodInfo = instruction.operand as MethodInfo;
				if (methodInfo != null && inventoryGetter != null && methodInfo == inventoryGetter && (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = operand;
					num++;
				}
				else if (methodInfo != null && itemGetter != null && methodInfo == itemGetter && (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = operand2;
					num2++;
				}
				list.Add(instruction);
			}
			string text = DescribeMethod(__originalMethod) + "(" + list.Count + " 条指令)";
			if (num == 0 && num2 == 0)
			{
				L.LogError((object)("[Fix] ★transpiler 未匹配到任何目标指令:" + text + " —— 该处补丁无效(Stackmaster 版本变了?)。"));
			}
			else
			{
				L.LogInfo((object)("[Fix] transpiler 生效:" + text + " → get_Inventory 替换 " + num + " 处,get_Item 替换 " + num2 + " 处(共 " + list.Count + " 条指令)。"));
			}
			return list;
		}

		private static string DescribeMethod(MethodBase m)
		{
			if (m == null)
			{
				return "未知方法";
			}
			Type declaringType = m.DeclaringType;
			return ((declaringType != null) ? (declaringType.Name + ".") : "") + m.Name;
		}

		internal static void AfterMutation(ref bool __result)
		{
			try
			{
				if (__result)
				{
					LiveResolver.FlushContainerWrites();
				}
				else
				{
					LiveResolver.TouchedClear();
				}
			}
			catch (Exception ex)
			{
				L.LogError((object)("[Fix] AfterMutation 异常:" + ex));
			}
		}
	}
	internal static class LiveResolver
	{
		private const BindingFlags F = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		internal static MethodInfo InventoryGetter;

		internal static MethodInfo ItemGetter;

		private static PropertyInfo _piHandleOnStack;

		private static PropertyInfo _piContainerOnHandle;

		private static MethodInfo _mGetInventory;

		private static MethodInfo _mGetItemAt;

		private static FieldInfo _fNview;

		private static MethodInfo _mSave;

		private static MethodInfo _mNviewGetZdo;

		private static Inventory _empty;

		private static readonly List<Container> Touched = new List<Container>();

		private static readonly HashSet<int> TouchedIds = new HashSet<int>();

		private static readonly HashSet<int> Warned = new HashSet<int>();

		internal static bool Init(Type runtimeStackType, Type containerHandleType)
		{
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			try
			{
				InventoryGetter = runtimeStackType.GetProperty("Inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetGetMethod(nonPublic: true);
				ItemGetter = runtimeStackType.GetProperty("Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetGetMethod(nonPublic: true);
				_piHandleOnStack = runtimeStackType.GetProperty("Container", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_piContainerOnHandle = containerHandleType.GetProperty("Container", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_mGetInventory = typeof(Container).GetMethod("GetInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_mGetItemAt = typeof(Inventory).GetMethod("GetItemAt", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(int),
					typeof(int)
				}, null);
				_fNview = typeof(Container).GetField("m_nview", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_mSave = typeof(Container).GetMethod("Save", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_mNviewGetZdo = typeof(ZNetView).GetMethod("GetZDO", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_empty = new Inventory(true);
				bool flag = InventoryGetter != null && ItemGetter != null && _piHandleOnStack != null && _piContainerOnHandle != null && _mGetInventory != null && _mGetItemAt != null && _fNview != null && _mSave != null && _mNviewGetZdo != null;
				FixPlugin.L.LogInfo((object)("[Fix] 反射句柄:getInventory=" + (InventoryGetter != null) + " getItem=" + (ItemGetter != null) + " handle.Container=" + (_piContainerOnHandle != null) + " Container.GetInventory=" + (_mGetInventory != null) + " Container.m_nview=" + (_fNview != null) + " Container.Save=" + (_mSave != null) + " => " + (flag ? "OK" : "★缺★")));
				return flag;
			}
			catch (Exception ex)
			{
				FixPlugin.L.LogError((object)("[Fix] LiveResolver.Init 异常:" + ex));
				return false;
			}
		}

		internal static Inventory ResolveLive(object stack)
		{
			try
			{
				bool redirected;
				return ResolveCore(stack, out redirected);
			}
			catch (Exception ex)
			{
				FixPlugin.L.LogError((object)("[Fix] ResolveLive 异常:" + ex));
				return null;
			}
		}

		internal static ItemData ResolveItem(object stack)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object obj = InvokeGetter(stack, ItemGetter);
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				bool redirected;
				Inventory val2 = ResolveCore(stack, out redirected);
				if (!redirected || val == null || val2 == null || val2 == _empty)
				{
					return val;
				}
				Vector2i gridPos = val.m_gridPos;
				object? obj2 = _mGetItemAt.Invoke(val2, new object[2] { gridPos.x, gridPos.y });
				ItemData val3 = (ItemData)((obj2 is ItemData) ? obj2 : null);
				if (val3 == null || val3.m_shared == null || val.m_shared == null)
				{
					return val;
				}
				if (!string.Equals(val3.m_shared.m_name, val.m_shared.m_name, StringComparison.Ordinal) || val3.m_quality != val.m_quality)
				{
					return val;
				}
				return val3;
			}
			catch (Exception ex)
			{
				FixPlugin.L.LogError((object)("[Fix] ResolveItem 异常:" + ex));
				return null;
			}
		}

		private static Inventory ResolveCore(object stack, out bool redirected)
		{
			redirected = false;
			object obj = InvokeGetter(stack, InventoryGetter);
			Inventory result = (Inventory)((obj is Inventory) ? obj : null);
			object obj2 = ReadMember(stack, _piHandleOnStack);
			if (obj2 == null)
			{
				return result;
			}
			object obj3 = ReadMember(obj2, _piContainerOnHandle);
			Container val = (Container)((obj3 is Container) ? obj3 : null);
			if ((Object)(object)val == (Object)null)
			{
				return _empty;
			}
			if (!val.IsOwner())
			{
				WarnOnce("[Fix] ★容器 " + Describe(val) + " 不是本机所有(IsOwner=False)→ 拒绝在副本上扣减,改为让本次操作失败(宁可取消也不复制)。");
				return _empty;
			}
			object? obj4 = _mGetInventory.Invoke(val, null);
			Inventory val2 = (Inventory)((obj4 is Inventory) ? obj4 : null);
			if (val2 == null)
			{
				WarnOnce("[Fix] ★容器 " + Describe(val) + " 拿不到活库存 → fail-closed。");
				return _empty;
			}
			redirected = true;
			if (Remember(val) && FixPlugin.Verbose.Value)
			{
				FixPlugin.L.LogInfo((object)("[Fix] 扣减重定向 → 真实容器 " + Describe(val) + " 活库存总数=" + TotalUnits(val2)));
			}
			return val2;
		}

		private static bool Remember(Container c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)c).GetInstanceID();
			if (!TouchedIds.Add(instanceID))
			{
				return false;
			}
			Touched.Add(c);
			return true;
		}

		private static object InvokeGetter(object target, MethodInfo getter)
		{
			if (target == null || getter == null)
			{
				return null;
			}
			try
			{
				return getter.Invoke(target, null);
			}
			catch
			{
				return null;
			}
		}

		private static object ReadMember(object target, MemberInfo member)
		{
			if (target == null || member == null)
			{
				return null;
			}
			try
			{
				FieldInfo fieldInfo = member as FieldInfo;
				if (fieldInfo != null)
				{
					return fieldInfo.GetValue(target);
				}
				PropertyInfo propertyInfo = member as PropertyInfo;
				return (propertyInfo != null) ? propertyInfo.GetValue(target, null) : null;
			}
			catch
			{
				return null;
			}
		}

		private static void WarnOnce(string msg)
		{
			int hashCode = msg.GetHashCode();
			if (!Warned.Contains(hashCode))
			{
				Warned.Add(hashCode);
				FixPlugin.L.LogWarning((object)msg);
			}
		}

		private static string Describe(Container c)
		{
			try
			{
				ZDO val = ZdoOf(c);
				string text = ((val != null) ? ((object)Unsafe.As<ZDOID, ZDOID>(ref val.m_uid)/*cast due to .constrained prefix*/).ToString() : "?");
				string text2 = (((Object)(object)((Component)c).gameObject != (Object)null) ? ((Object)((Component)c).gameObject).name : "?");
				return text2 + " uid=" + text + " 库存=" + TotalUnits(c.GetInventory());
			}
			catch
			{
				return "<描述失败>";
			}
		}

		private static int TotalUnits(Inventory inv)
		{
			if (inv == null)
			{
				return -1;
			}
			try
			{
				List<ItemData> allItems = inv.GetAllItems();
				if (allItems == null)
				{
					return 0;
				}
				int num = 0;
				for (int i = 0; i < allItems.Count; i++)
				{
					if (allItems[i] != null)
					{
						num += allItems[i].m_stack;
					}
				}
				return num;
			}
			catch
			{
				return -1;
			}
		}

		private static ZDO ZdoOf(Container c)
		{
			try
			{
				if ((Object)(object)c == (Object)null || _fNview == null || _mNviewGetZdo == null)
				{
					return null;
				}
				object? value = _fNview.GetValue(c);
				ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					return null;
				}
				object? obj = _mNviewGetZdo.Invoke(val, null);
				return (ZDO)((obj is ZDO) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		internal static void FlushContainerWrites()
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (Touched.Count == 0)
			{
				return;
			}
			try
			{
				long num = 0L;
				try
				{
					num = ZDOMan.GetSessionID();
				}
				catch
				{
				}
				Container[] array = Touched.ToArray();
				foreach (Container val in array)
				{
					try
					{
						if ((Object)(object)val == (Object)null)
						{
							continue;
						}
						ZDO val2 = ZdoOf(val);
						if (val2 == null)
						{
							continue;
						}
						if (num != 0L && val2.GetOwner() != num)
						{
							FixPlugin.L.LogWarning((object)("[Fix] ★跳过写回:容器 " + Describe(val) + " 的 owner(" + val2.GetOwner() + ") 不是本机(" + num + ") —— 这次扣减可能不落库。"));
						}
						else
						{
							_mSave.Invoke(val, null);
							if (ZDOMan.instance != null)
							{
								ZDOMan.instance.ForceSendZDO(val2.m_uid);
							}
							if (FixPlugin.Verbose.Value)
							{
								FixPlugin.L.LogInfo((object)("[Fix] 已强制写回 ZDO: " + Describe(val) + " zdoRev=" + val2.DataRevision));
							}
						}
					}
					catch (Exception ex)
					{
						FixPlugin.L.LogError((object)("[Fix] 写回单个容器异常:" + ex.Message));
					}
				}
			}
			finally
			{
				TouchedClear();
			}
		}

		internal static void TouchedClear()
		{
			Touched.Clear();
			TouchedIds.Clear();
		}
	}
}