Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of StackmasterFix v1.2.0
plugins/StackmasterFix/StackmasterFix.dll
Decompiled a month agousing 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.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyProduct("StackmasterFix")] [assembly: AssemblyTitle("StackmasterFix")] [assembly: AssemblyVersion("1.2.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.2.0")] public class FixPlugin : BaseUnityPlugin { internal static ManualLogSource L; internal static ConfigEntry<bool> FixKit; internal static ConfigEntry<bool> DisableRememberedChests; internal static ConfigEntry<bool> Verbose; private Harmony _harmony; private bool _installed; private bool _kitDone; private bool _memDone; private static bool _memAnnounced; private void Awake() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown L = ((BaseUnityPlugin)this).Logger; FixKit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FixExpeditionKit", true, "修复「建造菜单 Alt+左键拉取一整套材料」(上游叫 Quick Grab Materials)不扣料(材料复制)。\n※ 键名沿用旧版的 `FixExpeditionKit`(上游 1.2.0 之前管这个功能叫 expedition kit),改名会让老用户已写好的配置失效,故不改。\n合成/升级那条路径上游 Stackmaster 1.1.5 已自行修复,本补丁不再介入,故没有对应开关。"); DisableRememberedChests = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableRememberedChests", true, "拦截 Stackmaster 1.2.0 新增的「记忆上次用过的箱子」回退:\n开启(默认)= 快速堆叠(Alt+E)只往『当前已经装有同类物品』的箱子里放,不再把物品塞回你已经清空的箱子。\n关闭 = 完全恢复上游行为(清空的箱子会按记忆被重新填上)。"); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", true, "打印每次重定向与强制写回的细节(定位问题时很有用,确认没问题后可关)。"); L.LogInfo((object)"=============== StackmasterFix 1.2.0 启动 ==============="); L.LogInfo((object)"[A] 把「Quick Grab Materials」的扣减从『只读快照副本』改到『真实容器活库存』,并强制写回 ZDO。"); L.LogInfo((object)"[B] 拦截「记忆上次用过的箱子」回退 —— 快速堆叠不再把物品塞回已被你清空的箱子。"); L.LogInfo((object)"说明:『用箱子材料合成/升级不扣料』上游 Stackmaster 1.1.5 已自行修复 ⇒ 本补丁已移除该逻辑。"); L.LogInfo((object)"说明:上游 1.2.0 把内部类 ExpeditionKitAction 改名为 QuickGrabMaterialsAction,本版已跟随(双名查找)。"); _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 && !_kitDone) { _kitDone = true; L.LogInfo((object)"[Fix] 配置里已关闭「Quick Grab Materials 修复」⇒ A 项不挂载。"); } if (!DisableRememberedChests.Value && !_memDone) { _memDone = true; L.LogInfo((object)"[Fix] 配置里已关闭「拦截记忆箱子回退」⇒ B 项不挂载。"); } if (!_kitDone && InstallKit()) { _kitDone = true; } if (!_memDone && InstallRemembered()) { _memDone = true; } _installed = _kitDone && _memDone; } private bool InstallKit() { Type type = FindType("Stackmaster.QuickGrabMaterialsAction") ?? FindType("Stackmaster.ExpeditionKitAction"); Type type2 = FindType("Stackmaster.RuntimeResourceStack"); Type type3 = FindType("Stackmaster.ContainerHandle"); if (type == null || type2 == null || type3 == null) { return false; } if (!LiveResolver.Init(type2, type3)) { L.LogError((object)"[Fix] LiveResolver 初始化失败:反射句柄拿不全,拒绝打补丁。"); return true; } MethodInfo methodInfo = FindMethod(type, "ExecuteAtomic", 6); if (methodInfo == null) { L.LogError((object)("[Fix] 找不到目标方法:" + type.Name + ".ExecuteAtomic(6 个参数) —— Stackmaster 版本可能不匹配,A 项未生效。")); return true; } if (Patch(methodInfo, type.Name + ".ExecuteAtomic", withFlushPostfix: true)) { L.LogInfo((object)("[Fix] A 项安装完成(目标类型 " + type.Name + ")。transpiler 在方法首次被 JIT 时执行,届时会打印替换处数。")); } return true; } private bool Patch(MethodInfo target, string label, bool withFlushPostfix) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) try { HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(FixPlugin), "Transpile", (Type[])null, (Type[])null)); HarmonyMethod val2 = ((!withFlushPostfix) ? ((HarmonyMethod)null) : 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 + (withFlushPostfix ? "(transpiler + 写回 postfix)" : ""))); SelfCheck(target, label); return true; } catch (Exception ex) { L.LogError((object)("[Fix] 打补丁失败 " + label + " : " + ex)); return false; } } private bool InstallRemembered() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown Type type = FindType("Stackmaster.RememberedChestDestinations"); if (type == null) { return false; } MethodInfo methodInfo = FindMethod(type, "ResolveFallbacks", 3); if (methodInfo == null) { L.LogError((object)"[Fix] 找不到 RememberedChestDestinations.ResolveFallbacks(3 个参数) —— 上游改了结构,B 项未生效。"); return true; } Type typeFromHandle = typeof(IReadOnlyDictionary<string, string>); if (methodInfo.ReturnType != typeFromHandle) { L.LogError((object)("[Fix] ★ResolveFallbacks 的返回类型不是 " + typeFromHandle.FullName + "(实际 " + methodInfo.ReturnType.FullName + ")⇒ 拒绝打补丁,B 项未生效。")); return true; } try { HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(FixPlugin), "SuppressRememberedFallback", (Type[])null, (Type[])null)); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); L.LogInfo((object)"[Fix] 已打补丁:RememberedChestDestinations.ResolveFallbacks(拦截『记忆箱子』回退)"); SelfCheck(methodInfo, "RememberedChestDestinations.ResolveFallbacks"); return true; } catch (Exception ex) { L.LogError((object)("[Fix] 打补丁失败 RememberedChestDestinations.ResolveFallbacks:" + ex)); return true; } } internal static void SuppressRememberedFallback(ref IReadOnlyDictionary<string, string> __result) { try { int num = ((__result != null) ? __result.Count : 0); __result = new Dictionary<string, string>(); if (num > 0) { if (!_memAnnounced) { _memAnnounced = true; L.LogInfo((object)("[Fix] B 项已生效:本次拦下 " + num + " 条『记忆箱子』回退(这些物品会留在背包里)。")); } else if (Verbose.Value) { L.LogInfo((object)("[Fix] 拦下『记忆箱子』回退 " + num + " 条。")); } } else if (Verbose.Value) { L.LogInfo((object)"[Fix] 本次没有『记忆箱子』回退候选(正常堆叠不受影响)。"); } } catch (Exception ex) { L.LogError((object)("[Fix] SuppressRememberedFallback 异常:" + ex)); } } private void SelfCheck(MethodInfo target, string label) { try { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)target); List<string> list = new List<string>(); if (patchInfo != null) { if (patchInfo.Prefixes != null) { foreach (Patch prefix in patchInfo.Prefixes) { list.Add(prefix.owner + ":prefix"); } } if (patchInfo.Postfixes != null) { foreach (Patch postfix in patchInfo.Postfixes) { list.Add(postfix.owner + ":postfix"); } } if (patchInfo.Transpilers != null) { foreach (Patch transpiler in patchInfo.Transpilers) { list.Add(transpiler.owner + ":transpiler"); } } } L.LogInfo((object)("[Fix] 自检 " + label + " → 声明类型=" + ((target.DeclaringType != null) ? target.DeclaringType.FullName : "?") + " 返回类型=" + target.ReturnType.FullName + " prefix=" + (patchInfo?.Prefixes?.Count).GetValueOrDefault() + " postfix=" + (patchInfo?.Postfixes?.Count).GetValueOrDefault() + " transpiler=" + (patchInfo?.Transpilers?.Count).GetValueOrDefault() + " 挂载者=[" + string.Join(", ", list.ToArray()) + "]")); } catch (Exception ex) { L.LogError((object)("[Fix] 自检失败 " + label + ":" + ex.Message)); } } 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(); } } }