using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("StackingPlus")] [assembly: AssemblyDescription("Dyson Sphere Program BepInEx mod.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("StackingPlus")] [assembly: AssemblyCopyright("Copyright (c) 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("00000000-0000-0000-0000-000000000000")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")] [assembly: AssemblyVersion("1.0.0.0")] namespace StackingPlus; internal static class ModSettings { public static bool EnableSorterOutput = true; public static int SorterOutputCap = 8; public static bool EnableSorterInput = true; public static int SorterInputCap = 8; public static bool EnableStationPiler = true; public static int StationPilerCap = 8; public static bool EnableDeliveryPackage = true; public static int DeliveryPackageCap = 8; public static bool TechGated = true; public static int VanillaCeilingOutput = 4; public static int VanillaCeilingInput = 4; public static int VanillaCeilingPiler = 4; public static int VanillaCeilingPackage = 4; public static bool BeltSpeedEnable = false; public static float BeltSpeedMultiplier = 2f; public const int HardStackCeiling = 255; public static int ClampStack(int v) { if (v < 2) { return 2; } if (v > 255) { return 255; } return v; } } internal static class BeltSpeedPatch { private static readonly Dictionary _origBeltSpeed = new Dictionary(); private static readonly Dictionary _scaledToOrig = new Dictionary(); private static bool _snapshotTaken; private static float _appliedMult = float.NaN; private static FieldRef _chunkCountRef = AccessTools.FieldRefAccess("chunkCount"); internal static void EnsureProtoScaled() { try { if (!ModSettings.BeltSpeedEnable || (_snapshotTaken && _appliedMult == ModSettings.BeltSpeedMultiplier)) { return; } ItemProtoSet items = LDB.items; if ((Object)(object)items == (Object)null || ((ProtoSet)(object)items).dataArray == null) { return; } float beltSpeedMultiplier = ModSettings.BeltSpeedMultiplier; int num = 0; ItemProto[] dataArray = ((ProtoSet)(object)items).dataArray; foreach (ItemProto val in dataArray) { if (val == null) { continue; } PrefabDesc prefabDesc = val.prefabDesc; if (prefabDesc != null && prefabDesc.isBelt && prefabDesc.beltSpeed > 0) { if (!_origBeltSpeed.TryGetValue(((Proto)val).ID, out var value)) { value = prefabDesc.beltSpeed; _origBeltSpeed[((Proto)val).ID] = value; } int num2 = ScaleSpeed(value, beltSpeedMultiplier); StackingPlusLog.Info("[belt] proto " + ((Proto)val).ID + " (" + ((Proto)val).name + ") orig=" + value + " current=" + prefabDesc.beltSpeed + " -> target=" + num2 + " (x" + beltSpeedMultiplier + ")"); if (prefabDesc.beltSpeed != num2) { prefabDesc.beltSpeed = num2; num++; } _scaledToOrig[num2] = value; } } _snapshotTaken = true; _appliedMult = beltSpeedMultiplier; if (num > 0) { StackingPlusLog.Info("[belt] scaled " + num + " belt proto(s) x" + beltSpeedMultiplier); } } catch (Exception ex) { StackingPlusLog.Warn("EnsureProtoScaled failed: " + ex.Message); } } private static int ScaledSpeedForProto(int protoId) { if (_origBeltSpeed.TryGetValue(protoId, out var value)) { return ScaleSpeed(value, ModSettings.BeltSpeedMultiplier); } ItemProto val = ((ProtoSet)(object)LDB.items).Select(protoId); if (val != null && val.prefabDesc != null && val.prefabDesc.isBelt) { return val.prefabDesc.beltSpeed; } return 0; } internal static int OriginalSpeedForProto(int protoId) { if (_origBeltSpeed.TryGetValue(protoId, out var value)) { return value; } ItemProto val = ((ProtoSet)(object)LDB.items).Select(protoId); if (val != null && val.prefabDesc != null && val.prefabDesc.isBelt) { float beltSpeedMultiplier = ModSettings.BeltSpeedMultiplier; int beltSpeed = val.prefabDesc.beltSpeed; if (!(beltSpeedMultiplier > 1f)) { return beltSpeed; } return Math.Max(1, (int)Math.Round((float)beltSpeed / beltSpeedMultiplier)); } return -1; } internal static void BeforeSetBeltState(CargoTraffic __instance, int beltId, ref int state) { try { if (!ModSettings.BeltSpeedEnable) { return; } if (_scaledToOrig.TryGetValue(state, out var value) && value != state) { StackingPlusLog.Info("[belt][visual] SetBeltState belt#" + beltId + " tread-state " + state + " -> " + value + " (value-based; visual identity preserved)."); state = value; } else { if (__instance == null || __instance.factory == null || __instance.factory.entityPool == null || __instance.beltPool == null || beltId <= 0 || beltId >= __instance.beltPool.Length) { return; } int entityId = __instance.beltPool[beltId].entityId; if (entityId > 0 && entityId < __instance.factory.entityPool.Length) { int protoId = __instance.factory.entityPool[entityId].protoId; int num = OriginalSpeedForProto(protoId); if (num > 0 && state != num) { StackingPlusLog.Info("[belt][visual] SetBeltState belt#" + beltId + " proto=" + protoId + " tread-state " + state + " -> " + num + " (entity-based fallback)."); state = num; } } } } catch (Exception ex) { StackingPlusLog.Warn("BeforeSetBeltState failed: " + ex.Message); } } internal static void AfterHeadOrRearSpeed(ref int __result) { try { if (ModSettings.BeltSpeedEnable && _scaledToOrig.TryGetValue(__result, out var value) && value != __result) { __result = value; } } catch (Exception ex) { StackingPlusLog.Warn("AfterHeadOrRearSpeed failed: " + ex.Message); } } internal static void BeforeAlterBeltRenderer(CargoTraffic __instance, int beltId, out int __state) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) __state = -1; try { if (!ModSettings.BeltSpeedEnable || __instance == null || __instance.beltPool == null || beltId <= 0 || beltId >= __instance.beltPool.Length) { return; } BeltComponent val = __instance.beltPool[beltId]; if (val.id != beltId || __instance.factory == null || __instance.factory.entityPool == null) { return; } int entityId = val.entityId; if (entityId > 0 && entityId < __instance.factory.entityPool.Length) { int num = OriginalSpeedForProto(__instance.factory.entityPool[entityId].protoId); if (num > 0 && num != val.speed) { __state = val.speed; __instance.beltPool[beltId].speed = num; } } } catch (Exception ex) { StackingPlusLog.Warn("BeforeAlterBeltRenderer failed: " + ex.Message); } } internal static void AfterAlterBeltRenderer(CargoTraffic __instance, int beltId, int __state) { try { if (__state >= 0 && __instance != null && __instance.beltPool != null && beltId > 0 && beltId < __instance.beltPool.Length) { __instance.beltPool[beltId].speed = __state; } } catch (Exception ex) { StackingPlusLog.Warn("AfterAlterBeltRenderer failed: " + ex.Message); } } private static int ReadChunkSpeed(CargoPath path, int index) { try { if (path == null || path.chunks == null || _chunkCountRef == null) { return -1; } int num = _chunkCountRef.Invoke(path); for (int i = 0; i < num; i++) { int num2 = path.chunks[i * 3]; int num3 = path.chunks[i * 3 + 1]; if (index >= num2 && index < num2 + num3) { return path.chunks[i * 3 + 2]; } } } catch { } return -1; } private static int ScaleSpeed(int orig, float m) { if (m <= 1f) { return orig; } int num = (int)Math.Round((float)orig * m); if (num < 1) { num = 1; } if (num > 255) { num = 255; } return num; } internal static void AfterCargoTrafficImport(CargoTraffic __instance) { //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) try { if (!ModSettings.BeltSpeedEnable) { return; } EnsureProtoScaled(); PlanetFactory factory = __instance.factory; if (factory == null || factory.entityPool == null || __instance.beltPool == null || __instance.pathPool == null) { return; } int num = 0; int num2 = 0; for (int i = 1; i < __instance.beltCursor; i++) { BeltComponent val = __instance.beltPool[i]; if (val.id != i) { continue; } int entityId = val.entityId; if (entityId <= 0 || entityId >= factory.entityPool.Length) { continue; } int protoId = factory.entityPool[entityId].protoId; int num3 = ScaledSpeedForProto(protoId); if (num3 <= 0) { continue; } int segPathId = val.segPathId; CargoPath val2 = ((segPathId > 0 && segPathId < __instance.pathPool.Length) ? __instance.pathPool[segPathId] : null); bool flag = num2 < 3; int num4 = -1; if (flag && val2 != null) { num4 = ReadChunkSpeed(val2, val.segIndex); } if (__instance.beltPool[i].speed != num3) { __instance.beltPool[i].speed = num3; } if (val2 != null && val.segLength > 0) { val2.InsertChunk(val.segIndex, val.segLength, num3); num++; } int num5 = OriginalSpeedForProto(protoId); if (num5 > 0) { try { __instance.SetBeltState(i, num5); } catch { } } if (flag && val2 != null) { int num6 = ReadChunkSpeed(val2, val.segIndex); StackingPlusLog.Info("[belt] sample belt#" + i + " proto=" + protoId + " segIdx=" + val.segIndex + " segLen=" + val.segLength + " target=" + num3 + " chunkSpeed " + num4 + " -> " + num6 + " beltPoolSpeed=" + __instance.beltPool[i].speed); num2++; } } if (num > 0) { string text = ((factory.planet != null) ? factory.planet.displayName : "?"); StackingPlusLog.Info("[belt] rewrote " + num + " existing belt segment(s) on " + text + " (multiplier x" + ModSettings.BeltSpeedMultiplier + ")."); } } catch (Exception ex) { StackingPlusLog.Error("AfterCargoTrafficImport failed: " + ex); } } } internal static class StackingPatch { private static FieldRef _outputRef; private static FieldRef _inputRef; private static FieldRef _pilerRef; private static bool _outputOk; private static bool _inputOk; private static bool _pilerOk; private static bool _pendingRefresh; internal static void Init() { _outputRef = TryFieldRef("inserterStackOutput", out _outputOk); _inputRef = TryFieldRef("inserterStackInput", out _inputOk); _pilerRef = TryFieldRef("stationPilerLevel", out _pilerOk); } private static FieldRef TryFieldRef(string name, out bool ok) { try { FieldRef val = AccessTools.FieldRefAccess(name); ok = val != null; if (!ok) { StackingPlusLog.Warn("[compat] GameHistoryData." + name + " not found; that dimension is disabled."); } return val; } catch (Exception ex) { ok = false; StackingPlusLog.Warn("[compat] GameHistoryData." + name + " not found; that dimension is disabled: " + ex.Message); return null; } } internal static void AfterUnlockTech(GameHistoryData __instance) { Enforce(__instance, refreshInserters: true); } internal static void AfterImport(GameHistoryData __instance) { Enforce(__instance, refreshInserters: false); } internal static void AfterSetForNewGame(GameHistoryData __instance) { Enforce(__instance, refreshInserters: true); } internal static void OnGameBegin() { BeltSpeedPatch.EnsureProtoScaled(); if (_pendingRefresh) { SafeRefresh(GameMain.history); } } internal static void EnforceCurrent() { try { if ((Object)(object)GameMain.instance != (Object)null && GameMain.history != null) { Enforce(GameMain.history, refreshInserters: true); } } catch (Exception ex) { StackingPlusLog.Warn("EnforceCurrent failed: " + ex.Message); } } internal static void Enforce(GameHistoryData history, bool refreshInserters) { if (history == null) { return; } try { bool flag = false; if (ModSettings.EnableSorterOutput && _outputOk) { flag |= Apply(_outputRef, history, "SorterOutput", ModSettings.SorterOutputCap, ModSettings.VanillaCeilingOutput); } if (ModSettings.EnableSorterInput && _inputOk) { flag |= Apply(_inputRef, history, "SorterInput", ModSettings.SorterInputCap, ModSettings.VanillaCeilingInput); } if (ModSettings.EnableStationPiler && _pilerOk) { Apply(_pilerRef, history, "StationPiler", ModSettings.StationPilerCap, ModSettings.VanillaCeilingPiler); } if (ModSettings.EnableDeliveryPackage) { ApplyDelivery(); } if (flag) { if (refreshInserters) { SafeRefresh(history); } else { _pendingRefresh = true; } } } catch (Exception ex) { StackingPlusLog.Error("Enforce failed: " + ex); } } private static void SafeRefresh(GameHistoryData history) { try { if (history != null && history.gameData != null) { history.gameData.OnInserterTechChange(); _pendingRefresh = false; StackingPlusLog.Info("[stack] refreshed existing sorters."); } } catch (Exception ex) { _pendingRefresh = true; StackingPlusLog.Info("[stack] sorter refresh deferred (game not ready): " + ex.Message); } } private static bool Apply(FieldRef fieldRef, GameHistoryData history, string label, int cap, int ceiling) { int num = ModSettings.ClampStack(cap); int num2 = fieldRef.Invoke(history); if (ModSettings.TechGated && num2 < ceiling) { return false; } if (num <= num2) { return false; } fieldRef.Invoke(history) = num; StackingPlusLog.Info("[stack] " + label + ": " + num2 + " -> " + num + " (gated=" + ModSettings.TechGated + ", ceiling=" + ceiling + ")"); return true; } private static void ApplyDelivery() { try { Player mainPlayer = GameMain.mainPlayer; if (mainPlayer == null) { return; } DeliveryPackage deliveryPackage = mainPlayer.deliveryPackage; if (deliveryPackage != null) { int num = ModSettings.ClampStack(ModSettings.DeliveryPackageCap); int stackSizeMultiplier = deliveryPackage.stackSizeMultiplier; if ((!ModSettings.TechGated || stackSizeMultiplier >= ModSettings.VanillaCeilingPackage) && num > stackSizeMultiplier) { deliveryPackage.stackSizeMultiplier = num; StackingPlusLog.Info("[stack] DeliveryPackage: " + stackSizeMultiplier + " -> " + num + " (gated=" + ModSettings.TechGated + ", ceiling=" + ModSettings.VanillaCeilingPackage + ")"); } } } catch (Exception ex) { StackingPlusLog.Warn("ApplyDelivery failed: " + ex.Message); } } } [BepInPlugin("com.zicarius.StackingPlus", "StackingPlus", "0.1.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.zicarius.StackingPlus"; public const string PluginName = "StackingPlus"; public const string PluginVersion = "0.1.0"; private Harmony harmony; private ConfigEntry cfgDebugLog; private ConfigEntry cfgEnableOutput; private ConfigEntry cfgOutputCap; private ConfigEntry cfgEnableInput; private ConfigEntry cfgInputCap; private ConfigEntry cfgEnablePiler; private ConfigEntry cfgPilerCap; private ConfigEntry cfgEnablePackage; private ConfigEntry cfgPackageCap; private ConfigEntry cfgTechGated; private ConfigEntry cfgCeilingOutput; private ConfigEntry cfgCeilingInput; private ConfigEntry cfgCeilingPiler; private ConfigEntry cfgCeilingPackage; private ConfigEntry cfgBeltEnable; private ConfigEntry cfgBeltMultiplier; private void Awake() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown BindConfig(); StackingPlusLog.Init(((BaseUnityPlugin)this).Logger, cfgDebugLog); PushConfigToSettings(); WireLiveConfig(); StackingPatch.Init(); harmony = new Harmony("com.zicarius.StackingPlus"); ApplyPatches(); DumpTargetMethodSignatures(); LogResolvedConfig(); StackingPlusLog.Info("StackingPlus 0.1.0 loaded."); } private void Start() { StackingPatch.EnforceCurrent(); } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } StackingPlusLog.Info("StackingPlus unloaded."); } private void BindConfig() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Expected O, but got Unknown cfgDebugLog = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "DebugLog", false, "Enable verbose diagnostic logging to the BepInEx console."); cfgEnableOutput = ((BaseUnityPlugin)this).Config.Bind("Stacking", "EnableSorterOutput", true, "Raise Pile Sorter -> belt output stacking (the belt cargo cap). This is the core throughput fix."); cfgOutputCap = ((BaseUnityPlugin)this).Config.Bind("Stacking", "SorterOutputCap", 8, new ConfigDescription("Target sorter output stack size (vanilla 4). Cargo.stack is a byte so 255 is the hard ceiling.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 255), Array.Empty())); cfgEnableInput = ((BaseUnityPlugin)this).Config.Bind("Stacking", "EnableSorterInput", true, "Raise Pile Sorter pickup (input) stacking."); cfgInputCap = ((BaseUnityPlugin)this).Config.Bind("Stacking", "SorterInputCap", 8, new ConfigDescription("Target sorter input stack size (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange(2, 255), Array.Empty())); cfgEnablePiler = ((BaseUnityPlugin)this).Config.Bind("Stacking", "EnableStationPiler", true, "Raise the logistics station output piler level."); cfgPilerCap = ((BaseUnityPlugin)this).Config.Bind("Stacking", "StationPilerCap", 8, new ConfigDescription("Target station piler level (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange(2, 255), Array.Empty())); cfgEnablePackage = ((BaseUnityPlugin)this).Config.Bind("Stacking", "EnableDeliveryPackage", true, "Raise the delivery package stack-size multiplier."); cfgPackageCap = ((BaseUnityPlugin)this).Config.Bind("Stacking", "DeliveryPackageCap", 8, new ConfigDescription("Target delivery package multiplier (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange(2, 255), Array.Empty())); cfgTechGated = ((BaseUnityPlugin)this).Config.Bind("Stacking", "TechGated", true, "If true, each boost applies only once its vanilla ceiling is researched (early game stays vanilla). If false, caps are forced immediately."); cfgCeilingOutput = ((BaseUnityPlugin)this).Config.Bind("Advanced", "VanillaCeilingOutput", 4, "Vanilla max used for tech-gating sorter output. Override if a DSP update changes it."); cfgCeilingInput = ((BaseUnityPlugin)this).Config.Bind("Advanced", "VanillaCeilingInput", 4, "Vanilla max used for tech-gating sorter input."); cfgCeilingPiler = ((BaseUnityPlugin)this).Config.Bind("Advanced", "VanillaCeilingPiler", 4, "Vanilla max used for tech-gating station piler."); cfgCeilingPackage = ((BaseUnityPlugin)this).Config.Bind("Advanced", "VanillaCeilingPackage", 4, "Vanilla max used for tech-gating delivery package."); cfgBeltEnable = ((BaseUnityPlugin)this).Config.Bind("BeltSpeed", "Enable", false, "EXPERIMENTAL: multiply belt speed. Default OFF - stacking alone fixes throughput; belt speed needs in-game verification."); cfgBeltMultiplier = ((BaseUnityPlugin)this).Config.Bind("BeltSpeed", "Multiplier", 2f, new ConfigDescription("Belt speed multiplier applied to belts as they are created/rebuilt.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty())); } private void PushConfigToSettings() { ModSettings.EnableSorterOutput = cfgEnableOutput.Value; ModSettings.SorterOutputCap = cfgOutputCap.Value; ModSettings.EnableSorterInput = cfgEnableInput.Value; ModSettings.SorterInputCap = cfgInputCap.Value; ModSettings.EnableStationPiler = cfgEnablePiler.Value; ModSettings.StationPilerCap = cfgPilerCap.Value; ModSettings.EnableDeliveryPackage = cfgEnablePackage.Value; ModSettings.DeliveryPackageCap = cfgPackageCap.Value; ModSettings.TechGated = cfgTechGated.Value; ModSettings.VanillaCeilingOutput = cfgCeilingOutput.Value; ModSettings.VanillaCeilingInput = cfgCeilingInput.Value; ModSettings.VanillaCeilingPiler = cfgCeilingPiler.Value; ModSettings.VanillaCeilingPackage = cfgCeilingPackage.Value; ModSettings.BeltSpeedEnable = cfgBeltEnable.Value; ModSettings.BeltSpeedMultiplier = cfgBeltMultiplier.Value; } private void WireLiveConfig() { ((BaseUnityPlugin)this).Config.SettingChanged += delegate { PushConfigToSettings(); StackingPlusLog.Info("[config] changed; re-enforcing."); StackingPatch.EnforceCurrent(); }; } private void ApplyPatches() { TryPatch(typeof(GameHistoryData), "UnlockTechFunction", null, HM(typeof(StackingPatch), "AfterUnlockTech")); TryPatch(typeof(GameHistoryData), "Import", null, HM(typeof(StackingPatch), "AfterImport")); TryPatch(typeof(GameHistoryData), "SetForNewGame", null, HM(typeof(StackingPatch), "AfterSetForNewGame")); TryPatch(typeof(GameMain), "Begin", null, HM(typeof(StackingPatch), "OnGameBegin")); if (ModSettings.BeltSpeedEnable) { TryPatch(typeof(CargoTraffic), "Import", null, HM(typeof(BeltSpeedPatch), "AfterCargoTrafficImport")); TryPatch(typeof(CargoTraffic), "SetBeltState", HM(typeof(BeltSpeedPatch), "BeforeSetBeltState")); TryPatch(typeof(CargoTraffic), "AlterBeltRenderer", HM(typeof(BeltSpeedPatch), "BeforeAlterBeltRenderer"), HM(typeof(BeltSpeedPatch), "AfterAlterBeltRenderer")); HarmonyMethod postfix = HM(typeof(BeltSpeedPatch), "AfterHeadOrRearSpeed"); TryPatchProperty(typeof(CargoPath), "headSpeed", postfix); TryPatchProperty(typeof(CargoPath), "rearSpeed", postfix); } } private static HarmonyMethod HM(Type type, string method) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown return new HarmonyMethod(type.GetMethod(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); } private void TryPatchProperty(Type type, string propertyName, HarmonyMethod postfix) { try { MethodInfo methodInfo = AccessTools.PropertyGetter(type, propertyName); if (methodInfo == null) { StackingPlusLog.Warn("[compat] property getter not found: " + type.Name + "." + propertyName + " - patch skipped."); } else { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); StackingPlusLog.Info("[patch] patched " + type.Name + "." + propertyName + " (getter)"); } } catch (Exception ex) { StackingPlusLog.Error("[patch] failed to patch " + type.Name + "." + propertyName + " getter: " + ex); } } private void TryPatch(Type type, string method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { StackingPlusLog.Warn("[compat] target not found: " + type.Name + "." + method + " - patch skipped."); } else { harmony.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); StackingPlusLog.Info("[patch] patched " + type.Name + "." + method); } } catch (Exception ex) { StackingPlusLog.Error("[patch] failed to patch " + type.Name + "." + method + ": " + ex); } } private void LogResolvedConfig() { StackingPlusLog.Info("[config] gated=" + ModSettings.TechGated + " output=" + ModSettings.EnableSorterOutput + "/" + ModSettings.SorterOutputCap + "(v" + ModSettings.VanillaCeilingOutput + ") input=" + ModSettings.EnableSorterInput + "/" + ModSettings.SorterInputCap + "(v" + ModSettings.VanillaCeilingInput + ") piler=" + ModSettings.EnableStationPiler + "/" + ModSettings.StationPilerCap + "(v" + ModSettings.VanillaCeilingPiler + ") package=" + ModSettings.EnableDeliveryPackage + "/" + ModSettings.DeliveryPackageCap + "(v" + ModSettings.VanillaCeilingPackage + ") belt=" + ModSettings.BeltSpeedEnable + "/x" + ModSettings.BeltSpeedMultiplier); } private void DumpTargetMethodSignatures() { string[][] array = new string[8][] { new string[2] { "GameHistoryData", "UnlockTechFunction" }, new string[2] { "GameHistoryData", "Import" }, new string[2] { "GameHistoryData", "SetForNewGame" }, new string[2] { "GameData", "OnInserterTechChange" }, new string[2] { "GameMain", "Begin" }, new string[2] { "CargoTraffic", "Import" }, new string[2] { "CargoTraffic", "SetBeltState" }, new string[2] { "CargoTraffic", "AlterBeltRenderer" } }; foreach (string[] array2 in array) { try { Type type = AccessTools.TypeByName(array2[0]); if (type == null) { StackingPlusLog.Warn("[diag] Type not found: " + array2[0]); continue; } MethodInfo methodInfo = AccessTools.Method(type, array2[1], (Type[])null, (Type[])null); if (methodInfo == null) { StackingPlusLog.Warn("[diag] Method not found: " + array2[0] + "." + array2[1]); continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("[diag] ").Append(array2[0]).Append(".") .Append(array2[1]) .Append("("); for (int j = 0; j < parameters.Length; j++) { if (j > 0) { stringBuilder.Append(", "); } stringBuilder.Append(parameters[j].ParameterType.Name).Append(" ").Append(parameters[j].Name); } stringBuilder.Append(")"); StackingPlusLog.Info(stringBuilder.ToString()); } catch (Exception ex) { StackingPlusLog.Error("[diag] Failed to inspect " + array2[0] + "." + array2[1] + ": " + ex.Message); } } } } internal static class StackingPlusLog { private static ManualLogSource logger; private static bool debugEnabled; public static void Init(ManualLogSource src, ConfigEntry debug) { logger = src; debugEnabled = debug.Value; debug.SettingChanged += delegate { debugEnabled = debug.Value; }; } public static void Info(string msg) { if (debugEnabled && logger != null) { logger.LogInfo((object)("[StackingPlus] " + msg)); } } public static void Warn(string msg) { if (logger != null) { logger.LogWarning((object)("[StackingPlus] " + msg)); } } public static void Error(string msg) { if (logger != null) { logger.LogError((object)("[StackingPlus] " + msg)); } } }