Decompiled source of StorageGroups v0.1.7

plugins/StorageGroups.Core.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.CodeAnalysis;
using SmartContainerRouting.Core.Model;
using SmartContainerRouting.Core.Text;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("jonasd")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f556d01ba578811190b24467d0afe74ca948dcb8")]
[assembly: AssemblyProduct("Storage Groups")]
[assembly: AssemblyTitle("StorageGroups.Core")]
[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]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[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]
	[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]
	[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 SmartContainerRouting.Core.Text
{
	public static class ContainerHoverText
	{
		public static string? BuildExtraLine(ContainerSettings settings, Func<string, string?> groupDisplayName, Func<string, bool>? groupEvictsMismatching = null, bool isWarded = false)
		{
			List<string> list = new List<string>();
			string groupName = settings.GroupName;
			if (groupName != null)
			{
				list.Add("[" + (groupDisplayName(groupName) ?? groupName) + "]");
			}
			string groupName2 = settings.GroupName;
			if (groupName2 != null && (groupEvictsMismatching?.Invoke(groupName2) ?? settings.IsEvicting))
			{
				list.Add("Strict: only group items");
			}
			if (isWarded)
			{
				list.Add("Routes within this ward only");
			}
			if (list.Count != 0)
			{
				return string.Join(" | ", list);
			}
			return null;
		}
	}
	public static class DisplayNameSanitizer
	{
		public const int MaxLength = 32;

		public static string? Sanitize(string? value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return null;
			}
			string text = StripMarkupCharacters(value.Trim());
			if (text.Length == 0)
			{
				return null;
			}
			if (text.Length <= 32)
			{
				return text;
			}
			return text.Substring(0, 32);
		}

		private static string StripMarkupCharacters(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (c != '<' && c != '>')
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	public static class GroupButtonHover
	{
		public const int MaxItems = 8;

		public static string Describe(string? groupName, IEnumerable<string> itemNames)
		{
			List<string> list = new List<string> { "Open group settings" };
			if (groupName == null)
			{
				list.Add("No group assigned");
				return string.Join("\n", list);
			}
			list.Add("Items in current group [" + groupName + "]:");
			List<string> list2 = itemNames.Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string name) => name, StringComparer.Ordinal).ToList();
			if (list2.Count == 0)
			{
				list.Add("(none yet, use Add Contents)");
			}
			else
			{
				list.AddRange(list2.Take(8));
				if (list2.Count > 8)
				{
					list.Add($"+{list2.Count - 8} more");
				}
			}
			return string.Join("\n", list);
		}
	}
	public static class GroupNameNormalizer
	{
		public static string? Normalize(string? value)
		{
			return DisplayNameSanitizer.Sanitize(value)?.ToLowerInvariant();
		}
	}
	public static class GroupNameSuggestion
	{
		public static string? Suggest(IEnumerable<KeyValuePair<string, int>> stacks, GroupRegistry registry)
		{
			string text = (from entry in (from @group in (from stack in stacks
						select new KeyValuePair<string, int>(DisplayNameSanitizer.Sanitize(stack.Key), stack.Value) into stack
						where stack.Key != null
						select stack).GroupBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> stack) => stack.Key, StringComparer.Ordinal)
					select (Name: @group.Key, Total: @group.Sum((KeyValuePair<string, int> stack) => stack.Value)) into entry
					orderby entry.Total descending
					select entry).ThenBy<(string, int), string>(((string Name, int Total) entry) => entry.Name, StringComparer.Ordinal)
				select entry.Name).FirstOrDefault();
			if (text == null || !registry.HasGroup(text))
			{
				return text;
			}
			int num = 2;
			string text3;
			while (true)
			{
				string text2 = " " + num.ToString(CultureInfo.InvariantCulture);
				text3 = text[..Math.Min(text.Length, 32 - text2.Length)].TrimEnd(Array.Empty<char>()) + text2;
				if (!registry.HasGroup(text3))
				{
					break;
				}
				num++;
			}
			return text3;
		}
	}
	public static class RouteSummary
	{
		public sealed class Neighbour
		{
			public ContainerSettings Settings { get; }

			public double Distance { get; }

			public bool HasRoom { get; }

			public bool SameWard { get; }

			public Neighbour(ContainerSettings settings, double distance, bool hasRoom, bool sameWard)
			{
				Settings = settings;
				Distance = distance;
				HasRoom = hasRoom;
				SameWard = sameWard;
			}
		}

		public const int MaxGroupLines = 6;

		public static string Describe(ContainerSettings source, GroupRegistry groups, IEnumerable<Neighbour> neighbours, double range, bool held)
		{
			List<string> list = new List<string>();
			if (held)
			{
				list.Add("Sorting paused");
			}
			list.Add("Routes (" + Meters(range) + ")");
			List<Neighbour> source2 = neighbours.Where((Neighbour n) => n.Distance <= range && n.Settings.GroupName != null && groups.GetGroup(n.Settings.GroupName) != null).ToList();
			List<string> list2 = (from entry in (from @group in source2.GroupBy<Neighbour, string>((Neighbour n) => groups.GetGroup(n.Settings.GroupName).Name, StringComparer.OrdinalIgnoreCase)
					select DescribeGroup(@group.Key, @group.ToList()) into entry
					orderby entry.Distance
					select entry).ThenBy<(double, string), string>(((double Distance, string Text) entry) => entry.Text, StringComparer.Ordinal)
				select entry.Text).ToList();
			if (list2.Count == 0)
			{
				list.Add("No group chests in range");
			}
			else
			{
				list.AddRange(list2.Take(6));
				if (list2.Count > 6)
				{
					list.Add($"+{list2.Count - 6} more");
				}
			}
			IEnumerable<GroupDefinition?> first = source2.Select((Neighbour n) => groups.GetGroup(n.Settings.GroupName));
			string groupName = source.GroupName;
			object second;
			if (groupName != null)
			{
				GroupDefinition groupDefinition = groups.GetGroup(groupName);
				if (groupDefinition != null)
				{
					second = new GroupDefinition[1] { groupDefinition };
					goto IL_019d;
				}
			}
			second = new GroupDefinition[0];
			goto IL_019d;
			IL_019d:
			List<string> list3 = (from @group in first.Concat<GroupDefinition>((IEnumerable<GroupDefinition>)second)
				where @group.EvictMismatching
				select @group.Name).Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string name) => name, StringComparer.Ordinal).ToList();
			if (list3.Count > 0)
			{
				list.Add("Strict: " + string.Join(", ", list3.Select((string name) => "[" + name + "]")));
			}
			return string.Join("\n", list);
		}

		private static (double Distance, string Text) DescribeGroup(string name, IReadOnlyList<Neighbour> chests)
		{
			List<Neighbour> list = chests.Where((Neighbour c) => c.SameWard).ToList();
			if (list.Count == 0)
			{
				double num = chests.Min((Neighbour c) => c.Distance);
				return (Distance: num, Text: "[" + name + "] " + Meters(num) + ", ward edge");
			}
			List<Neighbour> list2 = list.Where((Neighbour c) => c.HasRoom).ToList();
			if (list2.Count <= 0)
			{
				return (Distance: list.Min((Neighbour c) => c.Distance), Text: "[" + name + "] " + Meters(list.Min((Neighbour c) => c.Distance)) + ", full");
			}
			return (Distance: list2.Min((Neighbour c) => c.Distance), Text: "[" + name + "] " + Meters(list2.Min((Neighbour c) => c.Distance)));
		}

		private static string Meters(double meters)
		{
			return Math.Round(meters).ToString(CultureInfo.InvariantCulture) + " m";
		}
	}
	public static class RoutingMessage
	{
		public static string Describe(string itemName, int amount, bool isEviction, string? destinationGroup, double distanceMeters, bool insideWard, bool relayed)
		{
			List<string> list = new List<string> { Math.Round(distanceMeters).ToString(CultureInfo.InvariantCulture) + " m" };
			if (insideWard)
			{
				list.Add("inside ward");
			}
			if (relayed)
			{
				list.Add("via another player");
			}
			string text = ((destinationGroup == null) ? "an ungrouped chest" : ("[" + destinationGroup + "]"));
			return string.Format("{0} {1} {2} to {3} ({4})", isEviction ? "Evicted" : "Routed", amount, itemName, text, string.Join(", ", list));
		}

		public static string Combine(IReadOnlyList<string> lines, int maxLines = 4)
		{
			List<string> list = new List<string>(lines.Take(maxLines));
			if (lines.Count > maxLines)
			{
				list.Add($"and {lines.Count - maxLines} more");
			}
			return string.Join("\n", list);
		}
	}
}
namespace SmartContainerRouting.Core.Routing
{
	public static class ContainerRouter
	{
		public static ContainerRoutingCandidate? SelectRoute(ContainerSettings source, PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, GroupRegistry groups, double range)
		{
			if (IsEligibleDestination(source, groups, item.ItemType))
			{
				return null;
			}
			IReadOnlyCollection<ContainerRoutingCandidate> readOnlyCollection = (IReadOnlyCollection<ContainerRoutingCandidate>)(((object)(candidates as IReadOnlyCollection<ContainerRoutingCandidate>)) ?? ((object)candidates.ToArray()));
			ContainerRoutingCandidate containerRoutingCandidate = SelectDestination(item, readOnlyCollection, groups, range);
			if (containerRoutingCandidate != null)
			{
				return containerRoutingCandidate;
			}
			string groupName = source.GroupName;
			if (groupName != null)
			{
				GroupDefinition? groupDefinition = groups.GetGroup(groupName);
				if (groupDefinition != null && groupDefinition.EvictMismatching)
				{
					return SelectNearest(item, readOnlyCollection.Where((ContainerRoutingCandidate candidate) => candidate.Settings.GroupName == null), range) ?? SelectNearest(item, readOnlyCollection.Where(delegate(ContainerRoutingCandidate candidate)
					{
						string groupName2 = candidate.Settings.GroupName;
						if (groupName2 != null)
						{
							GroupDefinition? groupDefinition2 = groups.GetGroup(groupName2);
							if (groupDefinition2 == null)
							{
								return true;
							}
							return !groupDefinition2.EvictMismatching;
						}
						return false;
					}), range);
				}
			}
			return null;
		}

		public static ContainerRoutingCandidate? SelectDestination(PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, GroupRegistry groups, double range)
		{
			string groupName = groups.GetGroupForItem(item.ItemType);
			if (groupName == null)
			{
				return null;
			}
			return SelectNearest(item, candidates.Where((ContainerRoutingCandidate c) => IsEligible(c.Settings, groupName)), range);
		}

		private static ContainerRoutingCandidate? SelectNearest(PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, double range)
		{
			ContainerRoutingCandidate containerRoutingCandidate = null;
			double num = double.PositiveInfinity;
			foreach (ContainerRoutingCandidate candidate in candidates)
			{
				if (candidate.HasCapacityFor(item.ItemType))
				{
					double num2 = item.Position.DistanceTo(candidate.Position);
					if (!(num2 > range) && (num2 < num || (num2 == num && (containerRoutingCandidate == null || string.CompareOrdinal(candidate.Id, containerRoutingCandidate.Id) < 0))))
					{
						num = num2;
						containerRoutingCandidate = candidate;
					}
				}
			}
			return containerRoutingCandidate;
		}

		public static bool IsEligibleDestination(ContainerSettings settings, GroupRegistry groups, string itemType)
		{
			return IsEligible(settings, groups.GetGroupForItem(itemType));
		}

		private static bool IsEligible(ContainerSettings settings, string? groupName)
		{
			if (groupName != null)
			{
				return string.Equals(settings.GroupName, groupName, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}
	public sealed class ContainerRoutingCandidate
	{
		public string Id { get; }

		public Position Position { get; }

		public int FreeCapacity { get; }

		public IReadOnlyDictionary<string, int> PartialStackCapacity { get; }

		public ContainerSettings Settings { get; }

		public ContainerRoutingCandidate(string id, Position position, int freeCapacity, ContainerSettings settings, IReadOnlyDictionary<string, int>? partialStackCapacity = null)
		{
			Id = id;
			Position = position;
			FreeCapacity = freeCapacity;
			Settings = settings;
			PartialStackCapacity = partialStackCapacity ?? new Dictionary<string, int>();
		}

		public bool HasCapacityFor(string itemType)
		{
			if (FreeCapacity <= 0)
			{
				if (PartialStackCapacity.TryGetValue(itemType, out var value))
				{
					return value > 0;
				}
				return false;
			}
			return true;
		}

		public bool HasMoreCapacityThan(ContainerRoutingCandidate previous)
		{
			if (FreeCapacity > previous.FreeCapacity)
			{
				return true;
			}
			foreach (KeyValuePair<string, int> item in PartialStackCapacity)
			{
				if (item.Value > 0 && (!previous.PartialStackCapacity.TryGetValue(item.Key, out var value) || item.Value > value))
				{
					return true;
				}
			}
			return false;
		}
	}
	public sealed class ItemTypeQuantity
	{
		public string ItemType { get; }

		public int Stack { get; }

		public ItemTypeQuantity(string itemType, int stack)
		{
			ItemType = itemType;
			Stack = stack;
		}
	}
	public static class NewArrivalFilter
	{
		public static Dictionary<string, int> ComputeBudget(IReadOnlyList<ItemTypeQuantity> knownStacks, IReadOnlyList<ItemTypeQuantity> currentStacks)
		{
			Dictionary<string, int> dictionary = SumByType(currentStacks);
			foreach (KeyValuePair<string, int> item in SumByType(knownStacks))
			{
				int value;
				int num = (dictionary.TryGetValue(item.Key, out value) ? value : 0);
				dictionary[item.Key] = num - item.Value;
			}
			return dictionary;
		}

		public static IReadOnlyList<T> SelectWithinBudget<T>(Dictionary<string, int> budgetByType, IReadOnlyList<T> candidates, Func<T, ItemTypeQuantity> toStack)
		{
			List<T> list = new List<T>();
			foreach (T candidate in candidates)
			{
				ItemTypeQuantity itemTypeQuantity = toStack(candidate);
				int value;
				int num = (budgetByType.TryGetValue(itemTypeQuantity.ItemType, out value) ? value : 0);
				if (num >= itemTypeQuantity.Stack)
				{
					list.Add(candidate);
					budgetByType[itemTypeQuantity.ItemType] = num - itemTypeQuantity.Stack;
				}
			}
			return list;
		}

		public static IReadOnlyList<T> SelectGenuinelyNew<T>(IReadOnlyList<ItemTypeQuantity> knownStacks, IReadOnlyList<ItemTypeQuantity> currentStacks, IReadOnlyList<T> candidates, Func<T, ItemTypeQuantity> toStack)
		{
			return SelectWithinBudget(ComputeBudget(knownStacks, currentStacks), candidates, toStack);
		}

		private static Dictionary<string, int> SumByType(IReadOnlyList<ItemTypeQuantity> stacks)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemTypeQuantity stack in stacks)
			{
				dictionary[stack.ItemType] = (dictionary.TryGetValue(stack.ItemType, out var value) ? value : 0) + stack.Stack;
			}
			return dictionary;
		}
	}
	public sealed class PlacedItem
	{
		public string ItemType { get; }

		public Position Position { get; }

		public PlacedItem(string itemType, Position position)
		{
			ItemType = itemType;
			Position = position;
		}
	}
	public readonly struct Position : IEquatable<Position>
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public Position(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public double DistanceTo(Position other)
		{
			float num = X - other.X;
			float num2 = Y - other.Y;
			float num3 = Z - other.Z;
			return Math.Sqrt(num * num + num2 * num2 + num3 * num3);
		}

		public bool Equals(Position other)
		{
			if (X.Equals(other.X) && Y.Equals(other.Y))
			{
				return Z.Equals(other.Z);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is Position other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((17 * 31 + X.GetHashCode()) * 31 + Y.GetHashCode()) * 31 + Z.GetHashCode();
		}
	}
	public sealed class RelayLedger<TPayload>
	{
		public sealed class PendingRequest
		{
			public long RequestId { get; }

			public string DestinationId { get; }

			public TPayload Payload { get; }

			public double SentAt { get; internal set; }

			public int Attempts { get; internal set; }

			internal PendingRequest(long requestId, string destinationId, TPayload payload, double sentAt)
			{
				RequestId = requestId;
				DestinationId = destinationId;
				Payload = payload;
				SentAt = sentAt;
				Attempts = 1;
			}
		}

		public sealed class DueRequests
		{
			public IReadOnlyList<PendingRequest> Resend { get; }

			public IReadOnlyList<PendingRequest> GiveUp { get; }

			internal DueRequests(IReadOnlyList<PendingRequest> resend, IReadOnlyList<PendingRequest> giveUp)
			{
				Resend = resend;
				GiveUp = giveUp;
			}
		}

		private readonly double _resendAfter;

		private readonly int _maxAttempts;

		private readonly double _cooldown;

		private readonly Dictionary<long, PendingRequest> _pending = new Dictionary<long, PendingRequest>();

		private readonly Dictionary<string, double> _cooldownUntil = new Dictionary<string, double>(StringComparer.Ordinal);

		private readonly Random _random = new Random();

		public int PendingCount => _pending.Count;

		public IEnumerable<PendingRequest> Pending => _pending.Values;

		public RelayLedger(double resendAfterSeconds, int maxAttempts, double cooldownSeconds)
		{
			_resendAfter = resendAfterSeconds;
			_maxAttempts = maxAttempts;
			_cooldown = cooldownSeconds;
		}

		public long Start(string destinationId, TPayload payload, double now)
		{
			long num;
			do
			{
				byte[] array = new byte[8];
				_random.NextBytes(array);
				num = BitConverter.ToInt64(array, 0);
			}
			while (num == 0L || _pending.ContainsKey(num));
			_pending[num] = new PendingRequest(num, destinationId, payload, now);
			return num;
		}

		public bool TryComplete(long requestId, out PendingRequest request)
		{
			if (_pending.TryGetValue(requestId, out request))
			{
				_pending.Remove(requestId);
				return true;
			}
			return false;
		}

		public DueRequests Tick(double now)
		{
			List<PendingRequest> list = new List<PendingRequest>();
			List<PendingRequest> list2 = new List<PendingRequest>();
			foreach (PendingRequest value in _pending.Values)
			{
				if (!(now - value.SentAt < _resendAfter))
				{
					if (value.Attempts >= _maxAttempts)
					{
						list2.Add(value);
						continue;
					}
					value.Attempts++;
					value.SentAt = now;
					list.Add(value);
				}
			}
			foreach (PendingRequest item in list2)
			{
				_pending.Remove(item.RequestId);
				MarkRejected(item.DestinationId, now);
			}
			return new DueRequests(list, list2);
		}

		public void MarkRejected(string destinationId, double now)
		{
			_cooldownUntil[destinationId] = now + _cooldown;
		}

		public bool IsCoolingDown(string destinationId, double now)
		{
			if (_cooldownUntil.TryGetValue(destinationId, out var value))
			{
				return now < value;
			}
			return false;
		}
	}
	public static class RelayReceipts
	{
		public const int DefaultCapacity = 64;

		public static bool TryFind(string? stored, long sender, long requestId, out int added)
		{
			foreach (var item in Parse(stored))
			{
				if (item.Sender == sender && item.RequestId == requestId)
				{
					added = item.Added;
					return true;
				}
			}
			added = 0;
			return false;
		}

		public static string Add(string? stored, long sender, long requestId, int added, int capacity = 64)
		{
			List<(long, long, int)> list = Parse(stored).ToList();
			list.Add((sender, requestId, added));
			return string.Join(";", from entry in list.Skip(Math.Max(0, list.Count - capacity))
				select string.Join(":", entry.Sender.ToString(CultureInfo.InvariantCulture), entry.RequestId.ToString(CultureInfo.InvariantCulture), entry.Added.ToString(CultureInfo.InvariantCulture)));
		}

		private static IEnumerable<(long Sender, long RequestId, int Added)> Parse(string? stored)
		{
			if (string.IsNullOrEmpty(stored))
			{
				yield break;
			}
			string[] array = stored.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				if (array2.Length == 3 && long.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && long.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3))
				{
					yield return (Sender: result, RequestId: result2, Added: result3);
				}
			}
		}
	}
	public static class WardWall
	{
		public static bool CanRoute(IEnumerable<string> sourceWardIds, IEnumerable<string> destinationWardIds)
		{
			return new HashSet<string>(sourceWardIds, StringComparer.Ordinal).SetEquals(destinationWardIds);
		}
	}
}
namespace SmartContainerRouting.Core.Persistence
{
	public static class ContainerSettingsCodec
	{
		public static ContainerSettings Decode(Func<string, string> getString, Func<string, bool> getBool, GroupRegistry? groups = null)
		{
			string name = getString("SCR_Name");
			string groupName = GroupReference.Resolve(getString("SCR_GroupId"), getString("SCR_Group"), groups);
			bool evictNonGroupItems = getBool("SCR_EvictNonGroup");
			return new ContainerSettings(name, groupName, evictNonGroupItems);
		}

		public static void Encode(ContainerSettings settings, Action<string, string> setString, Action<string, bool> setBool, GroupRegistry? groups = null, string? knownGroupId = null, Func<string, string>? getExisting = null)
		{
			string text = GroupReference.IdFor(settings.GroupName, groups);
			if (text.Length == 0 && settings.GroupName != null && !string.IsNullOrEmpty(knownGroupId))
			{
				text = knownGroupId;
			}
			if (text.Length == 0 && groups == null && settings.GroupName != null && getExisting != null && GroupNameNormalizer.Normalize(getExisting("SCR_Group")) == settings.GroupName)
			{
				text = getExisting("SCR_GroupId") ?? string.Empty;
			}
			setString("SCR_Name", settings.Name ?? string.Empty);
			setString("SCR_Group", settings.GroupName ?? string.Empty);
			setString("SCR_GroupId", text);
			setBool("SCR_EvictNonGroup", settings.EvictNonGroupItems);
		}
	}
	public sealed class GroupRegistryCache
	{
		private readonly Func<string?, GroupRegistry> _decode;

		private bool _hasValue;

		private string? _source;

		private GroupRegistry _registry = GroupRegistry.Empty;

		public GroupRegistryCache(Func<string?, GroupRegistry> decode)
		{
			_decode = decode ?? throw new ArgumentNullException("decode");
		}

		public GroupRegistry Get(string? source)
		{
			if (!_hasValue || !string.Equals(_source, source, StringComparison.Ordinal))
			{
				_source = source;
				_registry = _decode(source);
				_hasValue = true;
			}
			return _registry;
		}

		public void Set(string? source, GroupRegistry registry)
		{
			_source = source;
			_registry = registry;
			_hasValue = true;
		}

		public bool TryGet(string? source, out GroupRegistry registry)
		{
			if (source == null)
			{
				registry = GroupRegistry.Empty;
				return false;
			}
			registry = Get(source);
			return true;
		}
	}
	public static class GroupRegistryCodec
	{
		private sealed class GroupDto
		{
			public string? Id { get; set; }

			public string? Name { get; set; } = string.Empty;

			public long CreatorId { get; set; }

			public bool EvictMismatching { get; set; }

			public List<string?>? ItemTypes { get; set; } = new List<string>();
		}

		public static string Serialize(GroupRegistry registry)
		{
			StringBuilder stringBuilder = new StringBuilder("[");
			bool flag = true;
			foreach (GroupDefinition item in registry.Groups.OrderBy<GroupDefinition, string>((GroupDefinition group) => group.Name, StringComparer.Ordinal))
			{
				stringBuilder.Append(flag ? "{" : ",{");
				Json.WriteMember(stringBuilder, "id", item.Id, first: true);
				Json.WriteMember(stringBuilder, "name", item.Name);
				Json.WriteMember(stringBuilder, "creatorId", item.CreatorId);
				Json.WriteMember(stringBuilder, "evictMismatching", item.EvictMismatching);
				Json.WriteMember(stringBuilder, "itemTypes", item.ItemTypes.OrderBy<string, string>((string item) => item, StringComparer.Ordinal));
				stringBuilder.Append('}');
				flag = false;
			}
			return stringBuilder.Append(']').ToString();
		}

		public static GroupRegistry Deserialize(string? json)
		{
			TryDeserialize(json, out GroupRegistry registry);
			return registry;
		}

		public static bool TryDeserialize(string? json, out GroupRegistry registry)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				registry = GroupRegistry.Empty;
				return true;
			}
			List<GroupDto> list;
			try
			{
				list = ReadGroups(Json.Parse(json));
			}
			catch (FormatException)
			{
				registry = GroupRegistry.Empty;
				return false;
			}
			GroupRegistry groupRegistry = GroupRegistry.Empty;
			foreach (GroupDto item in list)
			{
				if (item == null)
				{
					continue;
				}
				string text = DisplayNameSanitizer.Sanitize(item.Name);
				if (text == null)
				{
					continue;
				}
				groupRegistry = groupRegistry.WithGroup(text, item.CreatorId, item.Id);
				if (!groupRegistry.HasGroup(text))
				{
					continue;
				}
				if (item.EvictMismatching)
				{
					groupRegistry = groupRegistry.WithEvictMismatching(text, value: true);
				}
				foreach (string item2 in item.ItemTypes ?? new List<string>())
				{
					if (!string.IsNullOrWhiteSpace(item2))
					{
						groupRegistry = groupRegistry.WithItemAssignedToGroup(item2, text);
					}
				}
			}
			registry = groupRegistry;
			return true;
		}

		private static List<GroupDto?> ReadGroups(Json.JsonValue root)
		{
			if (root.Kind == Json.JsonKind.Null)
			{
				return new List<GroupDto>();
			}
			if (root.Kind != Json.JsonKind.Array)
			{
				throw new FormatException("Expected an array of groups.");
			}
			List<GroupDto> list = new List<GroupDto>(root.Items.Count);
			foreach (Json.JsonValue item in root.Items)
			{
				if (item.Kind == Json.JsonKind.Null)
				{
					list.Add(null);
					continue;
				}
				if (item.Kind != Json.JsonKind.Object)
				{
					throw new FormatException("Expected a group object.");
				}
				GroupDto groupDto = new GroupDto();
				foreach (KeyValuePair<string, Json.JsonValue> member in item.Members)
				{
					switch (member.Key)
					{
					case "id":
						groupDto.Id = member.Value.AsNullableString();
						break;
					case "name":
						groupDto.Name = member.Value.AsNullableString();
						break;
					case "creatorId":
						groupDto.CreatorId = member.Value.AsLong();
						break;
					case "evictMismatching":
						groupDto.EvictMismatching = member.Value.AsBool();
						break;
					case "itemTypes":
						groupDto.ItemTypes = member.Value.AsNullableStringList();
						break;
					}
				}
				list.Add(groupDto);
			}
			return list;
		}
	}
	public static class GroupRegistryMutationCodec
	{
		private sealed class MutationDto
		{
			public string? Kind { get; set; }

			public string? GroupName { get; set; }

			public string? NewGroupName { get; set; }

			public string? ItemType { get; set; }

			public List<string>? ItemTypes { get; set; }

			public long PlayerId { get; set; }

			public bool EvictMismatching { get; set; }

			public string? GroupId { get; set; }
		}

		public static string Serialize(GroupRegistryMutation mutation)
		{
			StringBuilder stringBuilder = new StringBuilder("{");
			Json.WriteMember(stringBuilder, "kind", mutation.Kind.ToString(), first: true);
			Json.WriteMember(stringBuilder, "groupName", mutation.GroupName);
			Json.WriteMember(stringBuilder, "newGroupName", mutation.NewGroupName);
			Json.WriteMember(stringBuilder, "itemType", mutation.ItemType);
			Json.WriteMember(stringBuilder, "itemTypes", mutation.ItemTypes);
			Json.WriteMember(stringBuilder, "playerId", mutation.PlayerId);
			Json.WriteMember(stringBuilder, "evictMismatching", mutation.EvictMismatching);
			Json.WriteMember(stringBuilder, "groupId", mutation.GroupId);
			return stringBuilder.Append('}').ToString();
		}

		public static GroupRegistryMutation? Deserialize(string? json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return null;
			}
			MutationDto mutationDto;
			try
			{
				mutationDto = ReadMutation(Json.Parse(json));
			}
			catch (FormatException)
			{
				return null;
			}
			if (mutationDto?.Kind == null || !Enum.TryParse<GroupRegistryMutationKind>(mutationDto.Kind, out var result))
			{
				return null;
			}
			switch (result)
			{
			case GroupRegistryMutationKind.CreateGroup:
			{
				string groupName5 = mutationDto.GroupName;
				if (groupName5 != null)
				{
					return GroupRegistryMutation.CreateGroup(groupName5, mutationDto.PlayerId, mutationDto.GroupId);
				}
				break;
			}
			case GroupRegistryMutationKind.DeleteGroup:
			{
				string groupName4 = mutationDto.GroupName;
				if (groupName4 != null)
				{
					return GroupRegistryMutation.DeleteGroup(groupName4, mutationDto.PlayerId);
				}
				break;
			}
			case GroupRegistryMutationKind.RenameGroup:
			{
				string groupName6 = mutationDto.GroupName;
				if (groupName6 != null)
				{
					string newGroupName = mutationDto.NewGroupName;
					if (newGroupName != null)
					{
						return GroupRegistryMutation.RenameGroup(groupName6, newGroupName, mutationDto.PlayerId);
					}
				}
				break;
			}
			case GroupRegistryMutationKind.AssignItem:
			{
				string itemType = mutationDto.ItemType;
				if (itemType != null)
				{
					string groupName2 = mutationDto.GroupName;
					if (groupName2 != null)
					{
						return GroupRegistryMutation.AssignItem(itemType, groupName2);
					}
				}
				break;
			}
			case GroupRegistryMutationKind.UnassignItem:
			{
				string itemType2 = mutationDto.ItemType;
				if (itemType2 != null)
				{
					return GroupRegistryMutation.UnassignItem(itemType2);
				}
				break;
			}
			case GroupRegistryMutationKind.ReplaceGroupItems:
			{
				string groupName3 = mutationDto.GroupName;
				if (groupName3 != null)
				{
					List<string> itemTypes = mutationDto.ItemTypes;
					if (itemTypes != null)
					{
						return GroupRegistryMutation.ReplaceGroupItems(groupName3, itemTypes, mutationDto.PlayerId);
					}
				}
				break;
			}
			case GroupRegistryMutationKind.SetGroupEviction:
			{
				string groupName = mutationDto.GroupName;
				if (groupName != null)
				{
					return GroupRegistryMutation.SetGroupEviction(groupName, mutationDto.EvictMismatching, mutationDto.PlayerId);
				}
				break;
			}
			}
			return null;
		}

		private static MutationDto? ReadMutation(Json.JsonValue root)
		{
			if (root.Kind == Json.JsonKind.Null)
			{
				return null;
			}
			if (root.Kind != Json.JsonKind.Object)
			{
				throw new FormatException("Expected a mutation object.");
			}
			MutationDto mutationDto = new MutationDto();
			foreach (KeyValuePair<string, Json.JsonValue> member in root.Members)
			{
				switch (member.Key)
				{
				case "kind":
					mutationDto.Kind = member.Value.AsNullableString();
					break;
				case "groupName":
					mutationDto.GroupName = member.Value.AsNullableString();
					break;
				case "newGroupName":
					mutationDto.NewGroupName = member.Value.AsNullableString();
					break;
				case "itemType":
					mutationDto.ItemType = member.Value.AsNullableString();
					break;
				case "itemTypes":
					mutationDto.ItemTypes = member.Value.AsNullableStringList();
					break;
				case "playerId":
					mutationDto.PlayerId = member.Value.AsLong();
					break;
				case "evictMismatching":
					mutationDto.EvictMismatching = member.Value.AsBool();
					break;
				case "groupId":
					mutationDto.GroupId = member.Value.AsNullableString();
					break;
				}
			}
			return mutationDto;
		}
	}
	internal static class Json
	{
		public enum JsonKind
		{
			Null,
			True,
			False,
			Number,
			String,
			Array,
			Object
		}

		internal sealed class JsonValue
		{
			private readonly string? _text;

			private readonly bool _hasLoneSurrogate;

			public static readonly JsonValue Null = new JsonValue(JsonKind.Null);

			public static readonly JsonValue True = new JsonValue(JsonKind.True);

			public static readonly JsonValue False = new JsonValue(JsonKind.False);

			public JsonKind Kind { get; }

			public List<JsonValue> Items { get; } = new List<JsonValue>();

			public List<KeyValuePair<string, JsonValue>> Members { get; } = new List<KeyValuePair<string, JsonValue>>();

			private JsonValue(JsonKind kind, string? text = null, bool hasLoneSurrogate = false)
			{
				Kind = kind;
				_text = text;
				_hasLoneSurrogate = hasLoneSurrogate;
			}

			public static JsonValue Number(string text)
			{
				return new JsonValue(JsonKind.Number, text);
			}

			public static JsonValue String(string text, bool hasLoneSurrogate)
			{
				return new JsonValue(JsonKind.String, text, hasLoneSurrogate);
			}

			public static JsonValue Array()
			{
				return new JsonValue(JsonKind.Array);
			}

			public static JsonValue Object()
			{
				return new JsonValue(JsonKind.Object);
			}

			public string? AsNullableString()
			{
				if (Kind == JsonKind.Null)
				{
					return null;
				}
				if (Kind != JsonKind.String || _hasLoneSurrogate)
				{
					throw new FormatException("Expected a string.");
				}
				return _text;
			}

			public long AsLong()
			{
				if (Kind != JsonKind.Number || _text.IndexOfAny(new char[3] { '.', 'e', 'E' }) >= 0 || !long.TryParse(_text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result))
				{
					throw new FormatException("Expected an integer.");
				}
				return result;
			}

			public bool AsBool()
			{
				return Kind switch
				{
					JsonKind.True => true, 
					JsonKind.False => false, 
					_ => throw new FormatException("Expected true or false."), 
				};
			}

			public List<string?>? AsNullableStringList()
			{
				if (Kind == JsonKind.Null)
				{
					return null;
				}
				if (Kind != JsonKind.Array)
				{
					throw new FormatException("Expected an array.");
				}
				List<string> list = new List<string>(Items.Count);
				foreach (JsonValue item in Items)
				{
					list.Add(item.AsNullableString());
				}
				return list;
			}
		}

		private sealed class Parser
		{
			private readonly string _text;

			private int _position;

			private int _depth;

			public Parser(string text)
			{
				_text = text;
			}

			public JsonValue ParseDocument()
			{
				SkipWhitespace();
				JsonValue result = ParseValue();
				SkipWhitespace();
				if (_position != _text.Length)
				{
					throw Error("Unexpected content after the JSON value.");
				}
				return result;
			}

			private JsonValue ParseValue()
			{
				if (_position >= _text.Length)
				{
					throw Error("Unexpected end of input.");
				}
				switch (_text[_position])
				{
				case '{':
					return ParseObject();
				case '[':
					return ParseArray();
				case '"':
					return ParseString();
				case 't':
					Expect("true");
					return JsonValue.True;
				case 'f':
					Expect("false");
					return JsonValue.False;
				case 'n':
					Expect("null");
					return JsonValue.Null;
				default:
					return ParseNumber();
				}
			}

			private JsonValue ParseObject()
			{
				Enter();
				_position++;
				JsonValue jsonValue = JsonValue.Object();
				SkipWhitespace();
				if (TryConsume('}'))
				{
					_depth--;
					return jsonValue;
				}
				do
				{
					SkipWhitespace();
					if (_position >= _text.Length || _text[_position] != '"')
					{
						throw Error("Expected a property name.");
					}
					string key = ParseString().AsNullableString();
					SkipWhitespace();
					if (!TryConsume(':'))
					{
						throw Error("Expected ':'.");
					}
					SkipWhitespace();
					jsonValue.Members.Add(new KeyValuePair<string, JsonValue>(key, ParseValue()));
					SkipWhitespace();
					if (TryConsume('}'))
					{
						_depth--;
						return jsonValue;
					}
				}
				while (TryConsume(','));
				throw Error("Expected ',' or '}'.");
			}

			private JsonValue ParseArray()
			{
				Enter();
				_position++;
				JsonValue jsonValue = JsonValue.Array();
				SkipWhitespace();
				if (TryConsume(']'))
				{
					_depth--;
					return jsonValue;
				}
				do
				{
					SkipWhitespace();
					jsonValue.Items.Add(ParseValue());
					SkipWhitespace();
					if (TryConsume(']'))
					{
						_depth--;
						return jsonValue;
					}
				}
				while (TryConsume(','));
				throw Error("Expected ',' or ']'.");
			}

			private JsonValue ParseString()
			{
				_position++;
				StringBuilder stringBuilder = new StringBuilder();
				while (true)
				{
					if (_position >= _text.Length)
					{
						throw Error("Unterminated string.");
					}
					char c = _text[_position++];
					if (c == '"')
					{
						break;
					}
					if (c < ' ')
					{
						throw Error("Control character in string.");
					}
					if (c != '\\')
					{
						stringBuilder.Append(c);
						continue;
					}
					if (_position >= _text.Length)
					{
						throw Error("Unterminated escape.");
					}
					switch (_text[_position++])
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
					{
						if (_position + 4 > _text.Length || !ushort.TryParse(_text.Substring(_position, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result))
						{
							throw Error("Invalid \\u escape.");
						}
						stringBuilder.Append((char)result);
						_position += 4;
						break;
					}
					default:
						throw Error("Invalid escape.");
					}
				}
				string text = stringBuilder.ToString();
				return JsonValue.String(text, HasLoneSurrogate(text));
			}

			private JsonValue ParseNumber()
			{
				int position = _position;
				TryConsume('-');
				if (!TryConsume('0') && !ConsumeDigits())
				{
					throw Error("Invalid value.");
				}
				if (TryConsume('.') && !ConsumeDigits())
				{
					throw Error("Invalid number.");
				}
				if (_position < _text.Length && (_text[_position] == 'e' || _text[_position] == 'E'))
				{
					_position++;
					if (!TryConsume('+'))
					{
						TryConsume('-');
					}
					if (!ConsumeDigits())
					{
						throw Error("Invalid number.");
					}
				}
				return JsonValue.Number(_text.Substring(position, _position - position));
			}

			private bool ConsumeDigits()
			{
				int position = _position;
				while (_position < _text.Length && _text[_position] >= '0' && _text[_position] <= '9')
				{
					_position++;
				}
				return _position > position;
			}

			private void Expect(string literal)
			{
				if (string.CompareOrdinal(_text, _position, literal, 0, literal.Length) != 0)
				{
					throw Error("Invalid literal.");
				}
				_position += literal.Length;
			}

			private void Enter()
			{
				if (++_depth > 64)
				{
					throw Error("Nested too deeply.");
				}
			}

			private bool TryConsume(char expected)
			{
				if (_position < _text.Length && _text[_position] == expected)
				{
					_position++;
					return true;
				}
				return false;
			}

			private void SkipWhitespace()
			{
				while (true)
				{
					bool flag = _position < _text.Length;
					if (flag)
					{
						bool flag2;
						switch (_text[_position])
						{
						case '\t':
						case '\n':
						case '\r':
						case ' ':
							flag2 = true;
							break;
						default:
							flag2 = false;
							break;
						}
						flag = flag2;
					}
					if (flag)
					{
						_position++;
						continue;
					}
					break;
				}
			}

			private FormatException Error(string message)
			{
				return new FormatException($"{message} (at {_position})");
			}

			private static bool HasLoneSurrogate(string value)
			{
				for (int i = 0; i < value.Length; i++)
				{
					if (char.IsHighSurrogate(value[i]) && i + 1 < value.Length && char.IsLowSurrogate(value[i + 1]))
					{
						i++;
					}
					else if (char.IsSurrogate(value[i]))
					{
						return true;
					}
				}
				return false;
			}
		}

		private const int MaxDepth = 64;

		public static void WriteString(StringBuilder output, string? value)
		{
			if (value == null)
			{
				output.Append("null");
				return;
			}
			output.Append('"');
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				switch (c)
				{
				case '\\':
					output.Append("\\\\");
					continue;
				case '\b':
					output.Append("\\b");
					continue;
				case '\f':
					output.Append("\\f");
					continue;
				case '\n':
					output.Append("\\n");
					continue;
				case '\r':
					output.Append("\\r");
					continue;
				case '\t':
					output.Append("\\t");
					continue;
				}
				if (char.IsHighSurrogate(c) && i + 1 < value.Length && char.IsLowSurrogate(value[i + 1]))
				{
					AppendEscaped(output, c);
					AppendEscaped(output, value[++i]);
				}
				else if (char.IsSurrogate(c))
				{
					AppendEscaped(output, '\ufffd');
				}
				else if (c < ' ' || c > '~' || c == '"' || c == '&' || c == '\'' || c == '+' || c == '<' || c == '>' || c == '`')
				{
					AppendEscaped(output, c);
				}
				else
				{
					output.Append(c);
				}
			}
			output.Append('"');
		}

		public static void WriteMember(StringBuilder output, string name, string? value, bool first = false)
		{
			AppendName(output, name, first);
			WriteString(output, value);
		}

		public static void WriteMember(StringBuilder output, string name, long value)
		{
			AppendName(output, name, first: false);
			output.Append(value.ToString(CultureInfo.InvariantCulture));
		}

		public static void WriteMember(StringBuilder output, string name, bool value)
		{
			AppendName(output, name, first: false);
			output.Append(value ? "true" : "false");
		}

		public static void WriteMember(StringBuilder output, string name, IEnumerable<string?>? values)
		{
			AppendName(output, name, first: false);
			if (values == null)
			{
				output.Append("null");
				return;
			}
			output.Append('[');
			bool flag = true;
			foreach (string value in values)
			{
				if (!flag)
				{
					output.Append(',');
				}
				WriteString(output, value);
				flag = false;
			}
			output.Append(']');
		}

		private static void AppendName(StringBuilder output, string name, bool first)
		{
			if (!first)
			{
				output.Append(',');
			}
			output.Append('"').Append(name).Append("\":");
		}

		private static void AppendEscaped(StringBuilder output, char c)
		{
			StringBuilder stringBuilder = output.Append("\\u");
			int num = c;
			stringBuilder.Append(num.ToString("X4", CultureInfo.InvariantCulture));
		}

		public static JsonValue Parse(string text)
		{
			return new Parser(text).ParseDocument();
		}
	}
	public static class PersistenceKeys
	{
		public const string ContainerName = "SCR_Name";

		public const string ContainerGroup = "SCR_Group";

		public const string ContainerGroupId = "SCR_GroupId";

		public const string ContainerEvictNonGroupItems = "SCR_EvictNonGroup";

		public const string ContainerRelayReceipts = "SCR_RelayReceipts";

		public const string GroupRegistryBlob = "SCR_Groups";

		public const string GroupRegistryCorruptBackup = "SCR_GroupsCorruptBackup";
	}
}
namespace SmartContainerRouting.Core.Model
{
	public sealed class ContainerSettings
	{
		public string? Name { get; }

		public string? GroupName { get; }

		public bool EvictNonGroupItems { get; }

		public bool IsEvicting
		{
			get
			{
				if (EvictNonGroupItems)
				{
					return GroupName != null;
				}
				return false;
			}
		}

		public static ContainerSettings Default { get; } = new ContainerSettings();

		public ContainerSettings(string? name = null, string? groupName = null, bool evictNonGroupItems = false)
		{
			Name = NormalizeOrNull(name);
			GroupName = GroupNameNormalizer.Normalize(groupName);
			EvictNonGroupItems = evictNonGroupItems;
		}

		public ContainerSettings WithName(string? name)
		{
			return new ContainerSettings(name, GroupName, EvictNonGroupItems);
		}

		public ContainerSettings WithGroupName(string? groupName)
		{
			return new ContainerSettings(Name, groupName, EvictNonGroupItems);
		}

		public ContainerSettings WithEvictNonGroupItems(bool value)
		{
			return new ContainerSettings(Name, GroupName, value);
		}

		public bool Equals(ContainerSettings? other)
		{
			if (other != null && string.Equals(Name, other.Name, StringComparison.Ordinal) && string.Equals(GroupName, other.GroupName, StringComparison.Ordinal))
			{
				return EvictNonGroupItems == other.EvictNonGroupItems;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			return Equals(obj as ContainerSettings);
		}

		public override int GetHashCode()
		{
			return ((17 * 31 + (Name?.GetHashCode() ?? 0)) * 31 + (GroupName?.GetHashCode() ?? 0)) * 31 + EvictNonGroupItems.GetHashCode();
		}

		private static string? NormalizeOrNull(string? value)
		{
			return DisplayNameSanitizer.Sanitize(value);
		}
	}
	public sealed class GroupDefinition
	{
		public string Name { get; }

		public string Id { get; }

		public ItemTypeSet ItemTypes { get; }

		public bool EvictMismatching { get; }

		public long CreatorId { get; }

		private GroupDefinition(string id, string name, ItemTypeSet itemTypes, long creatorId, bool evictMismatching = false)
		{
			Id = id;
			Name = name;
			ItemTypes = itemTypes;
			CreatorId = creatorId;
			EvictMismatching = evictMismatching;
		}

		public static string LegacyIdFor(string name)
		{
			return "name:" + GroupNameNormalizer.Normalize(name);
		}

		public static GroupDefinition Empty(string name, long creatorId = 0L, bool evictMismatching = false, string? id = null)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentException("Group name must not be empty.", "name");
			}
			return new GroupDefinition(string.IsNullOrWhiteSpace(id) ? LegacyIdFor(name) : id, name, ItemTypeSet.Empty, creatorId, evictMismatching);
		}

		internal GroupDefinition WithItem(string itemType)
		{
			return new GroupDefinition(Id, Name, ItemTypes.Add(itemType), CreatorId, EvictMismatching);
		}

		internal GroupDefinition WithoutItem(string itemType)
		{
			return new GroupDefinition(Id, Name, ItemTypes.Remove(itemType), CreatorId, EvictMismatching);
		}

		internal GroupDefinition WithItems(ItemTypeSet itemTypes)
		{
			return new GroupDefinition(Id, Name, itemTypes, CreatorId, EvictMismatching);
		}

		internal GroupDefinition WithEvictMismatching(bool value)
		{
			return new GroupDefinition(Id, Name, ItemTypes, CreatorId, value);
		}
	}
	public static class GroupItemAdditions
	{
		public static IReadOnlyList<string> Missing(GroupRegistry registry, string groupName, IEnumerable<string> chestItemTypes)
		{
			GroupDefinition group = registry.GetGroup(groupName);
			if (group == null)
			{
				return Array.Empty<string>();
			}
			return chestItemTypes.Where((string itemType) => !string.IsNullOrEmpty(itemType) && !group.ItemTypes.Contains(itemType)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string itemType) => itemType, StringComparer.Ordinal)
				.ToList();
		}
	}
	public static class GroupItemTransfers
	{
		public static string? Describe(GroupRegistry registry, string targetGroupName, IEnumerable<string> itemTypes, Func<string, string> itemDisplayName)
		{
			string targetName = registry.GetGroup(targetGroupName)?.Name ?? targetGroupName;
			string[] array = (from @group in (from itemType in itemTypes.Distinct<string>(StringComparer.Ordinal)
					select (itemType: itemType, owner: registry.GetGroupForItem(itemType)) into pair
					where pair.owner != null && !string.Equals(pair.owner, targetName, StringComparison.OrdinalIgnoreCase)
					select pair).GroupBy<(string, string), string>(((string itemType, string owner) pair) => pair.owner, StringComparer.Ordinal).OrderBy<IGrouping<string, (string, string)>, string>((IGrouping<string, (string itemType, string owner)> @group) => @group.Key, StringComparer.Ordinal)
				select string.Join(", ", @group.Select(((string itemType, string owner) pair) => itemDisplayName(pair.itemType)).OrderBy<string, string>((string name) => name, StringComparer.Ordinal)) + " moved from " + @group.Key + " to " + targetName).ToArray();
			if (array.Length != 0)
			{
				return string.Join("\n", array);
			}
			return null;
		}
	}
	public static class GroupReference
	{
		public static string? Resolve(string? storedId, string? storedName, GroupRegistry? registry)
		{
			string text = GroupNameNormalizer.Normalize(storedName);
			if (registry == null)
			{
				return text;
			}
			GroupDefinition groupDefinition = ((!string.IsNullOrEmpty(storedId)) ? registry.GetGroupById(storedId) : ((text == null) ? null : (registry.GetGroupById(GroupDefinition.LegacyIdFor(text)) ?? registry.GetGroup(text))));
			if (groupDefinition != null)
			{
				return GroupNameNormalizer.Normalize(groupDefinition.Name);
			}
			return text;
		}

		public static string IdFor(string? groupName, GroupRegistry? registry)
		{
			object obj;
			if (groupName != null)
			{
				obj = registry?.GetGroup(groupName)?.Id;
				if (obj == null)
				{
					return string.Empty;
				}
			}
			else
			{
				obj = string.Empty;
			}
			return (string)obj;
		}
	}
	public sealed class GroupRegistry
	{
		private readonly Dictionary<string, GroupDefinition> _groups;

		public static GroupRegistry Empty { get; } = new GroupRegistry(new Dictionary<string, GroupDefinition>());

		public IReadOnlyCollection<GroupDefinition> Groups => (IReadOnlyCollection<GroupDefinition>)(object)_groups.Values.ToArray();

		private GroupRegistry(Dictionary<string, GroupDefinition> groups)
		{
			_groups = groups;
		}

		public bool HasGroup(string groupName)
		{
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text != null)
			{
				return _groups.ContainsKey(text);
			}
			return false;
		}

		public GroupDefinition? GetGroup(string groupName)
		{
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text == null || !_groups.TryGetValue(text, out GroupDefinition value))
			{
				return null;
			}
			return value;
		}

		public GroupDefinition? GetGroupById(string? groupId)
		{
			if (!string.IsNullOrEmpty(groupId))
			{
				return _groups.Values.FirstOrDefault((GroupDefinition group) => string.Equals(group.Id, groupId, StringComparison.Ordinal));
			}
			return null;
		}

		public GroupRegistry WithGroup(string groupName, long creatorId = 0L, string? groupId = null)
		{
			string? obj = DisplayNameSanitizer.Sanitize(groupName) ?? throw new ArgumentException("Group name must not be empty.", "groupName");
			string key = GroupNameNormalizer.Normalize(obj);
			GroupDefinition groupDefinition = GroupDefinition.Empty(obj, creatorId, evictMismatching: false, groupId);
			if (!_groups.ContainsKey(key) && GetGroupById(groupDefinition.Id) == null)
			{
				return new GroupRegistry(With(_groups, key, groupDefinition));
			}
			return this;
		}

		public GroupRegistry WithoutGroup(string groupName)
		{
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text == null)
			{
				return this;
			}
			if (!_groups.ContainsKey(text))
			{
				return this;
			}
			return new GroupRegistry(Without(_groups, text));
		}

		public GroupRegistry WithRenamedGroup(string oldName, string newName)
		{
			string text = DisplayNameSanitizer.Sanitize(newName);
			if (text == null)
			{
				throw new ArgumentException("Group name must not be empty.", "newName");
			}
			string text2 = GroupNameNormalizer.Normalize(oldName);
			if (!_groups.TryGetValue(text2, out GroupDefinition value))
			{
				throw new ArgumentException("Group '" + oldName + "' does not exist.", "oldName");
			}
			if (string.Equals(value.Name, text, StringComparison.Ordinal))
			{
				return this;
			}
			string text3 = GroupNameNormalizer.Normalize(text);
			if (text2 != text3 && _groups.ContainsKey(text3))
			{
				throw new ArgumentException("Group '" + text + "' already exists.", "newName");
			}
			GroupDefinition groupDefinition = GroupDefinition.Empty(text, value.CreatorId, value.EvictMismatching, value.Id).WithItems(value.ItemTypes);
			if (!(text2 == text3))
			{
				return new GroupRegistry(With(Without(_groups, text2), text3, groupDefinition));
			}
			return new GroupRegistry(With(_groups, text2, groupDefinition));
		}

		public GroupRegistry WithItemAssignedToGroup(string itemType, string groupName)
		{
			if (string.IsNullOrWhiteSpace(itemType))
			{
				throw new ArgumentException("Item type must not be empty.", "itemType");
			}
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text == null || !_groups.TryGetValue(text, out GroupDefinition _))
			{
				throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName");
			}
			Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups);
			foreach (KeyValuePair<string, GroupDefinition> group in _groups)
			{
				if (group.Key != text && group.Value.ItemTypes.Contains(itemType))
				{
					dictionary[group.Key] = group.Value.WithoutItem(itemType);
				}
			}
			dictionary[text] = dictionary[text].WithItem(itemType);
			return new GroupRegistry(dictionary);
		}

		public GroupRegistry WithItemUnassigned(string itemType)
		{
			Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups);
			foreach (KeyValuePair<string, GroupDefinition> group in _groups)
			{
				if (group.Value.ItemTypes.Contains(itemType))
				{
					dictionary[group.Key] = group.Value.WithoutItem(itemType);
				}
			}
			return new GroupRegistry(dictionary);
		}

		public GroupRegistry WithGroupItemsReplaced(string groupName, IEnumerable<string> itemTypes)
		{
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text == null || !_groups.TryGetValue(text, out GroupDefinition value))
			{
				throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName");
			}
			HashSet<string> hashSet = new HashSet<string>(itemTypes, StringComparer.Ordinal);
			if (hashSet.Any(string.IsNullOrWhiteSpace))
			{
				throw new ArgumentException("Item types must not be empty.", "itemTypes");
			}
			Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups);
			foreach (KeyValuePair<string, GroupDefinition> group in _groups)
			{
				if (!(group.Key != text))
				{
					continue;
				}
				string[] array = group.Value.ItemTypes.Intersect(hashSet).ToArray();
				if (array.Length != 0)
				{
					GroupDefinition groupDefinition = group.Value;
					string[] array2 = array;
					foreach (string itemType in array2)
					{
						groupDefinition = groupDefinition.WithoutItem(itemType);
					}
					dictionary[group.Key] = groupDefinition;
				}
			}
			dictionary[text] = GroupDefinition.Empty(value.Name, value.CreatorId, value.EvictMismatching, value.Id).WithItems(ItemTypeSet.From(hashSet));
			return new GroupRegistry(dictionary);
		}

		public GroupRegistry WithEvictMismatching(string groupName, bool value)
		{
			string text = GroupNameNormalizer.Normalize(groupName);
			if (text == null || !_groups.TryGetValue(text, out GroupDefinition value2))
			{
				throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName");
			}
			if (value2.EvictMismatching != value)
			{
				return new GroupRegistry(With(_groups, text, value2.WithEvictMismatching(value)));
			}
			return this;
		}

		public string? GetGroupForItem(string itemType)
		{
			return _groups.Values.FirstOrDefault((GroupDefinition group) => group.ItemTypes.Contains(itemType))?.Name;
		}

		private static Dictionary<string, GroupDefinition> With(Dictionary<string, GroupDefinition> groups, string key, GroupDefinition group)
		{
			return new Dictionary<string, GroupDefinition>(groups) { [key] = group };
		}

		private static Dictionary<string, GroupDefinition> Without(Dictionary<string, GroupDefinition> groups, string key)
		{
			Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(groups);
			dictionary.Remove(key);
			return dictionary;
		}
	}
	public enum GroupRegistryMutationKind
	{
		CreateGroup,
		DeleteGroup,
		RenameGroup,
		AssignItem,
		UnassignItem,
		ReplaceGroupItems,
		SetGroupEviction
	}
	public sealed class GroupRegistryMutation
	{
		public GroupRegistryMutationKind Kind { get; }

		public string? GroupName { get; }

		public string? NewGroupName { get; }

		public string? ItemType { get; }

		public IReadOnlyList<string>? ItemTypes { get; }

		public bool EvictMismatching { get; }

		public string? GroupId { get; }

		public long PlayerId { get; }

		private GroupRegistryMutation(GroupRegistryMutationKind kind, string? groupName, string? newGroupName, string? itemType, long playerId, IReadOnlyList<string>? itemTypes = null, bool evictMismatching = false, string? groupId = null)
		{
			GroupId = groupId;
			Kind = kind;
			GroupName = groupName;
			NewGroupName = newGroupName;
			ItemType = itemType;
			ItemTypes = itemTypes;
			EvictMismatching = evictMismatching;
			PlayerId = playerId;
		}

		public static GroupRegistryMutation CreateGroup(string groupName, long creatorId, string? groupId = null)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.CreateGroup, groupName, null, null, creatorId, null, evictMismatching: false, groupId);
		}

		public static GroupRegistryMutation DeleteGroup(string groupName, long requesterId)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.DeleteGroup, groupName, null, null, requesterId);
		}

		public static GroupRegistryMutation RenameGroup(string oldGroupName, string newGroupName, long requesterId)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.RenameGroup, oldGroupName, newGroupName, null, requesterId);
		}

		public static GroupRegistryMutation AssignItem(string itemType, string groupName)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.AssignItem, groupName, null, itemType, 0L);
		}

		public static GroupRegistryMutation UnassignItem(string itemType)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.UnassignItem, null, null, itemType, 0L);
		}

		public static GroupRegistryMutation ReplaceGroupItems(string groupName, IEnumerable<string> itemTypes, long requesterId)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.ReplaceGroupItems, groupName, null, null, requesterId, itemTypes.ToArray());
		}

		public static GroupRegistryMutation SetGroupEviction(string groupName, bool value, long requesterId)
		{
			return new GroupRegistryMutation(GroupRegistryMutationKind.SetGroupEviction, groupName, null, null, requesterId, null, value);
		}

		public GroupRegistryMutation WithVerifiedPlayerId(long verifiedPlayerId)
		{
			return new GroupRegistryMutation(Kind, GroupName, NewGroupName, ItemType, verifiedPlayerId, ItemTypes, EvictMismatching, GroupId);
		}

		public GroupRegistry Apply(GroupRegistry registry)
		{
			return Kind switch
			{
				GroupRegistryMutationKind.CreateGroup => registry.WithGroup(GroupName, PlayerId, GroupId), 
				GroupRegistryMutationKind.DeleteGroup => registry.WithoutGroup(GroupName), 
				GroupRegistryMutationKind.RenameGroup => registry.WithRenamedGroup(GroupName, NewGroupName), 
				GroupRegistryMutationKind.AssignItem => registry.WithItemAssignedToGroup(ItemType, GroupName), 
				GroupRegistryMutationKind.UnassignItem => registry.WithItemUnassigned(ItemType), 
				GroupRegistryMutationKind.ReplaceGroupItems => registry.WithGroupItemsReplaced(GroupName, ItemTypes), 
				GroupRegistryMutationKind.SetGroupEviction => registry.WithEvictMismatching(GroupName, EvictMismatching), 
				_ => throw new InvalidOperationException($"Unknown mutation kind '{Kind}'."), 
			};
		}
	}
	public sealed class ItemPinCandidates
	{
		public IReadOnlyList<string> InChest { get; }

		public IReadOnlyList<string> CarriedOnly { get; }

		private ItemPinCandidates(IReadOnlyList<string> inChest, IReadOnlyList<string> carriedOnly)
		{
			InChest = inChest;
			CarriedOnly = carriedOnly;
		}

		public static ItemPinCandidates Build(IEnumerable<string> chestItemTypes, IEnumerable<string> carriedItemTypes)
		{
			List<string> list = DistinctSorted(chestItemTypes);
			HashSet<string> inChestSet = new HashSet<string>(list, StringComparer.Ordinal);
			List<string> carriedOnly = (from itemType in DistinctSorted(carriedItemTypes)
				where !inChestSet.Contains(itemType)
				select itemType).ToList();
			return new ItemPinCandidates(list, carriedOnly);
		}

		private static List<string> DistinctSorted(IEnumerable<string> itemTypes)
		{
			return itemTypes.Where((string itemType) => !string.IsNullOrEmpty(itemType)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string itemType) => itemType, StringComparer.Ordinal)
				.ToList();
		}
	}
	public sealed class ItemTypeSet : IReadOnlyCollection<string>, IEnumerable<string>, IEnumerable
	{
		private readonly HashSet<string> _items;

		public static ItemTypeSet Empty { get; } = new ItemTypeSet(new HashSet<string>(StringComparer.Ordinal));

		public int Count => _items.Count;

		private ItemTypeSet(HashSet<string> items)
		{
			_items = items;
		}

		public bool Contains(string itemType)
		{
			return _items.Contains(itemType);
		}

		internal ItemTypeSet Add(string itemType)
		{
			if (_items.Contains(itemType))
			{
				return this;
			}
			return new ItemTypeSet(new HashSet<string>(_items, StringComparer.Ordinal) { itemType });
		}

		internal ItemTypeSet Remove(string itemType)
		{
			if (!_items.Contains(itemType))
			{
				return this;
			}
			HashSet<string> hashSet = new HashSet<string>(_items, StringComparer.Ordinal);
			hashSet.Remove(itemType);
			return new ItemTypeSet(hashSet);
		}

		internal static ItemTypeSet From(IEnumerable<string> itemTypes)
		{
			return new ItemTypeSet(new HashSet<string>(itemTypes, StringComparer.Ordinal));
		}

		public IEnumerator<string> GetEnumerator()
		{
			return _items.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}

plugins/StorageGroups.dll

Decompiled 16 hours 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.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SmartContainerRouting.Core.Model;
using SmartContainerRouting.Core.Persistence;
using SmartContainerRouting.Core.Routing;
using SmartContainerRouting.Core.Text;
using SmartContainerRouting.Plugin.Persistence;
using SmartContainerRouting.Plugin.Routing;
using SmartContainerRouting.Plugin.Ui;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("jonasd")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f556d01ba578811190b24467d0afe74ca948dcb8")]
[assembly: AssemblyProduct("Storage Groups")]
[assembly: AssemblyTitle("StorageGroups")]
[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.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]
	[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]
	[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 SmartContainerRouting.Plugin
{
	[BepInPlugin("com.jonasd.valheim.storagegroups", "Storage Groups", "0.1.7")]
	[BepInIncompatibility("flueno.SmartContainers")]
	public sealed class SmartContainerRoutingPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.jonasd.valheim.storagegroups";

		public const string PluginName = "Storage Groups";

		public const string PluginVersion = "0.1.7";

		private const string SmartContainersGuid = "flueno.SmartContainers";

		private Harmony? _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("com.jonasd.valheim.storagegroups");
			_harmony.PatchAll();
			if (Localization.instance != null)
			{
				LocalizationPatch.AddWord(Localization.instance, "scr_container_name", "Container name");
				LocalizationPatch.AddWord(Localization.instance, "scr_group_name", "Group name");
			}
			RoutingConfig.RangeMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Routing", "RangeMeters", 10f, "Maximum distance, in meters, between a container that just received an item and an eligible destination container for the item to route there.").Value;
			RoutingConfig.ShowRoutingMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowRoutingMessages", false, "Show a short message for every item routed near you, and log the details. Also toggled from the Storage Groups menu.");
			GroupRegistryAccess.AnchorZdoProvider = ModDataAnchorProvider.GetOrCreate;
			((Component)this).gameObject.AddComponent<GroupManagementController>();
			((Component)this).gameObject.AddComponent<GroupItemsController>();
		}

		private void Update()
		{
			ModDataAnchorProvider.GetOrCreate();
			ContainerRoutingService.ObserveGroupRegistry();
			ContainerRoutingService.ProcessPendingInventoryChanges();
			RouteRelay.Tick();
			RoutingNotifier.Flush();
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace SmartContainerRouting.Plugin.Ui
{
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ContainerAwakePatch
	{
		private static void Postfix(Container __instance)
		{
			if (ContainerKindFilter.IsRoutable(__instance) && (Object)(object)((Component)__instance).GetComponent<ContainerRoutingBehaviour>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<ContainerRoutingBehaviour>();
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class ContainerHoverTextPatch
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			ContainerRoutingBehaviour component = ((Component)__instance).GetComponent<ContainerRoutingBehaviour>();
			if (!((Object)(object)component == (Object)null))
			{
				string text = ContainerHoverText.BuildExtraLine(component.GetSettings(), (Func<string, string>)GroupRegistryAccess.DisplayName, (Func<string, bool>)GroupRegistryAccess.IsEvicting, ContainerAccessGuard.GetCoveringWardIds(__instance).Count > 0);
				if (!string.IsNullOrEmpty(text))
				{
					__result = __result + "\n" + text;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Load")]
	internal static class ContainerLoadPatch
	{
		public static bool IsLoading { get; private set; }

		private static void Prefix()
		{
			IsLoading = true;
		}

		private static void Postfix(Container __instance, bool __result)
		{
			IsLoading = false;
			ContainerRoutingBehaviour component = ((Component)__instance).GetComponent<ContainerRoutingBehaviour>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			component.RefreshKnownItems();
			if (__result)
			{
				component.MarkRoutingReady();
				if (GroupRegistryAccess.TryLoad(out GroupRegistry registry))
				{
					component.ReconcileGroup(registry);
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			IsLoading = false;
			return __exception;
		}
	}
	internal sealed class ContainerPanelController : MonoBehaviour
	{
		private static ContainerPanelController? _instance;

		private const float TopInset = 8f;

		private const float Gap = 8f;

		private const float ColumnWidth = 140f;

		private const float ControlHeight = 24f;

		private const float PanelArtOverhang = 10f;

		private static readonly string[] PanelArtPaths = new string[2] { "Bkg", "selected_frame/selected (1)" };

		private static readonly List<RectTransform> ScratchRects = new List<RectTransform>();

		private static readonly Vector3[] ScratchCorners = (Vector3[])(object)new Vector3[4];

		private const int SettleFrames = 10;

		private RectTransform? _row;

		private TMP_Text? _groupButtonLabel;

		private Button? _newGroupButton;

		private Button? _addButton;

		private Button? _editItemsButton;

		private Toggle? _pauseToggle;

		private InventoryGui? _gui;

		private ContainerRoutingBehaviour? _current;

		private ContainerSettings? _lastRefreshedSettings;

		private string? _lastRefreshedGroupDisplayName;

		private bool _layoutDirty;

		private int _framesSinceShown;

		public static ContainerRoutingBehaviour? Current
		{
			get
			{
				if (!((Object)(object)_instance != (Object)null))
				{
					return null;
				}
				return _instance._current;
			}
		}

		public static void ShowFor(InventoryGui gui, Container container)
		{
			if (!ContainerKindFilter.IsRoutable(container))
			{
				_instance?.Hide();
				return;
			}
			ContainerRoutingBehaviour containerRoutingBehaviour = ((Component)container).GetComponent<ContainerRoutingBehaviour>();
			if ((Object)(object)containerRoutingBehaviour == (Object)null)
			{
				containerRoutingBehaviour = ((Component)container).gameObject.AddComponent<ContainerRoutingBehaviour>();
			}
			ContainerPanelController orCreate = GetOrCreate(gui);
			if (orCreate._current != containerRoutingBehaviour)
			{
				orCreate.ReleaseHold();
			}
			orCreate._gui = gui;
			orCreate._current = containerRoutingBehaviour;
			orCreate._framesSinceShown = 0;
			RectTransform? row = orCreate._row;
			if (row != null)
			{
				((Component)row).gameObject.SetActive(true);
			}
			orCreate.Refresh();
			orCreate.PositionRow();
		}

		private void Hide()
		{
			ReleaseHold();
			_current = null;
			RectTransform? row = _row;
			if (row != null)
			{
				((Component)row).gameObject.SetActive(false);
			}
		}

		private static ContainerPanelController GetOrCreate(InventoryGui gui)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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)
			if ((Object)(object)_instance != (Object)null)
			{
				return _instance;
			}
			GameObject val = new GameObject("SCR_ContainerControls", new Type[3]
			{
				typeof(RectTransform),
				typeof(VerticalLayoutGroup),
				typeof(ContentSizeFitter)
			});
			val.transform.SetParent((Transform)(object)gui.m_container, false);
			ContentSizeFitter component = val.GetComponent<ContentSizeFitter>();
			component.horizontalFit = (FitMode)0;
			component.verticalFit = (FitMode)2;
			RectTransform component2 = val.GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(1f, 1f);
			component2.anchorMax = new Vector2(1f, 1f);
			component2.pivot = new Vector2(0f, 1f);
			component2.sizeDelta = new Vector2(140f, component2.sizeDelta.y);
			_instance = val.AddComponent<ContainerPanelController>();
			_instance._row = component2;
			_instance.Build(val.GetComponent<VerticalLayoutGroup>());
			return _instance;
		}

		private void Build(VerticalLayoutGroup layout)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			((HorizontalOrVerticalLayoutGroup)layout).spacing = 4f;
			((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)layout).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)layout).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)layout).childForceExpandHeight = false;
			((LayoutGroup)layout).childAlignment = (TextAnchor)0;
			Button val = UiFactory.CreateButton(((Component)this).transform, string.Empty, (UnityAction)delegate
			{
				GroupManagementController.Toggle(_current);
			});
			_groupButtonLabel = ((Component)val).GetComponentInChildren<TMP_Text>();
			((Component)val).gameObject.AddComponent<HoverTooltip>().Initialize(DescribeGroupButton);
			_newGroupButton = UiFactory.CreateButton(((Component)this).transform, "New Group From Contents", new UnityAction(OnNewGroupClicked));
			((Component)_newGroupButton).gameObject.SetActive(false);
			((Component)_newGroupButton).gameObject.AddComponent<HoverTooltip>().Initialize(delegate
			{
				ContainerRoutingBehaviour? current = _current;
				if (current != null)
				{
					Inventory? inventory = current.GetInventory();
					if (((inventory != null) ? new int?(inventory.GetAllItems().Count) : ((int?)null)) > 0)
					{
						return "Makes a group from this chest's items\nand puts this chest in it.";
					}
				}
				return "Put items in the chest first.";
			});
			_addButton = UiFactory.CreateButton(((Component)this).transform, "Add Contents", new UnityAction(OnAddContentsClicked));
			((Component)_addButton).gameObject.SetActive(false);
			((Component)_addButton).gameObject.AddComponent<GroupContentsTooltip>().Initialize(delegate
			{
				ContainerRoutingBehaviour? current = _current;
				return (current == null) ? null : current.GetSettings().GroupName;
			}, null, "Adds this chest's items to the group");
			_editItemsButton = UiFactory.CreateButton(((Component)this).transform, "Edit Items...", (UnityAction)delegate
			{
				GroupItemsController.Toggle(_current);
			});
			((Component)_editItemsButton).gameObject.SetActive(false);
			_pauseToggle = UiFactory.CreateToggle(((Component)this).transform, "Pause sorting", initialValue: false, delegate(bool value)
			{
				_current?.SetHeld(value);
			});
			((Component)_pauseToggle).gameObject.AddComponent<HoverTooltip>().Initialize(() => "Nothing moves in or out\nof this chest while ticked.\nTurns off when you close it.");
			LayoutElement[] componentsInChildren = ((Component)this).GetComponentsInChildren<LayoutElement>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				componentsInChildren[num].preferredHeight = 24f;
			}
		}

		private void PositionRow()
		{
			//IL_0051: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_row == (Object)null || (Object)(object)_gui == (Object)null)
			{
				return;
			}
			RectTransform container = _gui.m_container;
			if (container == null)
			{
				return;
			}
			if (_layoutDirty || _framesSinceShown <= 10)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(_row);
				_layoutDirty = false;
			}
			Vector3 lossyScale = ((Transform)container).lossyScale;
			if (lossyScale.x <= 0f || lossyScale.y <= 0f)
			{
				return;
			}
			(float left, float right, float bottom, float top) tuple = WorldBounds(container, includeChildren: false);
			float item = tuple.right;
			float item2 = tuple.top;
			float num = 8f * lossyScale.x;
			float num2 = 8f * lossyScale.y;
			float num3 = Mathf.Max(Mathf.Max(Mathf.Max(PanelArtRightEdge(container, item, lossyScale.x), TitleRightEdge(_gui.m_containerName)), RightEdge((Component?)(object)_gui.m_takeAllButton)), RightEdge((Component?)(object)_gui.m_stackAllButton)) + num;
			float num4 = item2 - 8f * lossyScale.y;
			Rect rect = _row.rect;
			float num5 = num4 - ((Rect)(ref rect)).height * lossyScale.y;
			float num6 = num3 + 140f * lossyScale.x;
			if ((Object)(object)_gui.m_containerWeight != (Object)null && ((Component)_gui.m_containerWeight).gameObject.activeInHierarchy)
			{
				Transform parent = _gui.m_containerWeight.transform.parent;
				RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				var (num7, num8, _, num9) = WorldBounds(((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)container) ? val : _gui.m_containerWeight.rectTransform, includeChildren: true);
				if (num3 < num8 && num7 < num6 && num5 < num9 + num2)
				{
					num3 = Mathf.Max(num3, num8 + num);
				}
			}
			_row.anchoredPosition = new Vector2((num3 - item) / lossyScale.x, -8f);
		}

		private static float PanelArtRightEdge(RectTransform panel, float panelRight, float scaleX)
		{
			float num = panelRight + 10f * scaleX;
			string[] panelArtPaths = PanelArtPaths;
			foreach (string text in panelArtPaths)
			{
				num = Mathf.Max(num, RightEdge((Component?)(object)((Transform)panel).Find(text)));
			}
			return num;
		}

		private static float RightEdge(Component? element)
		{
			if ((Object)(object)element != (Object)null && element.gameObject.activeInHierarchy)
			{
				Transform transform = element.transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val != null)
				{
					return WorldBounds(val, includeChildren: true).right;
				}
			}
			return float.NegativeInfinity;
		}

		private static float TitleRightEdge(TMP_Text? title)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)title == (Object)null || !((Component)title).gameObject.activeInHierarchy)
			{
				return float.NegativeInfinity;
			}
			float item = WorldBounds(title.rectTransform, includeChildren: false).right;
			Bounds textBounds = title.textBounds;
			if (!(((Bounds)(ref textBounds)).size.x > 0f))
			{
				return item;
			}
			return Mathf.Max(item, title.transform.TransformPoint(((Bounds)(ref textBounds)).max).x);
		}

		private static (float left, float right, float bottom, float top) WorldBounds(RectTransform rect, bool includeChildren)
		{
			rect.GetWorldCorners(ScratchCorners);
			float num = ScratchCorners[0].x;
			float num2 = ScratchCorners[0].y;
			float num3 = ScratchCorners[2].x;
			float num4 = ScratchCorners[2].y;
			if (!includeChildren)
			{
				return (left: num, right: num3, bottom: num2, top: num4);
			}
			((Component)rect).GetComponentsInChildren<RectTransform>(false, ScratchRects);
			foreach (RectTransform scratchRect in ScratchRects)
			{
				scratchRect.GetWorldCorners(ScratchCorners);
				num = Mathf.Min(num, ScratchCorners[0].x);
				num2 = Mathf.Min(num2, ScratchCorners[0].y);
				num3 = Mathf.Max(num3, ScratchCorners[2].x);
				num4 = Mathf.Max(num4, ScratchCorners[2].y);
			}
			ScratchRects.Clear();
			return (left: num, right: num3, bottom: num2, top: num4);
		}

		private void OnDisable()
		{
			ReleaseHold();
		}

		private void ReleaseHold()
		{
			_current?.SetHeld(held: false);
			Toggle? pauseToggle = _pauseToggle;
			if (pauseToggle != null)
			{
				pauseToggle.SetIsOnWithoutNotify(false);
			}
		}

		private string? DescribeGroupButton()
		{
			ContainerRoutingBehaviour? current = _current;
			string text = ((current != null) ? current.GetSettings().GroupName : null);
			if (text != null && GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				GroupDefinition val = registry.GetGroup(text);
				if (val != null)
				{
					string text2 = GroupButtonHover.Describe(val.Name, ((IEnumerable<string>)val.ItemTypes).Select(ItemTypeResolver.GetDisplayName));
					string text3 = DescribeRoutes();
					return text2 + ((text3 != null) ? ("\n\n" + text3) : string.Empty);
				}
			}
			string text4 = GroupButtonHover.Describe((string)null, Enumerable.Empty<string>());
			string text5 = DescribeRoutes();
			return text4 + ((text5 != null) ? ("\n\n" + text5) : string.Empty);
		}

		private string? DescribeRoutes()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			ContainerRoutingBehaviour current = _current;
			if ((Object)(object)current == (Object)null || !current.IsValid)
			{
				return null;
			}
			if (!GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				return "Groups not loaded yet";
			}
			Position position = current.GetPosition();
			List<Neighbour> list = new List<Neighbour>();
			foreach (ContainerRoutingBehaviour item in ContainerRegistry.All)
			{
				if (!((Object)(object)item == (Object)null) && item != current && item.IsValid && item.IsEligibleRoutingDestination && !item.IsHeld)
				{
					ContainerRoutingCandidate val = item.ToCandidate();
					list.Add(new Neighbour(val.Settings, ((Position)(ref position)).DistanceTo(val.Position), val.FreeCapacity > 0 || val.PartialStackCapacity.Values.Any((int space) => space > 0), current.CanRouteTo(item)));
				}
			}
			return RouteSummary.Describe(current.GetSettings(), registry, (IEnumerable<Neighbour>)list, RoutingConfig.RangeMeters, current.IsHeld);
		}

		private void OnNewGroupClicked()
		{
			ContainerRoutingBehaviour current = _current;
			if ((Object)(object)current == (Object)null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				return;
			}
			Inventory? inventory = current.GetInventory();
			List<ItemData> source = ((inventory != null) ? inventory.GetAllItems() : null) ?? new List<ItemData>();
			string text = GroupNameSuggestion.Suggest(source.Select((ItemData item) => new KeyValuePair<string, int>(ItemTypeResolver.GetDisplayName(ItemTypeResolver.GetItemType(item)), item.m_stack)), registry);
			if (text == null)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "Put items in the chest first", 0, (Sprite)null, false);
				}
			}
			else
			{
				IReadOnlyList<string> inChest = ItemPinCandidates.Build(source.Select(ItemTypeResolver.GetItemType), Enumerable.Empty<string>()).InChest;
				GroupManagementController.NewGroupFromContents(current, text, inChest);
			}
		}

		private static void SetShown(Button? button, bool shown, bool interactable)
		{
			if (!((Object)(object)button == (Object)null))
			{
				if (((Component)button).gameObject.activeSelf != shown)
				{
					((Component)button).gameObject.SetActive(shown);
				}
				((Selectable)button).interactable = interactable;
			}
		}

		private void OnAddContentsClicked()
		{
			ContainerRoutingBehaviour current = _current;
			string text = ((current != null) ? current.GetSettings().GroupName : null);
			if ((Object)(object)current == (Object)null || text == null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				return;
			}
			Inventory? inventory = current.GetInventory();
			IEnumerable<string> enumerable = ((inventory != null) ? inventory.GetAllItems().Select(ItemTypeResolver.GetItemType) : null) ?? Enumerable.Empty<string>();
			IReadOnlyList<string> readOnlyList = GroupItemAdditions.Missing(registry, text, enumerable);
			if (readOnlyList.Count == 0)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "Everything in this chest is already in the group", 0, (Sprite)null, false);
				}
				return;
			}
			string text2 = GroupItemTransfers.Describe(registry, text, (IEnumerable<string>)readOnlyList, (Func<string, string>)ItemTypeResolver.GetDisplayName);
			foreach (string item in readOnlyList)
			{
				GroupRegistryRpc.Request(GroupRegistryMutation.AssignItem(item, text));
			}
			Button? addButton = _addButton;
			if (addButton != null)
			{
				((Component)addButton).GetComponent<GroupContentsTooltip>()?.Refresh();
			}
			if (text2 != null)
			{
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					((Character)localPlayer2).Message((MessageType)2, text2, 0, (Sprite)null, false);
				}
			}
		}

		private void Refresh()
		{
			if ((Object)(object)_current == (Object)null)
			{
				return;
			}
			ContainerSettings settings = _current.GetSettings();
			string text = GroupRegistryAccess.DisplayName(settings.GroupName);
			bool flag = settings.GroupName != null;
			GroupRegistry registry;
			bool interactable = flag && (Object)(object)Player.m_localPlayer != (Object)null && GroupRegistryAccess.TryLoad(out registry) && registry.HasGroup(settings.GroupName);
			Button? newGroupButton = _newGroupButton;
			bool shown = !flag;
			int interactable2;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Inventory? inventory = _current.GetInventory();
				if (inventory != null && inventory.GetAllItems().Count > 0)
				{
					interactable2 = (GroupRegistryAccess.TryLoad(out GroupRegistry _) ? 1 : 0);
					goto IL_00a0;
				}
			}
			interactable2 = 0;
			goto IL_00a0;
			IL_00a0:
			SetShown(newGroupButton, shown, (byte)interactable2 != 0);
			SetShown(_addButton, flag, interactable);
			SetShown(_editItemsButton, flag, interactable);
			if (!settings.Equals(_lastRefreshedSettings) || !(text == _lastRefreshedGroupDisplayName))
			{
				_lastRefreshedSettings = settings;
				_lastRefreshedGroupDisplayName = text;
				_layoutDirty = true;
				if ((Object)(object)_groupButtonLabel != (Object)null)
				{
					_groupButtonLabel.text = "Group: " + (text ?? "(none)");
				}
			}
		}

		private void Update()
		{
			Refresh();
			_framesSinceShown++;
			PositionRow();
		}
	}
	public sealed class ContainerRoutingBehaviour : MonoBehaviour
	{
		private sealed class ReferenceItemComparer : IEqualityComparer<ItemData>
		{
			public static readonly ReferenceItemComparer Instance = new ReferenceItemComparer();

			public bool Equals(ItemData x, ItemData y)
			{
				return x == y;
			}

			public int GetHashCode(ItemData obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		private static readonly MethodInfo ContainerLoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);

		private ZNetView? _nview;

		private Container? _container;

		private List<ItemData> _knownItems = new List<ItemData>();

		private List<ItemTypeQuantity> _knownStacks = new List<ItemTypeQuantity>();

		private ContainerRoutingCandidate? _knownCapacity;

		private bool _wasOwner;

		private Dictionary<ItemData, int> _knownStackSizes = new Dictionary<ItemData, int>(ReferenceItemComparer.Instance);

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_nview != (Object)null)
				{
					return _nview.IsValid();
				}
				return false;
			}
		}

		public bool IsOwner
		{
			get
			{
				if ((Object)(object)_nview != (Object)null)
				{
					return _nview.IsOwner();
				}
				return false;
			}
		}

		public bool IsEligibleRoutingDestination
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return ContainerAccessGuard.IsEligibleRoutingDestination(_container);
				}
				return false;
			}
		}

		public bool IsRoutingReady { get; private set; }

		public bool IsHeld { get; private set; }

		public string RoutingId
		{
			get
			{
				if (!IsValid)
				{
					return ((Object)this).GetInstanceID().ToString();
				}
				return ((object)Unsafe.As<ZDOID, ZDOID>(ref _nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString();
			}
		}

		public ZDOID? ZdoId
		{
			get
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				if (!IsValid)
				{
					return null;
				}
				return _nview.GetZDO().m_uid;
			}
		}

		private void Awake()
		{
			_container = ((Component)this).GetComponent<Container>();
			_nview = (((Object)(object)_container != (Object)null && (Object)(object)_container.m_rootObjectOverride != (Object)null) ? _container.m_rootObjectOverride : ((Component)this).GetComponent<ZNetView>());
			_wasOwner = IsOwner;
			ContainerRegistry.Register(this);
			if ((Object)(object)_nview != (Object)null && _nview.IsValid())
			{
				RouteRelay.RegisterChest(_nview, this);
			}
			Inventory inventory = GetInventory();
			if (inventory != null)
			{
				SnapshotKnownItems(inventory.GetAllItems());
				_knownCapacity = ToCandidate();
				inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged));
			}
		}

		private void OnDestroy()
		{
			ContainerRegistry.Unregister(this);
			Inventory inventory = GetInventory();
			if (inventory != null)
			{
				inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(OnInventoryChanged));
			}
		}

		internal void RefreshIfOwnershipJustClaimed()
		{
			bool isOwner = IsOwner;
			if (isOwner && !_wasOwner && (Object)(object)_container != (Object)null)
			{
				ContainerLoadMethod.Invoke(_container, null);
			}
			_wasOwner = isOwner;
		}

		public List<string> CoveringWardIds()
		{
			if (!((Object)(object)_container != (Object)null))
			{
				return new List<string>();
			}
			return ContainerAccessGuard.GetCoveringWardIds(_container);
		}

		public bool CanRouteTo(ContainerRoutingBehaviour destination)
		{
			if ((Object)(object)_container != (Object)null && (Object)(object)destination._container != (Object)null)
			{
				return ContainerAccessGuard.CanRouteBetween(_container, destination._container);
			}
			return false;
		}

		public void SetHeld(bool held)
		{
			if (IsHeld != held)
			{
				IsHeld = held;
				if (!held)
				{
					ContainerRoutingService.QueueReroute(this);
					ContainerRoutingService.QueueCapacityAvailable(this);
				}
			}
		}

		internal void MarkRoutingReady()
		{
			IsRoutingReady = true;
		}

		public Inventory? GetInventory()
		{
			if (!((Object)(object)_container != (Object)null))
			{
				return null;
			}
			return _container.GetInventory();
		}

		public Position GetPosition()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (IsValid ? _nview.GetZDO().GetPosition() : ((Component)this).transform.position);
			return new Position(val.x, val.y, val.z);
		}

		public ContainerRoutingCandidate ToCandidate()
		{
			//IL_0084: 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_00a2: Expected O, but got Unknown
			Inventory inventory = GetInventory();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			if (inventory != null)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					int num = allItem.m_shared.m_maxStackSize - allItem.m_stack;
					if (num > 0)
					{
						string itemType = ItemTypeResolver.GetItemType(allItem);
						dictionary[itemType] = (dictionary.TryGetValue(itemType, out var value) ? (value + num) : num);
					}
				}
			}
			return new ContainerRoutingCandidate(RoutingId, GetPosition(), (inventory != null) ? inventory.GetEmptySlots() : 0, GetSettings(), (IReadOnlyDictionary<string, int>)dictionary);
		}

		public void RefreshKnownItems()
		{
			Inventory inventory = GetInventory();
			SnapshotKnownItems((inventory != null) ? inventory.GetAllItems() : new List<ItemData>());
			_knownCapacity = ToCandidate();
		}

		private void SnapshotKnownItems(List<ItemData> items)
		{
			_knownItems = new List<ItemData>(items);
			_knownStacks = items.Select(ToStack).ToList();
			_knownStackSizes = new Dictionary<ItemData, int>(ReferenceItemComparer.Instance);
			foreach (ItemData item in items)
			{
				_knownStackSizes[item] = item.m_stack;
			}
		}

		private void OnInventoryChanged()
		{
			try
			{
				if (!ContainerRoutingService.IsRouting && !ContainerLoadPatch.IsLoading)
				{
					ContainerRoutingService.QueueInventoryChanged(this);
				}
				HandleInventoryChanged();
			}
			catch (Exception arg)
			{
				SmartContainerRoutingPlugin.Log.LogError((object)$"Inventory-change routing hook threw and was suppressed to avoid corrupting the in-flight inventory operation: {arg}");
			}
		}

		private void HandleInventoryChanged()
		{
			if (ContainerRoutingService.IsRouting || ContainerLoadPatch.IsLoading)
			{
				return;
			}
			Inventory inventory = GetInventory();
			if (inventory == null)
			{
				return;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			ContainerRoutingCandidate val = ToCandidate();
			bool flag = _knownCapacity != null && val.HasMoreCapacityThan(_knownCapacity);
			List<ItemData> list = allItems.Where((ItemData item2) => !_knownItems.Contains(item2)).ToList();
			List<ItemTypeQuantity> knownStacks = _knownStacks;
			List<ItemTypeQuantity> list2 = allItems.Select(ToStack).ToList();
			List<(ItemData, int)> list3 = new List<(ItemData, int)>();
			foreach (ItemData item2 in allItems)
			{
				if (_knownStackSizes.TryGetValue(item2, out var value) && item2.m_stack > value)
				{
					list3.Add((item2, item2.m_stack - value));
				}
			}
			SnapshotKnownItems(allItems);
			_knownCapacity = val;
			Dictionary<string, int> dictionary = NewArrivalFilter.ComputeBudget((IReadOnlyList<ItemTypeQuantity>)knownStacks, (IReadOnlyList<ItemTypeQuantity>)list2);
			IReadOnlyList<ItemData> readOnlyList = NewArrivalFilter.SelectWithinBudget<ItemData>(dictionary, (IReadOnlyList<ItemData>)list, (Func<ItemData, ItemTypeQuantity>)ToStack);
			IReadOnlyList<(ItemData, int)> readOnlyList2 = NewArrivalFilter.SelectWithinBudget<(ItemData, int)>(dictionary, (IReadOnlyList<(ItemData, int)>)list3, (Func<(ItemData, int), ItemTypeQuantity>)(((ItemData Item, int Delta) growth) => new ItemTypeQuantity(ItemTypeResolver.GetItemType(growth.Item), growth.Delta)));
			foreach (ItemData item3 in readOnlyList)
			{
				ContainerRoutingService.TryRoute(this, item3, item3.m_stack);
			}
			foreach (var (item, amount) in readOnlyList2)
			{
				ContainerRoutingService.TryRoute(this, item, amount);
			}
			if (flag)
			{
				ContainerRoutingService.QueueCapacityAvailable(this);
			}
		}

		private static ItemTypeQuantity ToStack(ItemData item)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			return new ItemTypeQuantity(ItemTypeResolver.GetItemType(item), item.m_stack);
		}

		public ContainerSettings GetSettings()
		{
			if (!IsValid)
			{
				return ContainerSettings.Default;
			}
			return ContainerZdoStore.Load(_nview.GetZDO());
		}

		public void SaveSettings(ContainerSettings settings, string? knownGroupId = null)
		{
			if (ContainerOwnershipGuard.CanWrite(_nview))
			{
				string groupName = GetSettings().GroupName;
				ContainerZdoStore.Save(_nview.GetZDO(), settings, knownGroupId);
				if (groupName != settings.GroupName)
				{
					ContainerRoutingService.QueueReroute(this);
					ContainerRoutingService.QueueCapacityAvailable(this);
				}
			}
			else if (IsValid)
			{
				GroupRegistry registry;
				string text = GroupReference.IdFor(settings.GroupName, GroupRegistryAccess.TryLoad(out registry) ? registry : null);
				RouteRelay.RequestSettings(this, settings, (text.Length > 0) ? text : (knownGroupId ?? string.Empty));
			}
		}

		public void SetGroup(string? groupName, string? knownGroupId = null)
		{
			SaveSettings(GetSettings().WithGroupName(groupName), knownGroupId);
		}

		public void ReconcileGroup(GroupRegistry groups)
		{
			if (IsOwner)
			{
				string groupName = GetSettings().GroupName;
				if (groupName != null && !groups.HasGroup(groupName))
				{
					SetGroup(null);
				}
				else if (groupName != null && ContainerZdoStore.LacksGroupId(_nview.GetZDO()))
				{
					SetGroup(groupName);
				}
			}
		}
	}
	internal sealed class DelegateTextReceiver : TextReceiver
	{
		private readonly Func<string> _getText;

		private readonly Action<string> _setText;

		public DelegateTextReceiver(Func<string> getText, Action<string> setText)
		{
			_getText = getText;
			_setText = setText;
		}

		public string GetText()
		{
			return _getText();
		}

		public void SetText(string text)
		{
			_setText(text);
		}
	}
	internal sealed class GroupContentsTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		private const int VisibleItemLimit = 8;

		private const float RegistryCheckIntervalSeconds = 0.1f;

		private bool _hovered;

		private string? _lastRegistryBlob;

		private float _nextRegistryCheck;

		private Func<string?>? _groupName;

		private Transform? _displayParent;

		private GameObject? _panel;

		private string? _actionHint;

		public void Initialize(Func<string?> groupName, Transform? displayParent = null, string? actionHint = null)
		{
			_groupName = groupName;
			_displayParent = displayParent;
			_actionHint = actionHint;
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			_hovered = true;
			Refresh();
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			_hovered = false;
			Hide();
		}

		private void OnDisable()
		{
			_hovered = false;
			Hide();
		}

		private void Update()
		{
			if (_hovered && !(Time.unscaledTime < _nextRegistryCheck))
			{
				_nextRegistryCheck = Time.unscaledTime + 0.1f;
				if (GroupRegistryAccess.CurrentBlob() != _lastRegistryBlob)
				{
					Refresh();
				}
			}
		}

		public void Refresh()
		{
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			Hide();
			_lastRegistryBlob = GroupRegistryAccess.CurrentBlob();
			string text = _groupName?.Invoke();
			if (text == null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				return;
			}
			GroupDefinition val = registry.GetGroup(text);
			if (val == null)
			{
				return;
			}
			string[] array = ((IEnumerable<string>)val.ItemTypes).OrderBy<string, string>((string item) => item, StringComparer.Ordinal).ToArray();
			string[] array2 = array.Take(8).ToArray();
			int num = array.Length - array2.Length;
			string[] array3 = ((array2.Length != 0) ? array2 : new string[1] { "(empty)" });
			string text2 = val.Name + "\n" + ((_actionHint == null) ? string.Empty : (_actionHint + "\n")) + array.Length + " current item types:\n" + string.Join("\n", array3);
			if (num > 0)
			{
				text2 = text2 + "\n+" + num + " more";
			}
			int num2 = array3.Length + 2 + ((_actionHint != null) ? 1 : 0) + ((num > 0) ? 1 : 0);
			float num3 = 20f + (float)num2 * 19f;
			_panel = UiFactory.CreatePanel(_displayParent ?? ((Component)this).transform, "SCR_GroupContentsTooltip", new Vector2(220f, num3));
			RectTransform component = _panel.GetComponent<RectTransform>();
			component.pivot = new Vector2(1f, 1f);
			if ((Object)(object)_displayParent == (Object)null)
			{
				component.anchorMin = new Vector2(0f, 1f);
				component.anchorMax = new Vector2(0f, 1f);
				component.anchoredPosition = new Vector2(-8f, 0f);
			}
			else
			{
				Transform transform = ((Component)this).transform;
				RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val2 != null)
				{
					Rect rect = val2.rect;
					float num4 = ((Rect)(ref rect)).xMin - 8f;
					rect = val2.rect;
					((Transform)component).position = ((Transform)val2).TransformPoint(new Vector3(num4, ((Rect)(ref rect)).yMax, 0f));
				}
			}
			((Graphic)_panel.GetComponent<Image>()).raycastTarget = false;
			TMP_Text obj = UiFactory.CreateText(_panel.transform, text2);
			obj.alignment = (TextAlignmentOptions)257;
			obj.fontSize = 14f;
			obj.textWrappingMode = (TextWrappingModes)0;
			obj.overflowMode = (TextOverflowModes)1;
			((Graphic)obj).raycastTarget = false;
			((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = num3 - 20f;
		}

		private void Hide()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
				Object.Destroy((Object)(object)_panel);
				_panel = null;
			}
		}
	}
	internal sealed class GroupEvictionTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		private const string Explanation = "Strict: only group items\n\nApplies to every chest in this group. Items that aren't in the group move to a nearby chest with room. A chest in the item's group is tried first, even if that group also evicts. Otherwise, the nearest chest that isn't strict is used. A strict chest only receives its group's items.";

		private Transform? _displayParent;

		private GameObject? _panel;

		public void Initialize(Transform? displayParent)
		{
			_displayParent = displayParent;
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			Hide();
			if ((Object)(object)_displayParent == (Object)null)
			{
				return;
			}
			Transform transform = ((Component)this).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (val != null)
			{
				_panel = UiFactory.CreatePanel(_displayParent, "SCR_GroupEvictionTooltip", new Vector2(320f, 195f));
				RectTransform component = _panel.GetComponent<RectTransform>();
				Rect rect = val.rect;
				float num = ((Rect)(ref rect)).xMax + 8f;
				rect = val.rect;
				Vector3 val2 = ((Transform)val).TransformPoint(new Vector3(num, ((Rect)(ref rect)).yMax, 0f));
				if (val2.x + 320f <= (float)Screen.width)
				{
					component.pivot = new Vector2(0f, 1f);
					((Transform)component).position = val2;
				}
				else
				{
					component.pivot = Vector2.one;
					rect = val.rect;
					float num2 = ((Rect)(ref rect)).xMin - 8f;
					rect = val.rect;
					((Transform)component).position = ((Transform)val).TransformPoint(new Vector3(num2, ((Rect)(ref rect)).yMax, 0f));
				}
				((Graphic)_panel.GetComponent<Image>()).raycastTarget = false;
				TMP_Text obj = UiFactory.CreateText(_panel.transform, "Strict: only group items\n\nApplies to every chest in this group. Items that aren't in the group move to a nearby chest with room. A chest in the item's group is tried first, even if that group also evicts. Otherwise, the nearest chest that isn't strict is used. A strict chest only receives its group's items.");
				obj.alignment = (TextAlignmentOptions)257;
				obj.fontSize = 16f;
				obj.textWrappingMode = (TextWrappingModes)1;
				obj.overflowMode = (TextOverflowModes)1;
				((Graphic)obj).raycastTarget = false;
				((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = 175f;
			}
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			Hide();
		}

		private void OnDisable()
		{
			Hide();
		}

		private void Hide()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
				Object.Destroy((Object)(object)_panel);
				_panel = null;
			}
		}
	}
	internal sealed class GroupItemsController : MonoBehaviour
	{
		private const float PanelWidth = 360f;

		private const float RowHeight = 32f;

		private const float MaxListHeight = 360f;

		private const float RegistryCheckIntervalSeconds = 0.1f;

		private const float ClearConfirmSeconds = 3f;

		private static GroupItemsController? _instance;

		private ContainerRoutingBehaviour? _source;

		private Transform? _canvasTransform;

		private GameObject? _panel;

		private string? _lastRegistryBlob;

		private float _nextRegistryCheck;

		private float _clearConfirmUntil;

		private void Awake()
		{
			_instance = this;
		}

		private void OnDestroy()
		{
			if (_instance == this)
			{
				_instance = null;
			}
		}

		public static void Toggle(ContainerRoutingBehaviour? source)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if ((Object)(object)_instance._panel != (Object)null)
				{
					_instance.Close();
				}
				else if (source != null && source.GetSettings().GroupName != null)
				{
					GroupManagementController.CloseIfOpen();
					_instance.Open(source);
				}
			}
		}

		public static void CloseIfOpen()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._panel != (Object)null)
			{
				_instance.Close();
			}
		}

		private void Update()
		{
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			if (!IsSourceChestStillOpen())
			{
				Close();
				return;
			}
			if (_clearConfirmUntil > 0f && Time.unscaledTime >= _clearConfirmUntil)
			{
				_clearConfirmUntil = 0f;
				Rebuild();
			}
			if (Time.unscaledTime >= _nextRegistryCheck)
			{
				_nextRegistryCheck = Time.unscaledTime + 0.1f;
				string text = GroupRegistryAccess.CurrentBlob();
				if (text != null && text != _lastRegistryBlob)
				{
					Rebuild();
				}
			}
		}

		private bool IsSourceChestStillOpen()
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)_source != (Object)null && InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen())
			{
				return (Object)(object)ContainerPanelController.Current == (Object)(object)_source;
			}
			return false;
		}

		private void Open(ContainerRoutingBehaviour source)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			_source = source;
			_clearConfirmUntil = 0f;
			GameObject val = new GameObject("SCR_GroupItemsCanvas", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			val.transform.SetParent(((Component)this).transform, false);
			Canvas component = val.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.overrideSorting = true;
			component.sortingOrder = SortingOrderAboveInventory();
			_canvasTransform = val.transform;
			_panel = UiFactory.CreatePanel(val.transform, "SCR_GroupItemsPanel", new Vector2(360f, 200f));
			_panel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			Rebuild();
		}

		private void Close()
		{
			if ((Object)(object)_canvasTransform != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvasTransform).gameObject);
			}
			_canvasTransform = null;
			_panel = null;
			_source = null;
			_lastRegistryBlob = null;
			_clearConfirmUntil = 0f;
		}

		private void Rebuild()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Expected O, but got Unknown
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			foreach (Transform item in _panel.transform)
			{
				((Component)item).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			_lastRegistryBlob = GroupRegistryAccess.CurrentBlob();
			ContainerRoutingBehaviour? source = _source;
			string text = ((source != null) ? source.GetSettings().GroupName : null);
			GroupRegistry val2 = GroupRegistryAccess.Load();
			if (text != null)
			{
				GroupDefinition group = val2.GetGroup(text);
				if (group != null)
				{
					List<(string, string)> list = ((IEnumerable<string>)group.ItemTypes).Select((string itemType) => (Type: itemType, Name: ItemTypeResolver.GetDisplayName(itemType))).OrderBy<(string, string), string>(((string Type, string Name) item) => item.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
					GameObject obj = UiFactory.CreateRow(_panel.transform, "Header");
					((Component)UiFactory.CreateText(obj.transform, $"{group.Name}: {list.Count} items")).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
					UiFactory.CreateButton(obj.transform, "Close", new UnityAction(Close));
					if (list.Count == 0)
					{
						UiFactory.CreateText(_panel.transform, "No items yet. Use Add Contents.");
						return;
					}
					float height = Mathf.Min(360f, (float)list.Count * 36f);
					RectTransform content = UiFactory.CreateScrollList(_panel.transform, 340f, height).content;
					foreach (var item2 in list)
					{
						BuildItemRow((Transform)(object)content, item2.Item1, item2.Item2);
					}
					GameObject obj2 = UiFactory.CreateRow(_panel.transform, "Footer");
					UiFactory.CreateButton(label: (_clearConfirmUntil > Time.unscaledTime) ? "Click again to clear all" : "Clear all", parent: obj2.transform, onClick: (UnityAction)delegate
					{
						OnClearAllClicked(group.Name);
					});
					return;
				}
			}
			Close();
		}

		private void BuildItemRow(Transform content, string itemType, string displayName)
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = UiFactory.CreateRow(content, "Row_" + itemType);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.minHeight = 32f;
			component.preferredHeight = 32f;
			Sprite icon = ItemTypeResolver.GetIcon(itemType);
			float minWidth;
			if (icon != null)
			{
				GameObject val2 = new GameObject("Icon", new Type[3]
				{
					typeof(RectTransform),
					typeof(Image),
					typeof(LayoutElement)
				});
				val2.transform.SetParent(val.transform, false);
				Image component2 = val2.GetComponent<Image>();
				component2.sprite = icon;
				component2.preserveAspect = true;
				((Graphic)component2).raycastTarget = false;
				LayoutElement component3 = val2.GetComponent<LayoutElement>();
				minWidth = (component3.preferredWidth = 28f);
				component3.minWidth = minWidth;
				minWidth = (component3.preferredHeight = 28f);
				component3.minHeight = minWidth;
			}
			((Component)UiFactory.CreateText(val.transform, displayName)).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
			LayoutElement component4 = ((Component)UiFactory.CreateButton(val.transform, "Remove", (UnityAction)delegate
			{
				GroupRegistryRpc.Request(GroupRegistryMutation.UnassignItem(itemType));
			})).GetComponent<LayoutElement>();
			minWidth = (component4.preferredWidth = 90f);
			component4.minWidth = minWidth;
		}

		private void OnClearAllClicked(string groupName)
		{
			if (_clearConfirmUntil <= Time.unscaledTime)
			{
				_clearConfirmUntil = Time.unscaledTime + 3f;
				Rebuild();
				return;
			}
			_clearConfirmUntil = 0f;
			Player localPlayer = Player.m_localPlayer;
			long num = ((localPlayer != null) ? localPlayer.GetPlayerID() : 0);
			GroupRegistryRpc.Request(GroupRegistryMutation.ReplaceGroupItems(groupName, (IEnumerable<string>)Array.Empty<string>(), num));
			Rebuild();
		}

		private static int SortingOrderAboveInventory()
		{
			Canvas val = (((Object)(object)InventoryGui.instance != (Object)null) ? ((Component)InventoryGui.instance).GetComponentInParent<Canvas>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return 1;
			}
			return ((val.isRootCanvas || val.overrideSorting) ? val : val.rootCanvas).sortingOrder + 1;
		}
	}
	internal sealed class GroupManagementController : MonoBehaviour
	{
		private static GroupManagementController? _instance;

		private const float PanelWidth = 460f;

		private const float InitialPanelHeight = 300f;

		private const float MaxContentListHeight = 240f;

		private GameObject? _panel;

		private Transform? _canvasTransform;

		private string? _lastRegistryBlob;

		private const float RegistryCheckIntervalSeconds = 0.1f;

		private float _nextRegistryCheck;

		private bool _textInputBlocked;

		private bool _nameEditorRequested;

		private readonly Dictionary<Toggle, string> _evictionToggles = new Dictionary<Toggle, string>();

		private ContainerRoutingBehaviour? _source;

		internal static bool IsNameEditorOpen
		{
			get
			{
				if ((Object)(object)_instance != (Object)null && _instance._nameEditorRequested && (Object)(object)TextInput.instance != (Object)null && (Object)(object)TextInput.instance.m_panel != (Object)null)
				{
					return TextInput.instance.m_panel.activeInHierarchy;
				}
				return false;
			}
		}

		private void Awake()
		{
			_instance = this;
		}

		private void OnDestroy()
		{
			if (_instance == this)
			{
				_instance = null;
			}
		}

		public static void Toggle(ContainerRoutingBehaviour? source)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if ((Object)(object)_instance._panel != (Object)null)
				{
					_instance.Close();
				}
				else if ((Object)(object)source != (Object)null)
				{
					GroupItemsController.CloseIfOpen();
					_instance.Open(source);
				}
			}
		}

		private void Update()
		{
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			if (!IsSourceChestStillOpen())
			{
				Close();
				return;
			}
			bool flag = TextInput.IsVisible();
			if (_nameEditorRequested && ((Object)(object)TextInput.instance == (Object)null || (Object)(object)TextInput.instance.m_panel == (Object)null || !TextInput.instance.m_panel.activeInHierarchy))
			{
				_nameEditorRequested = false;
			}
			SetTextInputBlocked(flag);
			if ((Object)(object)_panel != (Object)null && _panel.activeSelf == flag)
			{
				_panel.SetActive(!flag);
			}
			if (!flag && Time.unscaledTime >= _nextRegistryCheck)
			{
				_nextRegistryCheck = Time.unscaledTime + 0.1f;
				string text = GroupRegistryAccess.CurrentBlob();
				if (text != null && text != _lastRegistryBlob)
				{
					Rebuild();
				}
			}
		}

		private bool IsSourceChestStillOpen()
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)_source != (Object)null && InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen())
			{
				return (Object)(object)ContainerPanelController.Current == (Object)(object)_source;
			}
			return false;
		}

		private static long LocalPlayerId()
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer == null)
			{
				return 0L;
			}
			return localPlayer.GetPlayerID();
		}

		private void Close()
		{
			if ((Object)(object)_canvasTransform != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvasTransform).gameObject);
			}
			_canvasTransform = null;
			_panel = null;
			_source = null;
			_lastRegistryBlob = null;
			_nameEditorRequested = false;
		}

		private void Open(ContainerRoutingBehaviour source)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			_source = source;
			GameObject val = new GameObject("SCR_GroupManagementCanvas", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			val.transform.SetParent(((Component)this).transform, false);
			Canvas component = val.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.overrideSorting = true;
			component.sortingOrder = SortingOrderAboveInventory();
			_canvasTransform = val.transform;
			_panel = UiFactory.CreatePanel(val.transform, "SCR_GroupManagementPanel", new Vector2(460f, 300f));
			_panel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			Rebuild();
		}

		private static int SortingOrderAboveInventory()
		{
			Canvas val = (((Object)(object)InventoryGui.instance != (Object)null) ? ((Component)InventoryGui.instance).GetComponentInParent<Canvas>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return 1;
			}
			return ((val.isRootCanvas || val.overrideSorting) ? val : val.rootCanvas).sortingOrder + 1;
		}

		private void Rebuild(string? validationMessage = null)
		{
			//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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Expected O, but got Unknown
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			_evictionToggles.Clear();
			foreach (Transform item in _panel.transform)
			{
				((Component)item).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			GameObject obj = UiFactory.CreateRow(_panel.transform, "Header");
			((Component)UiFactory.CreateText(obj.transform, "Storage Groups")).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
			UiFactory.CreateButton(obj.transform, "Close", new UnityAction(Close));
			Toggle obj2 = UiFactory.CreateToggle(UiFactory.CreateRow(_panel.transform, "Options").transform, "Show routing messages", RoutingNotifier.Enabled, delegate(bool value)
			{
				RoutingNotifier.Enabled = value;
			});
			LayoutElement component = ((Component)obj2).GetComponent<LayoutElement>();
			component.minWidth = 260f;
			component.preferredWidth = 260f;
			TMP_Text componentInChildren = ((Component)obj2).GetComponentInChildren<TMP_Text>();
			if (componentInChildren != null)
			{
				componentInChildren.fontSizeMin = 14f;
				componentInChildren.fontSizeMax = 18f;
				componentInChildren.fontSize = 18f;
			}
			if (validationMessage != null)
			{
				TMP_Text obj3 = UiFactory.CreateText(_panel.transform, validationMessage);
				((Graphic)obj3).color = new Color(1f, 0.35f, 0.25f);
				obj3.textWrappingMode = (TextWrappingModes)1;
				((Component)obj3).gameObject.AddComponent<LayoutElement>().minHeight = 44f;
			}
			_lastRegistryBlob = GroupRegistryAccess.CurrentBlob();
			GroupRegistry val2 = GroupRegistryAccess.Load();
			float height = Mathf.Min(240f, (float)(val2.Groups.Count + 1) * 28f);
			RectTransform content = UiFactory.CreateScrollList(_panel.transform, 440f, height).content;
			foreach (GroupDefinition item2 in val2.Groups.OrderBy<GroupDefinition, string>((GroupDefinition group) => group.Name, StringComparer.Ordinal))
			{
				BuildGroupRow((Transform)(object)content, val2, item2);
			}
			UiFactory.CreateButton(UiFactory.CreateRow((Transform)(object)content, "Row_NewGroup").transform, "+", new UnityAction(OnNewGroupClicked));
		}

		private void BuildGroupRow(Transform content, GroupRegistry registry, GroupDefinition group)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			GameObject obj = UiFactory.CreateRow(content, "Row_" + group.Name);
			ContainerRoutingBehaviour? source = _source;
			bool isSelected = ((source != null) ? source.GetSettings().GroupName : null) == GroupNameNormalizer.Normalize(group.Name);
			Button obj2 = UiFactory.CreateButton(obj.transform, $"{group.Name} ({group.ItemTypes.Count})", (UnityAction)delegate
			{
				_source?.SetGroup(isSelected ? null : group.Name);
				Rebuild();
			});
			((Component)obj2).GetComponent<LayoutElement>().flexibleWidth = 1f;
			UiFactory.CreateSelectionBorder(((Component)obj2).transform).SetActive(isSelected);
			((Component)obj2).gameObject.AddComponent<GroupContentsTooltip>().Initialize(() => group.Name, _canvasTransform);
			Toggle val = UiFactory.CreateToggle(obj.transform, "Strict", group.EvictMismatching, delegate(bool value)
			{
				SetGroupEviction(group.Name, value);
			});
			LayoutElement component = ((Component)val).GetComponent<LayoutElement>();
			component.minWidth = 100f;
			component.preferredWidth = 100f;
			((Selectable)val).interactable = !_textInputBlocked;
			_evictionToggles.Add(val, group.Name);
			((Component)val).gameObject.AddComponent<GroupEvictionTooltip>().Initialize(_canvasTransform);
			TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>();
			if (componentInChildren != null)
			{
				componentInChildren.fontSizeMin = 14f;
				componentInChildren.fontSizeMax = 18f;
				componentInChildren.fontSize = 18f;
			}
			UiFactory.CreateButton(obj.transform, "Rename", (UnityAction)delegate
			{
				RequestGroupRename(group.Name);
			});
			UiFactory.CreateButton(obj.transform, "X", (UnityAction)delegate
			{
				GroupRegistryRpc.Request(GroupRegistryMutation.DeleteGroup(group.Name, LocalPlayerId()));
				ContainerRegistry.ClearGroup(group.Name);
				Rebuild();
			});
		}

		public static void NewGroupFromContents(ContainerRoutingBehaviour source, string suggestedName, IReadOnlyList<string> itemTypes)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if ((Object)(object)_instance._panel != (Object)null)
				{
					_instance.Close();
				}
				GroupItemsController.CloseIfOpen();
				_instance.Open(source);
				_instance.RequestGroupCreate(suggestedName, itemTypes);
			}
		}

		public static void CloseIfOpen()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._panel != (Object)null)
			{
				_instance.Close();
			}
		}

		private void OnNewGroupClicked()
		{
			if (!((Object)(object)TextInput.instance == (Object)null))
			{
				GroupRegistry val = GroupRegistryAccess.Load();
				int i;
				for (i = val.Groups.Count + 1; val.HasGroup($"Group-{i}"); i++)
				{
				}
				string suggestedName = $"Group-{i}";
				RequestGroupCreate(suggestedName);
			}
		}

		private void RequestGroupCreate(string suggestedName, IReadOnlyList<string>? itemTypes = null)
		{
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				return;
			}
			DelegateTextReceiver delegateTextReceiver = new DelegateTextReceiver(() => suggestedName, delegate(string name)
			{
				string text = DisplayNameSanitizer.Sanitize(name);
				if (text == null)
				{
					Rebuild("Group name cannot be empty.");
				}
				else
				{
					GroupRegistry val = GroupRegistryAccess.Load();
					if (val.HasGroup(text))
					{
						Rebuild("A group with that name already exists.");
					}
					else
					{
						string text2 = "g:" + Guid.NewGuid().ToString("N");
						GroupRegistryRpc.Request(GroupRegistryMutation.CreateGroup(text, LocalPlayerId(), text2));
						_source?.SetGroup(text, text2);
						if (itemTypes != null && itemTypes.Count > 0)
						{
							string text3 = GroupItemTransfers.Describe(val, text, (IEnumerable<string>)itemTypes, (Func<string, string>)ItemTypeResolver.GetDisplayName);
							foreach (string itemType in itemTypes)
							{
								GroupRegistryRpc.Request(GroupRegistryMutation.AssignItem(itemType, text));
							}
							if (text3 != null)
							{
								Player localPlayer = Player.m_localPlayer;
								if (localPlayer != null)
								{
									((Character)localPlayer).Message((MessageType)2, text3, 0, (Sprite)null, false);
								}
							}
						}
						Rebuild();
					}
				}
			});
			TextInput.instance.RequestText((TextReceiver)(object)delegateTextReceiver, "$scr_group_name", 32);
			_nameEditorRequested = true;
			PrepareForTextInput();
			((MonoBehaviour)this).StartCoroutine(FocusNameOnNextFrame(selectAll: true));
		}

		private void SetGroupEviction(string groupName, bool value)
		{
			if (!GroupRegistryAccess.TryLoad(out GroupRegistry registry))
			{
				Rebuild("Group settings aren't available yet. Try again in a moment.");
				return;
			}
			try
			{
				registry.WithEvictMismatching(groupName, value);
			}
			catch (ArgumentException)
			{
				Rebuild("This group no longer exists.");
				return;
			}
			GroupRegistryRpc.Request(GroupRegistryMutation.SetGroupEviction(groupName, value, LocalPlayerId()));
		}

		private void SetTextInputBlocked(bool blocked)
		{
			if (_textInputBlocked == blocked)
			{
				return;
			}
			_textInputBlocked = blocked;
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			foreach (Toggle key in _evictionToggles.Keys)
			{
				((Selectable)key).interactable = !blocked;
			}
		}

		private void RequestGroupRename(string oldName)
		{
			if ((Object)(object)TextInput.instance == (Object)null)
			{
				return;
			}
			DelegateTextReceiver delegateTextReceiver = new DelegateTextReceiver(() => oldName, delegate(string newName)
			{
				if (string.IsNullOrWhiteSpace(newName) || string.Equals(newName.Trim(), oldName, StringComparison.Ordinal))
				{
					Rebuild();
				}
				else
				{
					try
					{
						GroupRegistry val = GroupRegistryAccess.Load();
						if (val.HasGroup(newName) && GroupNameNormalizer.Normalize(newName) != GroupNameNormalizer.Normalize(oldName))
						{
							throw new ArgumentException("Duplicate group name.");
						}
						if (val.HasGroup(oldName))
						{
							val.WithRenamedGroup(oldName, newName);
						}
						else if (DisplayNameSanitizer.Sanitize(newName) == null)
						{
							throw new ArgumentException("Empty group name.");
						}
					}
					catch (ArgumentException)
					{
						Rebuild("A group with that name already exists, or the name is empty.");
						return;
					}
					GroupRegistryRpc.Request(GroupRegistryMutation.RenameGroup(oldName, newName, LocalPlayerId()));
					Rebuild();
				}
			});
			TextInput.instance.RequestText((TextReceiver)(object)delegateTextReceiver, "$scr_group_name", 32);
			_nameEditorRequested = true;
			PrepareForTextInput();
			((MonoBehaviour)this).StartCoroutine(FocusNameOnNextFrame(selectAll: false));
		}

		private static IEnumerator FocusNameOnNextFrame(bool selectAll)
		{
			yield return null;
			if ((Object)(object)TextInput.instance == (Object)null || !TextInput.IsVisible())
			{
				yield break;
			}
			TMP_InputField componentInChildren = ((Component)TextInput.instance).GetComponentInChildren<TMP_InputField>(true);
			if (componentInChildren != null)
			{
				((Selectable)componentInChildren).Select();
				componentInChildren.ActivateInputField();
				if (selectAll)
				{
					componentInChildren.selectionAnchorPosition = 0;
					componentInChildren.selectionFocusPosition = componentInChildren.text.Length;
				}
				else
				{
					componentInChildren.caretPosition = componentInChildren.text.Length;
				}
				yield break;
			}
			InputField componentInChildren2 = ((Component)TextInput.instance).GetComponentInChildren<InputField>(true);
			if (componentInChildren2 != null)
			{
				((Selectable)componentInChildren2).Select();
				componentInChildren2.ActivateInputField();
				if (selectAll)
				{
					componentInChildren2.selectionAnchorPosition = 0;
					componentInChildren2.selectionFocusPosition = componentInChildren2.text.Length;
				}
				else
				{
					componentInChildren2.caretPosition = componentInChildren2.text.Length;
				}
			}
		}

		private void PrepareForTextInput()
		{
			SetTextInputBlocked(blocked: true);
			GameObject? panel = _panel;
			if (panel != null)
			{
				panel.SetActive(false);
			}
		}
	}
	public static class GroupRegistryAccess
	{
		private static readonly GroupRegistryCache Cache = new GroupRegistryCache((Func<string, GroupRegistry>)DecodeAndBackUpIfCorrupt);

		public static Func<ZDO?> AnchorZdoProvider { get; set; } = () => (ZDO?)null;

		public static GroupRegistry Load()
		{
			TryLoad(out GroupRegistry registry);
			return registry;
		}

		public static bool TryLoad(out GroupRegistry registry)
		{
			return Cache.TryGet(CurrentBlob(), ref registry);
		}

		public static string? CurrentBlob()
		{
			ZDO obj = AnchorZdoProvider();
			if (obj == null)
			{
				return null;
			}
			return obj.GetString("SCR_Groups", string.Empty);
		}

		private static GroupRegistry DecodeAndBackUpIfCorrupt(string? blob)
		{
			GroupRegistry result = default(GroupRegistry);
			if (GroupRegistryCodec.TryDeserialize(blob, ref result))
			{
				return result;
			}
			ZDO val = AnchorZdoProvider();
			if (val != null)
			{
				val.Set("SCR_GroupsCorruptBackup", blob ?? string.Empty);
			}
			return result;
		}

		public static string? DisplayName(string? groupKey)
		{
			object obj2;
			if (groupKey != null)
			{
				GroupDefinition obj = Load().GetGroup(groupKey);
				obj2 = ((obj != null) ? obj.Name : null);
				if (obj2 == null)
				{
					return groupKey;
				}
			}
			else
			{
				obj2 = null;
			}
			return (string?)obj2;
		}

		public static bool IsEvicting(string groupKey)
		{
			GroupDefinition obj = Load().GetGroup(groupKey);
			if (obj == null)
			{
				return false;
			}
			return obj.EvictMismatching;
		}
	}
	internal sealed class HoverTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		private const float Width = 260f;

		private const float TextCheckIntervalSeconds = 0.25f;

		private Func<string?>? _text;

		private GameObject? _panel;

		private bool _hovered;

		private string? _shownText;

		private float _nextTextCheck;

		public void Initialize(Func<string?> text)
		{
			_text = text;
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			_hovered = true;
			Show(_text?.Invoke());
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			_hovered = false;
			Hide();
		}

		private void OnDisable()
		{
			_hovered = false;
			Hide();
		}

		private void Update()
		{
			if (_hovered && !(Time.unscaledTime < _nextTextCheck))
			{
				_nextTextCheck = Time.unscaledTime + 0.25f;
				string text = _text?.Invoke();
				if (text != _shownText)
				{
					Show(text);
				}
			}
		}

		private void Show(string? body)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			Hide();
			_shownText = body;
			if (body != null && body.Length > 0)
			{
				int num = body.Split(new char[1] { '\n' }).Length;
				float num2 = 20f + (float)num * 19f;
				_panel = UiFactory.CreatePanel(((Component)this).transform, "SCR_HoverTooltip", new Vector2(260f, num2));
				RectTransform component = _panel.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0f, 1f);
				component.anchorMax = new Vector2(0f, 1f);
				component.pivot = new Vector2(1f, 1f);
				component.anchoredPosition = new Vector2(-8f, 0f);
				((Graphic)_panel.GetComponent<Image>()).raycastTarget = false;
				TMP_Text obj = UiFactory.CreateText(_panel.transform, body);
				obj.alignment = (TextAlignmentOptions)257;
				obj.fontSize = 14f;
				obj.textWrappingMode = (TextWrappingModes)0;
				obj.overflowMode = (TextOverflowModes)1;
				((Graphic)obj).raycastTarget = false;
				((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = num2 - 20f;
			}
		}

		private void Hide()
		{
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
				Object.Destroy((Object)(object)_panel);
				_panel = null;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Awake")]
	internal static class InventoryGuiAwakePatch
	{
		private static void Postfix(InventoryGui __instance)
		{
			UiFactory.CaptureVanillaStyle(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show", new Type[]
	{
		typeof(Container),
		typeof(int)
	})]
	internal static class InventoryGuiContainerShowPatch
	{
		private static void Postfix(InventoryGui __instance, Container container)
		{
			if (!((Object)(object)container == (Object)null))
			{
				ContainerPanelController.ShowFor(__instance, container);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class InventoryGuiNameEditInputPatch
	{
		private static void Prefix()
		{
			if (GroupManagementController.IsNameEditorOpen)
			{
				ZInput.ResetButtonStatus("Use");
				ZInput.ResetButtonStatus("JoyUse");
			}
		}
	}
	internal static class ItemTypeResolver
	{
		public static string GetItemType(ItemData item)
		{
			if (!((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return item.m_shared.m_name;
			}
			return ((Object)item.m_dropPrefab).name;
		}

		public static string GetDisplayName(string itemType)
		{
			ObjectDB instance = ObjectDB.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject itemPrefab = instance.GetItemPrefab(itemType);
				obj = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name);
			}
			string text = (string)obj;
			if (string.IsNullOrEmpty(text))
			{
				return itemType;
			}
			Localization instance2 = Localization.instance;
			return ((instance2 != null) ? instance2.Localize(text) : null) ?? itemType;
		}

		public static Sprite? GetIcon(string itemType)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemType) : null);
			Sprite[] array = ((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<ItemDrop>()?.m_itemData?.m_shared)?.m_icons;
			if (array == null || array.Length <= 0)
			{
				return null;
			}
			return array[0];
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class LocalizationPatch
	{
		private static readonly MethodInfo AddWordMethod = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null);

		internal static void AddWord(Localization instance, string key, string text)
		{
			AddWordMethod.Invoke(instance, new object[2] { key, text });
		}

		private static void Postfix(Localization __instance)
		{
			AddWord(__instance, "scr_group_name", "Group name");
		}
	}
	internal static class UiFactory
	{
		private const float MaxControlFontSize = 19.2f;

		private static bool _captured;

		private static TMP_FontAsset? _font;

		private static Material? _fontMaterial;

		private static Color _textColor = Color.white;

		private static float _fontSize = 14f;

		private static Sprite? _buttonSprite;

		private static Type _buttonSpriteType = (Type)0;

		private static Transition _buttonTransition = (Transition)1;

		private static ColorBlock _buttonColors = ColorBlock.defaultColorBlock;

		private static SpriteState _buttonSpriteState;

		private static Sprite? _toggleBackgroundSprite;

		private static Type _toggleBackgroundSpriteType = (Type)0;

		private static Sprite? _toggleCheckmarkSprite;

		private static readonly Color PanelColor = new Color(0.07f, 0.06f, 0.05f, 1f);

		private static readonly Color PanelEdgeColor = new Color(0.45f, 0.35f, 0.18f, 1f);

		public static void CaptureVanillaStyle(InventoryGui gui)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			if (_captured || (Object)(object)gui == (Object)null)
			{
				return;
			}
			_captured = true;
			if ((Object)(object)gui.m_containerName != (Object)null)
			{
				_font = gui.m_containerName.font;
				_fontMaterial = gui.m_containerName.fontSharedMaterial;
				_textColor = ((Graphic)gui.m_containerName).color;
				_fontSize = gui.m_containerName.fontSize;
			}
			Button[] array = (Button[])(object)new Button[3] { gui.m_takeAllButton, gui.m_stackAllButton, gui.m_dropButton };
			foreach (Button val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Image component = ((Component)val).GetComponent<Image>();
					if (component != null && component.sprite != null)
					{
						_buttonSprite = component.sprite;
						_buttonSpriteType = component.type;
						_buttonTransition = ((Selectable)val).transition;
						_buttonColors = ((Selectable)val).colors;
						_buttonSpriteState = ((Selectable)val).spriteState;
						break;
					}
				}
			}
			if ((Object)(object)gui.m_pvp != (Object)null)
			{
				Image component2 = ((Component)gui.m_pvp).GetComponent<Image>();
				if ((Object)(object)component2 != (Object)null)
				{
					_toggleBackgroundSprite = component2.sprite;
					_toggleBackgroundSpriteType = component2.type;
				}
				Graphic graphic = gui.m_pvp.graphic;
				Image val2 = (Image)(object)((graphic is Image) ? graphic : null);
				if (val2 != null)
				{
					_toggleCheckmarkSprite = val2.sprite;
				}
			}
		}

		public static GameObject CreatePanel(Transform parent, string name, Vector2 size)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0096: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_010b: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(VerticalLayoutGroup)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.anchoredPosition = Vector2.zero;
			component.sizeDelta = size;
			((Graphic)val.GetComponent<Image>()).color = PanelColor;
			Outline obj = val.AddComponent<Outline>();
			((Shadow)obj).effectColor = PanelEdgeColor;
			((Shadow)obj).effectDistance = new Vector2(2f, -2f);
			VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>();
			((LayoutGroup)component2).padding = new RectOffset(10, 10, 10, 10);
			((HorizontalOrVerticalLayoutGroup)component2).spacing = 6f;
			((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false;
			return val;
		}

		public static GameObject CreateSelectionBorder(Transform parent)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: 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)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			GameObject val = new GameObject("SelectionBorder", new Type[2]
			{
				typeof(RectTransform),
				typeof(LayoutElement)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = new Vector2(1f, 1f);
			component.offsetMax = new Vector2(-1f, -1f);
			val.GetComponent<LayoutElement>().ignoreLayout = true;
			AddBorderBar(val.transform, "Top", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 3f));
			AddBorderBar(val.transform, "Bottom", Vector2.zero, new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 3f));
			AddBorderBar(val.transform, "Left", Vector2.zero, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(3f, 0f));
			AddBorderBar(val.transform, "Right", new Vector2(1f, 0f), Vector2.one, new Vector2(1f, 0.5f), new Vector2(3f, 0f));
			val.SetActive(false);
			return val;
		}

		private static void AddBorderBar(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 size)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = anchorMin;
			component.anchorMax = anchorMax;
			component.pivot = pivot;
			component.sizeDelta = size;
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(1f, 0.84f, 0.12f, 1f);
			((Graphic)component2).raycastTarget = false;
		}

		public static GameObject CreateRow(Transform parent, string name)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(HorizontalLayoutGroup),
				typeof(LayoutElement)
			});
			val.transform.SetParent(parent, false);
			HorizontalLayoutGroup component = val.GetComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component).spacing = 6f;
			((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
			((LayoutGroup)component).childAlignment = (TextAnchor)3;
			LayoutElement component2 = val.GetComponent<LayoutElement>();
			component2.minHeight = 24f;
			component2.preferredHeight = 24f;
			return val;
		}

		public static ScrollRect CreateScrollList(Transform parent, float width, float height)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_004d: 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_00a2: Expected O, but got Unknown
			//IL_00bc: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Expected O, but got Unknown
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ScrollList", new Type[3]
			{
				typeof(RectTransform),
				typeof(ScrollRect),
				typeof(LayoutElement)
			});
			val.transform.SetParent(parent, false);
			val.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.preferredHeight = height;
			component.minHeight = height;
			GameObject val2 = new GameObject("Viewport", new Type[3]
			{
				typeof(RectTransform),
				typeof(RectMask2D),
				typeof(Image)
			});
			val2.transform.SetParent(val.transform, false);
			RectTransform component2 = val2.GetComponent<RectTransform>();
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = Vector2.zero;
			component2.offsetMax = new Vector2(-14f, 0f);
			component2.pivot = new Vector2(0f, 1f);
			Image component3 = val2.GetComponent<Image>();
			((Graphic)component3).color = new Color(1f, 1f, 1f, 0f);
			((Graphic)component3).raycastTarget = true;
			GameObject val3 = new GameObject("Content", new Type[3]
			{
				typeof(RectTransform),
				typeof(VerticalLayoutGroup),
				typeof(ContentSizeFitter)
			});
			val3.transform.SetParent(val2.transform, false);
			RectTransform component4 = val3.GetComponent<RectTransform>();
			component4.anchorMin = new Vector2(0f, 1f);
			component4.anchorMax = new Vector2(1f, 1f);
			component4.pivot = new Vector2(0f, 1f);
			component4.anchoredPosition = Vector2.zero;
			component4.sizeDelta = Vector2.zero;
			VerticalLayoutGroup component5 = val3.GetComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component5).spacing = 4f;
			((HorizontalOrVerticalLayoutGroup)component5).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component5).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component5).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)component5).childForceExpandHeight = false;
			val3.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			ScrollRect component6 = val.GetComponent<ScrollRect>();
			component6.horizontal = false;
			component6.vertical = true;
			component6.scrollSensitivity = 64f;
			component6.movementType = (MovementType)2;
			component6.viewport = component2;
			component6.content = component4;
			GameObject val4 = new GameObject("Scrollbar", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Scrollbar)
			});
			val4.transform.SetParent(val.transform, false);
			RectTransform component7 = val4.GetComponent<RectTransform>();
			component7.anchorMin = new Vector2(1f, 0f);
			component7.anchorMax = Vector2.one;
			component7.pivot = new Vector2(1f, 0.5f);
			component7.sizeDelta = new Vector2(10f, 0f);
			component7.anchoredPosition = new Vector2(-1f, 0f);
			((Graphic)val4.GetComponent<Image>()).color = new Color(0.18f, 0.13f, 0.08f, 0.85f);
			GameObject val5 = new GameObject("Handle", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val5.transform.SetParent(val4.transform, false);
			RectTransform component8 = val5.GetComponent<RectTransform>();
			component8.anchorMin = Vector2.zero;
			component8.anchorMax = Vector2.one;
			component8.offsetMin = Vector2.zero;
			component8.offsetMax = Vector2.zero;
			Image component9 = val5.GetComponent<Image>();
			((Graphic)component9).color = new Color(1f, 0.84f, 0.12f, 1f);
			Scrollbar component10 = val4.GetComponent<Scrollbar>();
			component10.direction = (Direction)2;
			component10.handleRect = component8;
			((Selectable)component10).targetGraphic = (Graphic)(object)component9;
			component6.verticalScrollbar = component10;
			component6.verticalScrollbarVisibility = (ScrollbarVisibility)1;
			return component6;
		}

		public static TMP_Text CreateText(Transform parent, string initialText)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).text = initialText;
			((Graphic)val2).color = _textColor;
			((TMP_Text)val2).fontSize = Mathf.Min(_fontSize, 19.2f);
			((TMP_Text)val2).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
			if ((Object)(object)_font != (Object)null)
			{
				((TMP_Text)val2).font = _font;
			}
			if ((Object)(object)_fontMaterial != (Object)null)
			{
				((TMP_Text)val2).fontSharedMaterial = _fontMaterial;
			}
			val.SetActive(true);
			return (TMP_Text)(object)val2;
		}

		public static Button CreateButton(Transform parent, string label, UnityAction onClick)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Button_" + label, new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Button)
			});
			val.transform.SetParent(parent, false);
			Image component = val.GetComponent<Image>();
			Button component2 = val.GetComponent<Button>();
			if ((Object)(object)_buttonSprite != (Object)null)
			{
				component.sprite = _buttonSprite;
				component.type = _buttonSpriteType;
				((Graphic)component).color = Color.white;
				((Selectable)component2).transition = _buttonTransition;
				((Selectable)component2).colors = _buttonColors;
				((Selectable)component2).spriteState = _buttonSpriteState;
			}
			else
			{
				((Graphic)component).color = new Color(0.2f, 0.2f, 0.2f, 1f);
			}
			((UnityEvent)component2.onClick).AddListener(onClick);
			TMP_Text obj = CreateText(val.transform, label);
			obj.alignment = (TextAlignmentOptions)4098;
			ApplyControlFontSize(obj);
			RectTransform component3 = ((Component)obj).GetComponent<RectTransform>();
			component3.anchorMin = Vector2.zero;
			component3.anchorMax = Vector2.one;
			component3.offsetMin = Vector2.zero;
			component3.offsetMax = Vector2.zero;
			LayoutElement obj2 = val.AddComponent<LayoutElement>();
			obj2.minHeight = 22f;
			obj2.minWidth = 56f;
			return component2;
		}

		private static void ApplyControlFontSize(TMP_Text text)
		{
			float fontSizeMax = (text.fontSize = Mathf.Min(_fontSize, 19.2f));
			text.enableAutoSizing = true;
			text.fontSizeMin = 8f;
			text.fontSizeMax = fontSizeMax;
			text.overflowMode = (TextOverflowModes)1;
		}

		public static Toggle CreateToggle(Transform parent, string label, bool initialValue, UnityAction<bool> onValueChanged)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_009d: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be d