Decompiled source of QuickStackStore ItemDrawers Fix v1.0.4

QuickStackStore.ItemDrawersCompat.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
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.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5e2d14ab692a8caa079858192fbfdad2db188d47")]
[assembly: AssemblyProduct("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyTitle("QuickStackStore.ItemDrawersCompat")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 QuickStackStoreItemDrawersCompat
{
	[BepInPlugin("pavel.quickstackstore.itemdrawers_compat", "QSS - ItemDrawers Compat (UseItem, MP-safe)", "1.0.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class QuickStackStoreItemDrawersCompatPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "pavel.quickstackstore.itemdrawers_compat";

		public const string ModName = "QSS - ItemDrawers Compat (UseItem, MP-safe)";

		public const string ModVersion = "1.0.3";

		internal static ConfigEntry<float> SearchRadius;

		internal static ConfigEntry<bool> IncludeHotbar;

		internal static ConfigEntry<int> MaxDrawersToCheck;

		internal static ConfigEntry<bool> PreferQssRadiusIfAvailable;

		internal static ConfigEntry<bool> FillEmptyDrawers;

		internal static ConfigEntry<bool> DebugLogging;

		private void Awake()
		{
			SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SearchRadius", 10f, "Drawer search radius (meters). Used if PreferQssRadiusIfAvailable=false, or if QSS radius cannot be read.");
			IncludeHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeHotbar", false, "If true, quickstack will also move items from hotbar.");
			MaxDrawersToCheck = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxDrawersToCheck", 200, "Safety limit: max number of drawers to evaluate (nearest first).");
			PreferQssRadiusIfAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreferQssRadiusIfAvailable", true, "If true, tries to read QSS nearby-range config and use it instead of SearchRadius.");
			FillEmptyDrawers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FillEmptyDrawers", false, "If false: only drawers that are already set to the same item will be used. If true: if no matching drawer exists, may place into empty drawers.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enable debug logging.");
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "pavel.quickstackstore.itemdrawers_compat");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"QSS - ItemDrawers Compat (UseItem, MP-safe) v1.0.3 loaded");
		}

		internal static void Dbg(string msg)
		{
			if (DebugLogging.Value)
			{
				Logger.CreateLogSource("QSS - ItemDrawers Compat (UseItem, MP-safe)").LogInfo((object)("[QSS-DrawersCompat] " + msg));
			}
		}
	}
	[HarmonyPatch]
	internal static class QssReportPatch
	{
		[HarmonyTargetMethod]
		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("QuickStackStore.QuickStackModule");
			if (!(type == null))
			{
				return AccessTools.Method(type, "ReportQuickStackResult", new Type[2]
				{
					typeof(Player),
					typeof(int)
				}, (Type[])null);
			}
			return null;
		}

		[HarmonyPrefix]
		private static void Prefix(Player player, ref int movedCount)
		{
			try
			{
				if (!((Object)(object)player == (Object)null) && !(AccessTools.TypeByName("kg_ItemDrawers.DrawerComponent") == null))
				{
					int num = DrawerQuickStacker.TryQuickStackIntoNearbyDrawers(player);
					if (num > 0)
					{
						movedCount += num;
					}
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[QSS-DrawersCompat] Error: {arg}");
			}
		}
	}
	internal static class DrawerQuickStacker
	{
		private readonly struct DrawerInfo
		{
			public readonly Component Comp;

			public readonly float Distance;

			public readonly string CurrentPrefab;

			public readonly int CurrentAmount;

			public DrawerInfo(Component comp, float dist, string currentPrefab, int currentAmount)
			{
				Comp = comp;
				Distance = dist;
				CurrentPrefab = currentPrefab;
				CurrentAmount = currentAmount;
			}
		}

		private const string DrawerTypeName = "kg_ItemDrawers.DrawerComponent";

		private static MethodInfo? _useItemMethod;

		private static MethodInfo? _getInventoryMethod;

		private static FieldInfo? _inventoryField;

		private static PropertyInfo? _piCurrentPrefab;

		private static PropertyInfo? _piCurrentAmount;

		public static int TryQuickStackIntoNearbyDrawers(Player player)
		{
			Player player2 = player;
			Type type = AccessTools.TypeByName("kg_ItemDrawers.DrawerComponent");
			if (type == null)
			{
				return 0;
			}
			if ((object)_useItemMethod == null)
			{
				_useItemMethod = AccessTools.Method(type, "UseItem", new Type[2]
				{
					typeof(Humanoid),
					typeof(ItemData)
				}, (Type[])null);
			}
			if (_useItemMethod == null)
			{
				return 0;
			}
			if ((object)_piCurrentPrefab == null)
			{
				_piCurrentPrefab = AccessTools.Property(type, "CurrentPrefab");
			}
			if ((object)_piCurrentAmount == null)
			{
				_piCurrentAmount = AccessTools.Property(type, "CurrentAmount");
			}
			float range = GetEffectiveRange();
			if (range <= 0.05f)
			{
				return 0;
			}
			Object[] array = Object.FindObjectsByType(type, (FindObjectsSortMode)0);
			if (array == null || array.Length == 0)
			{
				return 0;
			}
			int num = Math.Max(1, QuickStackStoreItemDrawersCompatPlugin.MaxDrawersToCheck.Value);
			List<DrawerInfo> list = (from o in array
				select (Component)(object)((o is Component) ? o : null) into c
				where (Object)(object)c != (Object)null && (Object)(object)c.gameObject != (Object)null && c.gameObject.activeInHierarchy
				select new DrawerInfo(c, Vector3.Distance(((Component)player2).transform.position, c.transform.position), ReadCurrentPrefab(c), ReadCurrentAmount(c)) into x
				where x.Distance <= range
				orderby x.Distance
				select x).Take(num).ToList();
			if (list.Count == 0)
			{
				return 0;
			}
			QuickStackStoreItemDrawersCompatPlugin.Dbg($"Nearby drawers found: {list.Count} (radius={range}, max={num})");
			Inventory humanoidInventory = GetHumanoidInventory((Humanoid)(object)player2);
			if (humanoidInventory == null)
			{
				return 0;
			}
			List<ItemData> list2 = humanoidInventory.GetAllItems()?.ToList();
			if (list2 == null || list2.Count == 0)
			{
				return 0;
			}
			bool includeHotbar = QuickStackStoreItemDrawersCompatPlugin.IncludeHotbar.Value;
			List<ItemData> list3 = (from i in list2
				where i != null
				where i.m_stack > 0
				where includeHotbar || !IsHotbarItem(i)
				where i.m_customData == null || i.m_customData.Count == 0
				where (Object)(object)i.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)i.m_dropPrefab).name)
				select i).ToList();
			if (list3.Count == 0)
			{
				return 0;
			}
			Dictionary<string, List<DrawerInfo>> dictionary = new Dictionary<string, List<DrawerInfo>>(StringComparer.Ordinal);
			List<DrawerInfo> list4 = new List<DrawerInfo>();
			foreach (DrawerInfo item in list)
			{
				if (!string.IsNullOrEmpty(item.CurrentPrefab))
				{
					if (!dictionary.TryGetValue(item.CurrentPrefab, out var value))
					{
						value = new List<DrawerInfo>();
						dictionary[item.CurrentPrefab] = value;
					}
					value.Add(item);
				}
				else
				{
					list4.Add(item);
				}
			}
			foreach (KeyValuePair<string, List<DrawerInfo>> item2 in dictionary)
			{
				item2.Value.Sort(delegate(DrawerInfo a, DrawerInfo b)
				{
					float distance3 = a.Distance;
					return distance3.CompareTo(b.Distance);
				});
			}
			list4.Sort(delegate(DrawerInfo a, DrawerInfo b)
			{
				float distance2 = a.Distance;
				return distance2.CompareTo(b.Distance);
			});
			bool value2 = QuickStackStoreItemDrawersCompatPlugin.FillEmptyDrawers.Value;
			int num2 = 0;
			foreach (ItemData item3 in list3)
			{
				if (item3 == null || !humanoidInventory.ContainsItem(item3))
				{
					continue;
				}
				string name = ((Object)item3.m_dropPrefab).name;
				bool flag = false;
				if (dictionary.TryGetValue(name, out var value3) && value3.Count > 0)
				{
					foreach (DrawerInfo item4 in value3)
					{
						if (TryUseItem(player2, item3, item4.Comp))
						{
							num2++;
							flag = true;
							break;
						}
					}
				}
				if (flag || !value2 || list4.Count <= 0)
				{
					continue;
				}
				foreach (DrawerInfo item5 in list4)
				{
					if (!TryUseItem(player2, item3, item5.Comp))
					{
						continue;
					}
					num2++;
					flag = true;
					string text = ReadCurrentPrefab(item5.Comp);
					int num3 = ReadCurrentAmount(item5.Comp);
					if (!string.IsNullOrEmpty(text) && num3 > 0)
					{
						list4.Remove(item5);
						if (!dictionary.TryGetValue(text, out var value4))
						{
							value4 = (dictionary[text] = new List<DrawerInfo>());
						}
						value4.Add(new DrawerInfo(item5.Comp, item5.Distance, text, num3));
						value4.Sort(delegate(DrawerInfo a, DrawerInfo b)
						{
							float distance = a.Distance;
							return distance.CompareTo(b.Distance);
						});
					}
					break;
				}
			}
			if (num2 > 0)
			{
				QuickStackStoreItemDrawersCompatPlugin.Dbg($"Moved stacks into drawers: {num2}");
			}
			return num2;
		}

		private static bool TryUseItem(Player player, ItemData item, Component drawerComp)
		{
			try
			{
				ZNetView component = drawerComp.GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					component.ClaimOwnership();
				}
				object obj = _useItemMethod.Invoke(drawerComp, new object[2] { player, item });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static float GetEffectiveRange()
		{
			float result = Math.Max(0f, QuickStackStoreItemDrawersCompatPlugin.SearchRadius.Value);
			if (!QuickStackStoreItemDrawersCompatPlugin.PreferQssRadiusIfAvailable.Value)
			{
				return result;
			}
			float num = TryGetQssNearbyRangeOrNaN();
			if (!float.IsNaN(num) && num > 0.05f)
			{
				return num;
			}
			return result;
		}

		private static float TryGetQssNearbyRangeOrNaN()
		{
			try
			{
				Type type = AccessTools.TypeByName("QuickStackStore.QSSConfig+QuickStackConfig");
				if (type == null)
				{
					return float.NaN;
				}
				FieldInfo fieldInfo = AccessTools.Field(type, "QuickStackToNearbyRange");
				if (fieldInfo == null)
				{
					return float.NaN;
				}
				object value = fieldInfo.GetValue(null);
				if (value == null)
				{
					return float.NaN;
				}
				PropertyInfo property = value.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
				if (property == null)
				{
					return float.NaN;
				}
				return (property.GetValue(value) is float num) ? num : float.NaN;
			}
			catch
			{
				return float.NaN;
			}
		}

		private static Inventory? GetHumanoidInventory(Humanoid h)
		{
			try
			{
				if ((object)_getInventoryMethod == null)
				{
					_getInventoryMethod = AccessTools.Method(((object)h).GetType(), "GetInventory", Type.EmptyTypes, (Type[])null);
				}
				if (_getInventoryMethod != null)
				{
					object? obj = _getInventoryMethod.Invoke(h, Array.Empty<object>());
					Inventory val = (Inventory)((obj is Inventory) ? obj : null);
					if (val != null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			try
			{
				if ((object)_inventoryField == null)
				{
					_inventoryField = AccessTools.Field(((object)h).GetType(), "m_inventory");
				}
				if (_inventoryField != null)
				{
					object? value = _inventoryField.GetValue(h);
					Inventory val2 = (Inventory)((value is Inventory) ? value : null);
					if (val2 != null)
					{
						return val2;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool IsHotbarItem(ItemData item)
		{
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(((object)item).GetType(), "m_gridPos");
				if (fieldInfo == null)
				{
					return false;
				}
				object value = fieldInfo.GetValue(item);
				if (value == null)
				{
					return false;
				}
				FieldInfo field = value.GetType().GetField("y", BindingFlags.Instance | BindingFlags.Public);
				if (field == null)
				{
					return false;
				}
				return field.GetValue(value) is int num && num == 0;
			}
			catch
			{
				return false;
			}
		}

		private static string ReadCurrentPrefab(Component drawerComp)
		{
			try
			{
				if (_piCurrentPrefab == null)
				{
					return string.Empty;
				}
				return (_piCurrentPrefab.GetValue(drawerComp) as string) ?? string.Empty;
			}
			catch
			{
				return string.Empty;
			}
		}

		private static int ReadCurrentAmount(Component drawerComp)
		{
			try
			{
				if (_piCurrentAmount == null)
				{
					return 0;
				}
				return (_piCurrentAmount.GetValue(drawerComp) is int num) ? num : 0;
			}
			catch
			{
				return 0;
			}
		}
	}
}