Decompiled source of PlayerInventoryStacks v1.0.1

plugins/PlayerInventoryStacks/PlayerInventoryStacks.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SpaceCraft;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("PlayerInventoryStacks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PlayerInventoryStacks")]
[assembly: AssemblyTitle("PlayerInventoryStacks")]
[assembly: AssemblyVersion("1.0.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 PlayerInventoryStacks
{
	internal static class Backpacks
	{
		private static readonly HashSet<int> Ids = new HashSet<int>();

		private static readonly FieldInfo LockTime = typeof(WorldObject).GetField("_lockInInventoryTime", BindingFlags.Instance | BindingFlags.NonPublic);

		internal static void Clear()
		{
			Ids.Clear();
		}

		internal static void Register(int id)
		{
			if (id > 0)
			{
				Ids.Add(id);
			}
		}

		internal static bool IsPlayer(Inventory inventory)
		{
			if (inventory != null)
			{
				return Ids.Contains(inventory.GetId());
			}
			return false;
		}

		internal static string Key(WorldObject item)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			if (item == null)
			{
				throw new ArgumentNullException("item");
			}
			StringBuilder b;
			if (item.GetLinkedInventoryId() == 0)
			{
				List<int> secondaryInventoriesId = item.GetSecondaryInventoriesId();
				if ((secondaryInventoriesId == null || secondaryInventoriesId.Count <= 0) && item.GetLinkedWorldObject() == 0 && (float)LockTime.GetValue(item) == 0f)
				{
					b = new StringBuilder();
					Add(item.GetGroup().GetId());
					Add(item.GetText());
					Add(item.GetSetting());
					Add(item.GetGeneticTraitType());
					Add(item.GetGeneticTraitValue());
					Color color = item.GetColor();
					Add(color.r);
					Add(color.g);
					Add(color.b);
					Add(color.a);
					Add(item.GetEnergy());
					Add(item.GetGrowth());
					Add(item.GetHunger());
					Add(item.GetPetTime());
					Vector2Int count = item.GetCount();
					Add(((Vector2Int)(ref count)).x);
					count = item.GetCount();
					Add(((Vector2Int)(ref count)).y);
					Add(item.GetPlanetLinkedHash());
					List<Group> linkedGroups = item.GetLinkedGroups();
					Add(linkedGroups?.Count ?? 0);
					if (linkedGroups != null)
					{
						foreach (Group item2 in linkedGroups)
						{
							Add(item2.GetId());
						}
					}
					List<int> panelsId = item.GetPanelsId();
					Add(panelsId?.Count ?? 0);
					if (panelsId != null)
					{
						foreach (int item3 in panelsId)
						{
							Add(item3);
						}
					}
					return b.ToString();
				}
			}
			return "unique:" + item.GetId();
			void Add(object value)
			{
				string text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? "";
				b.Append(text.Length).Append(':').Append(text)
					.Append('|');
			}
		}

		internal static List<StackLayout.Stack<WorldObject>> Layout(Inventory inventory)
		{
			return StackLayout.Create(inventory.GetInsideWorldObjects(), Key);
		}

		internal static bool FullFor(Inventory inventory, WorldObject item)
		{
			if (!IsPlayer(inventory))
			{
				return inventory.IsFull();
			}
			if (item == null)
			{
				return Layout(inventory).Count >= inventory.GetSize();
			}
			if (inventory.ContainWorldObject(item))
			{
				return false;
			}
			return !StackLayout.CanAdd(inventory.GetInsideWorldObjects(), item, inventory.GetSize(), Key);
		}

		internal static bool FullForGroup(Inventory inventory, Group group)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			return FullFor(inventory, new WorldObject(0, group));
		}

		internal static bool FitsCraft(Inventory inventory, List<Group> ingredients, Group output, bool freeCraft)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			List<WorldObject> list = inventory.GetInsideWorldObjects().ToList();
			if (!freeCraft)
			{
				foreach (Group group in ingredients)
				{
					int num = list.FindLastIndex((WorldObject item) => item.GetGroup() == group);
					if (num >= 0)
					{
						list.RemoveAt(num);
					}
				}
			}
			return StackLayout.CanAdd(list, new WorldObject(0, output), inventory.GetSize(), Key);
		}
	}
	[BepInPlugin("local.planetcrafter.playerinventorystacks", "Player Inventory Stacks", "1.0.0")]
	[BepInProcess("Planet Crafter.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "local.planetcrafter.playerinventorystacks";

		private Harmony harmony;

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			harmony = new Harmony("local.planetcrafter.playerinventorystacks");
			try
			{
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Player Inventory Stacks 1.0.0 loaded: 64 items per backpack stack; containers unchanged.");
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("Player Inventory Stacks could not patch this game version; all its patches were removed. " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch(typeof(InventoriesHandler), "OnNetworkSpawn")]
	internal static class ResetWorld
	{
		private static void Prefix()
		{
			Backpacks.Clear();
		}
	}
	[HarmonyPatch(typeof(PlayerBackpack), "SetInventory")]
	internal static class RegisterBackpack
	{
		private static void Prefix(Inventory _playerInventory)
		{
			if (_playerInventory != null)
			{
				Backpacks.Register(_playerInventory.GetId());
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMainController), "InitPlayerClientRpc")]
	internal static class RegisterClientBackpack
	{
		private static void Prefix(int inventoryId)
		{
			Backpacks.Register(inventoryId);
		}
	}
	[HarmonyPatch(typeof(SavedDataHandler), "LoadSavedData")]
	internal static class RegisterSavedBackpacks
	{
		private static void Postfix(SavedDataHandler __instance)
		{
			List<JsonablePlayerState> lastLoadedPlayerData = __instance.GetLastLoadedPlayerData();
			if (lastLoadedPlayerData == null)
			{
				return;
			}
			foreach (JsonablePlayerState item in lastLoadedPlayerData)
			{
				Backpacks.Register(item.inventoryId);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "IsFull")]
	internal static class InventoryFull
	{
		private static bool Prefix(Inventory __instance, ref bool __result)
		{
			if (!Backpacks.IsPlayer(__instance))
			{
				return true;
			}
			__result = Backpacks.Layout(__instance).Count >= __instance.GetSize();
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItemInInventory")]
	internal static class AddItemCapacity
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			return PatchTools.ReplaceFull(instructions, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[1]
			{
				new CodeInstruction(OpCodes.Ldarg_1, (object)null)
			}, "FullFor");
		}
	}
	[HarmonyPatch(typeof(Inventory), "DropObjectsIfNotEnoughSpace")]
	internal static class BackpackShrink
	{
		private static bool Prefix(Inventory __instance, Vector3 dropPosition, bool removeOnly, ref List<WorldObject> __result)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!Backpacks.IsPlayer(__instance))
			{
				return true;
			}
			List<WorldObject> list = Backpacks.Layout(__instance).Skip(Math.Max(0, __instance.GetSize())).SelectMany((StackLayout.Stack<WorldObject> s) => s.Items)
				.ToList();
			foreach (WorldObject item in list)
			{
				if (!removeOnly)
				{
					WorldObjectsHandler.Instance.DropOnFloor(item, dropPosition, 0f, true, false, 0);
				}
				__instance.RemoveItem(item);
			}
			__result = list;
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryDisplayer), "SetInventoryBlocks")]
	internal static class DisplayStacks
	{
		private static void Prefix(Inventory ____inventory, ref ReadOnlyCollection<WorldObject> inventoryWorldObjects, out List<StackLayout.Stack<WorldObject>> __state)
		{
			__state = null;
			if (Backpacks.IsPlayer(____inventory))
			{
				__state = Backpacks.Layout(____inventory);
				inventoryWorldObjects = __state.Select((StackLayout.Stack<WorldObject> s) => s.Items[0]).ToList().AsReadOnly();
			}
		}

		private static void Postfix(Inventory ____inventory, GridLayoutGroup ____grid, List<StackLayout.Stack<WorldObject>> __state)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null)
			{
				return;
			}
			InventoryBlock[] componentsInChildren = ((Component)____grid).GetComponentsInChildren<InventoryBlock>();
			componentsInChildren = componentsInChildren.Skip(Math.Max(0, componentsInChildren.Length - ____inventory.GetSize())).ToArray();
			for (int i = 0; i < Math.Min(componentsInChildren.Length, __state.Count); i++)
			{
				int count = __state[i].Items.Count;
				if (count > 1)
				{
					GameObject val = new GameObject("PlayerStackCount", new Type[2]
					{
						typeof(RectTransform),
						typeof(TextMeshProUGUI)
					});
					val.transform.SetParent(((Component)componentsInChildren[i]).transform, false);
					RectTransform val2 = (RectTransform)val.transform;
					val2.anchorMin = Vector2.zero;
					val2.anchorMax = Vector2.one;
					val2.offsetMin = new Vector2(3f, 3f);
					val2.offsetMax = new Vector2(-5f, -3f);
					TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
					((TMP_Text)component).font = TMP_Settings.defaultFontAsset;
					((TMP_Text)component).text = count.ToString();
					((TMP_Text)component).fontSize = 22f;
					((TMP_Text)component).fontStyle = (FontStyles)1;
					((Graphic)component).color = Color.white;
					((TMP_Text)component).alignment = (TextAlignmentOptions)1028;
					((Graphic)component).raycastTarget = false;
					((TMP_Text)component).textWrappingMode = (TextWrappingModes)0;
					((TMP_Text)component).outlineWidth = 0.22f;
					((TMP_Text)component).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue);
				}
			}
		}
	}
	internal static class PatchTools
	{
		internal static readonly MethodInfo IsFull = AccessTools.Method(typeof(Inventory), "IsFull", (Type[])null, (Type[])null);

		internal static IEnumerable<CodeInstruction> ReplaceFull(IEnumerable<CodeInstruction> source, IEnumerable<CodeInstruction> loadCandidate, string helper, int expected = 1)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			foreach (CodeInstruction item in source)
			{
				if (CodeInstructionExtensions.Calls(item, IsFull))
				{
					List<CodeInstruction> list2 = loadCandidate.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction c) => new CodeInstruction(c))).ToList();
					list2[0].labels.AddRange(item.labels);
					list2[0].blocks.AddRange(item.blocks);
					list.AddRange(list2);
					list.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Backpacks), helper, (Type[])null, (Type[])null)));
					num++;
				}
				else
				{
					list.Add(item);
				}
			}
			if (num != expected)
			{
				throw new InvalidOperationException($"Expected {expected} capacity checks, found {num}.");
			}
			return list;
		}

		internal static WorldObject ActionItem(Component component)
		{
			WorldObjectAssociated componentInParent = component.GetComponentInParent<WorldObjectAssociated>();
			if (componentInParent == null)
			{
				return null;
			}
			return componentInParent.GetWorldObject();
		}

		internal static IEnumerable<CodeInstruction> ActionCapacity(IEnumerable<CodeInstruction> source, FieldInfo owner = null)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null)
			};
			if (owner != null)
			{
				list.Add(new CodeInstruction(OpCodes.Ldfld, (object)owner));
			}
			list.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(PatchTools), "ActionItem", (Type[])null, (Type[])null)));
			return ReplaceFull(source, list, "FullFor");
		}
	}
	[HarmonyPatch(typeof(ActionMinable), "OnAction")]
	internal static class StartMiningCapacity
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return PatchTools.ActionCapacity(instructions);
		}
	}
	[HarmonyPatch]
	internal static class FinishMiningCapacity
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(ActionMinable), "FinishMining", (Type[])null, (Type[])null));
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			return PatchTools.ActionCapacity(instructions, AccessTools.Field(original.DeclaringType, "<>4__this"));
		}
	}
	[HarmonyPatch(typeof(ActionGrabable), "OnAction")]
	internal static class GrabCapacity
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return PatchTools.ActionCapacity(instructions);
		}
	}
	[HarmonyPatch(typeof(CraftManager), "TryToCraftInInventory")]
	internal static class CraftCapacity
	{
		private static bool Prefix(PlayerMainController playerController, GroupItem groupItem, ref bool __result)
		{
			Inventory inventory = playerController.GetPlayerBackpack().GetInventory();
			if (!Backpacks.IsPlayer(inventory))
			{
				return true;
			}
			bool freeCraft = Managers.GetManager<GameSettingsHandler>().GetCurrentGameSettings().GetFreeCraft();
			if (Backpacks.FitsCraft(inventory, ((Group)groupItem).GetRecipe().GetIngredientsGroupInRecipe(), (Group)(object)groupItem, freeCraft))
			{
				return true;
			}
			Managers.GetManager<BaseHudHandler>().DisplayCursorText("UI_InventoryFull", 2f, "", "", false);
			__result = false;
			return false;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			return PatchTools.ReplaceFull(instructions, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[1]
			{
				new CodeInstruction(OpCodes.Ldarg_2, (object)null)
			}, "FullForGroup", 2);
		}
	}
	[HarmonyPatch(typeof(WorldObjectsHandler), "RetrieveResourcesFromDeconstructionServerRpc")]
	internal static class DeconstructionCapacity
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			int num = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, PatchTools.IsFull));
			int num2 = ((num < 0) ? (-1) : list.FindLastIndex(num, (CodeInstruction c) => CodeInstructionExtensions.Calls(c, AccessTools.Method(typeof(WorldObject), "GetGroup", (Type[])null, (Type[])null))));
			if (num2 <= 0 || !CodeInstructionExtensions.IsLdloc(list[num2 - 1], (LocalBuilder)null))
			{
				throw new InvalidOperationException("Could not identify deconstruction resource local.");
			}
			return PatchTools.ReplaceFull(list, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[1]
			{
				new CodeInstruction(list[num2 - 1].opcode, list[num2 - 1].operand)
			}, "FullFor");
		}
	}
	[HarmonyPatch(typeof(ActionPanelDeconstruct), "Deconstruct")]
	internal static class PanelCapacity
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			var list = (from local in original.GetMethodBody().LocalVariables
				select new
				{
					local = local,
					field = local.LocalType.GetField("group", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				} into pair
				where pair.field?.FieldType == typeof(Group)
				select pair).ToList();
			if (list.Count != 1)
			{
				throw new InvalidOperationException("Could not identify panel ingredient local.");
			}
			var anon = list[0];
			return PatchTools.ReplaceFull(instructions, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldloc, (object)anon.local.LocalIndex),
				new CodeInstruction(OpCodes.Ldfld, (object)anon.field)
			}, "FullForGroup");
		}
	}
	[HarmonyPatch(typeof(InventoriesHandler), "AnInventoryHasBeenClicked")]
	internal static class QuickTransferKey
	{
		internal static bool IsPressed(PlayerInputDispatcher input, Inventory source)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (input.IsPressingAccessibilityKey())
			{
				return true;
			}
			if (Keyboard.current == null || !Keyboard.current.shiftKey.isPressed)
			{
				return false;
			}
			WindowsHandler manager = Managers.GetManager<WindowsHandler>();
			UiWindow windowViaUiId = manager.GetWindowViaUiId(manager.GetOpenedUi());
			UiWindowContainer val = (UiWindowContainer)(object)((windowViaUiId is UiWindowContainer) ? windowViaUiId : null);
			if ((Object)(object)val != (Object)null)
			{
				if (!Backpacks.IsPlayer(source))
				{
					return Backpacks.IsPlayer(val.GetOtherInventory(source));
				}
				return true;
			}
			return false;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayerInputDispatcher), "IsPressingAccessibilityKey", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, methodInfo))
				{
					CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_1, (object)null);
					val.labels.AddRange(instruction.labels);
					val.blocks.AddRange(instruction.blocks);
					list.Add(val);
					list.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(QuickTransferKey), "IsPressed", (Type[])null, (Type[])null)));
					num++;
				}
				else
				{
					list.Add(instruction);
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Could not identify inventory quick-transfer key check.");
			}
			return list;
		}
	}
	internal static class StackLayout
	{
		internal sealed class Stack<T>
		{
			internal readonly List<T> Items = new List<T>();
		}

		internal const int Limit = 64;

		internal static List<Stack<T>> Create<T>(IEnumerable<T> items, Func<T, string> key)
		{
			List<Stack<T>> list = new List<Stack<T>>();
			Dictionary<string, Stack<T>> dictionary = new Dictionary<string, Stack<T>>(StringComparer.Ordinal);
			foreach (T item in items)
			{
				string key2 = key(item);
				if (!dictionary.TryGetValue(key2, out var value) || value.Items.Count == 64)
				{
					value = (dictionary[key2] = new Stack<T>());
					list.Add(value);
				}
				value.Items.Add(item);
			}
			return list;
		}

		internal static bool CanAdd<T>(IEnumerable<T> items, T candidate, int slots, Func<T, string> key)
		{
			List<Stack<T>> list = Create(items, key);
			if (list.Count < slots)
			{
				return true;
			}
			if (list.Count > slots)
			{
				return false;
			}
			string text = key(candidate);
			foreach (Stack<T> item in list)
			{
				if (item.Items.Count < 64 && key(item.Items[0]) == text)
				{
					return true;
				}
			}
			return false;
		}
	}
}