Decompiled source of InfiniteStacks v1.0.1

InfiniteStacks.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("InfiniteStacks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("InfiniteStacks")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("2b503c2b-8ca8-43aa-a404-7c9e1133c06f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace WacoDev.Valheim.InfiniteStacks;

[BepInPlugin("wacodev.valheim.infinitestacks", "InfiniteStacks", "1.0.2")]
public class InfiniteStacksPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(ZNet), "Awake")]
	private static class ZNet_Awake_Patch
	{
		private static void Postfix(ZNet __instance)
		{
			if (__instance.IsServer())
			{
				Log.LogInfo((object)"ZNet.Awake (server) — scheduling ApplyInfiniteStacks when ObjectDB is ready.");
				((MonoBehaviour)Instance).StartCoroutine(WaitAndApplyOnServer());
			}
			else
			{
				Log.LogInfo((object)"ZNet.Awake (client) — not modifying ObjectDB.");
			}
		}
	}

	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	public static class InventoryGrid_UpdateGui_Patch
	{
		private static void Postfix(InventoryGrid __instance)
		{
			Log.LogInfo((object)"InfiniteStacks 1.0.2 UpdateGui Postfix.");
			if (!_hideMaxInTooltip.Value || !(AccessTools.Field(typeof(InventoryGrid), "m_elements")?.GetValue(__instance) is IList list))
			{
				return;
			}
			foreach (object item in list)
			{
				if (item == null)
				{
					continue;
				}
				object obj = AccessTools.Field(item.GetType(), "m_amount")?.GetValue(item);
				if (obj == null)
				{
					continue;
				}
				PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "text");
				if (propertyInfo == null || !propertyInfo.CanRead || !propertyInfo.CanWrite)
				{
					continue;
				}
				string text = propertyInfo.GetValue(obj) as string;
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				int num = text.IndexOf('/');
				if (num > 0)
				{
					string text2 = text.Substring(0, num);
					if ((object)text != text2)
					{
						propertyInfo.SetValue(obj, text2);
					}
				}
			}
		}

		private static Component FindFirstTextComponent(GameObject go)
		{
			Component[] componentsInChildren = go.GetComponentsInChildren<Component>(true);
			Component[] array = componentsInChildren;
			foreach (Component val in array)
			{
				PropertyInfo property = ((object)val).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite)
				{
					return val;
				}
			}
			return null;
		}
	}

	[HarmonyPatch(typeof(Hud), "Update")]
	public static class Hud_Update_Patch
	{
		private static void Postfix(Hud __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Transform[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val) || string.IsNullOrEmpty(((Object)val).name) || !((Object)val).name.ToLowerInvariant().Contains("hotkey"))
				{
					continue;
				}
				Component[] componentsInChildren2 = ((Component)val).gameObject.GetComponentsInChildren<Component>(true);
				Component[] array = componentsInChildren2;
				foreach (Component val2 in array)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					PropertyInfo property = ((object)val2).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public);
					if (property == null || !property.CanRead || !property.CanWrite)
					{
						continue;
					}
					string text = property.GetValue(val2) as string;
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					int num = text.IndexOf('/');
					if (num > 0)
					{
						string text2 = text.Substring(0, num);
						if ((object)text != text2)
						{
							property.SetValue(val2, text2);
						}
					}
				}
			}
		}
	}

	[CompilerGenerated]
	private sealed class <WaitAndApplyOnServer>d__10 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <WaitAndApplyOnServer>d__10(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				goto IL_0044;
			case 1:
				<>1__state = -1;
				goto IL_0044;
			case 2:
				{
					<>1__state = -1;
					break;
				}
				IL_0044:
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				break;
			}
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null || ObjectDB.instance.m_items.Count == 0)
			{
				<>2__current = null;
				<>1__state = 2;
				return true;
			}
			Log.LogInfo((object)"ObjectDB ready on server; applying InfiniteStacks…");
			ApplyInfiniteStacks(ObjectDB.instance);
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	public const string PluginGuid = "wacodev.valheim.infinitestacks";

	public const string PluginName = "InfiniteStacks";

	public const string PluginVersion = "1.0.2";

	private static ConfigEntry<int> _maxStackSize;

	private static ConfigEntry<bool> _includeEquipment;

	private static ConfigEntry<bool> _hideMaxInTooltip;

	public static ManualLogSource Log;

	internal static InfiniteStacksPlugin Instance;

	private void Awake()
	{
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Expected O, but got Unknown
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Expected O, but got Unknown
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Expected O, but got Unknown
		Instance = this;
		int num = 1000;
		_maxStackSize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Max Stack Size", int.MaxValue, new ConfigDescription("Max stack size to assign to eligible items. Default is effectively infinite.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, int.MaxValue), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Category = "General",
				Order = num--
			}
		}));
		_includeEquipment = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Include Equipment (Advanced)", false, new ConfigDescription("If true, also applies to weapons/armor/tools. Not recommended as stacked gear can be awkward.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Category = "General",
				Order = num--,
				IsAdvanced = true
			}
		}));
		_hideMaxInTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hide Max In Tooltip", true, new ConfigDescription("If true, tooltips show only the current stack count (e.g., “Stack: 1234”) and hide the max (e.g., “/999999”).", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Category = "General",
				Order = num--
			}
		}));
		Harmony val = new Harmony("wacodev.valheim.infinitestacks");
		val.PatchAll();
		Log = ((BaseUnityPlugin)this).Logger;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"InfiniteStacks 1.0.2 loaded.");
	}

	internal static bool IsEligible(SharedData sd, bool includeEquipment)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Invalid comparison between Unknown and I4
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Invalid comparison between Unknown and I4
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Invalid comparison between Unknown and I4
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Invalid comparison between Unknown and I4
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Invalid comparison between Unknown and I4
		ItemType itemType = sd.m_itemType;
		if (includeEquipment)
		{
			return true;
		}
		return (int)itemType == 1 || (int)itemType == 2 || (int)itemType == 9 || (int)itemType == 13 || (int)itemType == 16;
	}

	[IteratorStateMachine(typeof(<WaitAndApplyOnServer>d__10))]
	internal static IEnumerator WaitAndApplyOnServer()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <WaitAndApplyOnServer>d__10(0);
	}

	internal static void ApplyInfiniteStacks(ObjectDB db)
	{
		if (((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) || (Object)(object)db == (Object)null || db.m_items == null)
		{
			return;
		}
		int num = Math.Max(1, _maxStackSize.Value);
		foreach (GameObject item in db.m_items.Where((GameObject i) => Object.op_Implicit((Object)(object)i)))
		{
			ItemDrop component = item.GetComponent<ItemDrop>();
			if (component?.m_itemData?.m_shared != null)
			{
				SharedData shared = component.m_itemData.m_shared;
				if (IsEligible(shared, _includeEquipment.Value) && shared.m_maxStackSize < num)
				{
					Log.LogInfo((object)string.Format("{0} {1} Applying {2} to {3}.", "InfiniteStacks", "1.0.2", num, ((Object)component).name));
					shared.m_maxStackSize = num;
				}
			}
		}
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public string Category { get; set; }

	public int? Order { get; set; }

	public bool? IsAdvanced { get; set; }

	public bool? Browsable { get; set; }

	public bool? ReadOnly { get; set; }

	public bool? HideDefaultButton { get; set; }

	public bool? ShowRangeAsPercent { get; set; }

	public bool? RequiresRestart { get; set; }

	public string Description { get; set; }
}