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.1.0
plugins/StackmasterFix/StackmasterFix.dll
Decompiled 20 hours 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.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(); } } }