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<int, int> _origBeltSpeed = new Dictionary<int, int>();
private static readonly Dictionary<int, int> _scaledToOrig = new Dictionary<int, int>();
private static bool _snapshotTaken;
private static float _appliedMult = float.NaN;
private static FieldRef<CargoPath, int> _chunkCountRef = AccessTools.FieldRefAccess<CargoPath, int>("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<ItemProto>)(object)items).dataArray == null)
{
return;
}
float beltSpeedMultiplier = ModSettings.BeltSpeedMultiplier;
int num = 0;
ItemProto[] dataArray = ((ProtoSet<ItemProto>)(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<ItemProto>)(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<ItemProto>)(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<GameHistoryData, int> _outputRef;
private static FieldRef<GameHistoryData, int> _inputRef;
private static FieldRef<GameHistoryData, int> _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<GameHistoryData, int> TryFieldRef(string name, out bool ok)
{
try
{
FieldRef<GameHistoryData, int> val = AccessTools.FieldRefAccess<GameHistoryData, int>(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<GameHistoryData, int> 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<bool> cfgDebugLog;
private ConfigEntry<bool> cfgEnableOutput;
private ConfigEntry<int> cfgOutputCap;
private ConfigEntry<bool> cfgEnableInput;
private ConfigEntry<int> cfgInputCap;
private ConfigEntry<bool> cfgEnablePiler;
private ConfigEntry<int> cfgPilerCap;
private ConfigEntry<bool> cfgEnablePackage;
private ConfigEntry<int> cfgPackageCap;
private ConfigEntry<bool> cfgTechGated;
private ConfigEntry<int> cfgCeilingOutput;
private ConfigEntry<int> cfgCeilingInput;
private ConfigEntry<int> cfgCeilingPiler;
private ConfigEntry<int> cfgCeilingPackage;
private ConfigEntry<bool> cfgBeltEnable;
private ConfigEntry<float> 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<bool>("Diagnostics", "DebugLog", false, "Enable verbose diagnostic logging to the BepInEx console.");
cfgEnableOutput = ((BaseUnityPlugin)this).Config.Bind<bool>("Stacking", "EnableSorterOutput", true, "Raise Pile Sorter -> belt output stacking (the belt cargo cap). This is the core throughput fix.");
cfgOutputCap = ((BaseUnityPlugin)this).Config.Bind<int>("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<int>(2, 255), Array.Empty<object>()));
cfgEnableInput = ((BaseUnityPlugin)this).Config.Bind<bool>("Stacking", "EnableSorterInput", true, "Raise Pile Sorter pickup (input) stacking.");
cfgInputCap = ((BaseUnityPlugin)this).Config.Bind<int>("Stacking", "SorterInputCap", 8, new ConfigDescription("Target sorter input stack size (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 255), Array.Empty<object>()));
cfgEnablePiler = ((BaseUnityPlugin)this).Config.Bind<bool>("Stacking", "EnableStationPiler", true, "Raise the logistics station output piler level.");
cfgPilerCap = ((BaseUnityPlugin)this).Config.Bind<int>("Stacking", "StationPilerCap", 8, new ConfigDescription("Target station piler level (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 255), Array.Empty<object>()));
cfgEnablePackage = ((BaseUnityPlugin)this).Config.Bind<bool>("Stacking", "EnableDeliveryPackage", true, "Raise the delivery package stack-size multiplier.");
cfgPackageCap = ((BaseUnityPlugin)this).Config.Bind<int>("Stacking", "DeliveryPackageCap", 8, new ConfigDescription("Target delivery package multiplier (vanilla 4).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 255), Array.Empty<object>()));
cfgTechGated = ((BaseUnityPlugin)this).Config.Bind<bool>("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<int>("Advanced", "VanillaCeilingOutput", 4, "Vanilla max used for tech-gating sorter output. Override if a DSP update changes it.");
cfgCeilingInput = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "VanillaCeilingInput", 4, "Vanilla max used for tech-gating sorter input.");
cfgCeilingPiler = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "VanillaCeilingPiler", 4, "Vanilla max used for tech-gating station piler.");
cfgCeilingPackage = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "VanillaCeilingPackage", 4, "Vanilla max used for tech-gating delivery package.");
cfgBeltEnable = ((BaseUnityPlugin)this).Config.Bind<bool>("BeltSpeed", "Enable", false, "EXPERIMENTAL: multiply belt speed. Default OFF - stacking alone fixes throughput; belt speed needs in-game verification.");
cfgBeltMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("BeltSpeed", "Multiplier", 2f, new ConfigDescription("Belt speed multiplier applied to belts as they are created/rebuilt.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
}
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<bool> 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));
}
}
}