Decompiled source of UnifiedStorage v1.1.2

plugins\UnifiedStorage\UnifiedStorage.Core.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[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("UnifiedStorage.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1bfd7610fb9c5d1c7a459055f53ca382a8873b38")]
[assembly: AssemblyProduct("UnifiedStorage.Core")]
[assembly: AssemblyTitle("UnifiedStorage.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UnifiedStorage.Core
{
	public static class AggregationService
	{
		public static IReadOnlyList<AggregatedItem> Aggregate(IReadOnlyList<SourceStack> sourceStacks, Func<string, int>? resolveMaxStackSize = null)
		{
			Func<string, int> resolveMaxStackSize2 = resolveMaxStackSize;
			return (from item in (from s in sourceStacks
					where s.Amount > 0
					group s by s.Key).Select(delegate(IGrouping<ItemKey, SourceStack> @group)
				{
					int num = ((resolveMaxStackSize2 != null) ? resolveMaxStackSize2(@group.Key.PrefabName) : @group.Max((SourceStack s) => s.StackSize));
					if (num <= 0)
					{
						num = 1;
					}
					return new AggregatedItem
					{
						Key = @group.Key,
						DisplayName = (@group.Select((SourceStack s) => s.DisplayName).FirstOrDefault() ?? @group.Key.PrefabName),
						TotalAmount = @group.Sum((SourceStack s) => s.Amount),
						SourceCount = @group.Select((SourceStack s) => s.SourceId).Distinct().Count(),
						StackSize = num
					};
				})
				orderby item.DisplayName, item.TotalAmount descending
				select item).ToList();
		}

		public static int CalculateVirtualSlots(IReadOnlyList<AggregatedItem> items)
		{
			return items.Sum((AggregatedItem i) => (i.StackSize <= 0) ? 1 : ((int)Math.Ceiling((double)i.TotalAmount / (double)i.StackSize)));
		}
	}
	public static class ChunkedTransfer
	{
		public static int ClampMeasuredMove(int requestedAmount, int beforeAmount, int afterAmount)
		{
			if (requestedAmount <= 0)
			{
				return 0;
			}
			int num = afterAmount - beforeAmount;
			if (num <= 0)
			{
				return 0;
			}
			return Math.Min(requestedAmount, num);
		}

		public static int Move(int requestedAmount, int maxChunkSize, Func<int, int> moveChunk)
		{
			if (moveChunk == null)
			{
				throw new ArgumentNullException("moveChunk");
			}
			if (requestedAmount <= 0 || maxChunkSize <= 0)
			{
				return 0;
			}
			int num = requestedAmount;
			int num2 = 0;
			while (num > 0)
			{
				int num3 = Math.Min(maxChunkSize, num);
				int num4 = moveChunk(num3);
				if (num4 <= 0)
				{
					break;
				}
				if (num4 > num3)
				{
					num4 = num3;
				}
				num2 += num4;
				num -= num4;
				if (num4 < num3)
				{
					break;
				}
			}
			return num2;
		}
	}
	public sealed class ContainerSlot
	{
		public string SourceId { get; set; } = string.Empty;


		public int FreeSpace { get; set; }

		public float Distance { get; set; }
	}
	public static class DepositPlanner
	{
		public static DepositPlan Plan(IReadOnlyList<ContainerSlot> containers, int requestedAmount)
		{
			DepositPlan depositPlan = new DepositPlan
			{
				RequestedAmount = requestedAmount
			};
			int num = Math.Max(0, requestedAmount);
			if (num <= 0 || containers.Count == 0)
			{
				return depositPlan;
			}
			int num2 = num;
			foreach (ContainerSlot item in (from c in containers
				where c.FreeSpace > 0
				orderby c.Distance
				select c).ThenBy<ContainerSlot, string>((ContainerSlot c) => c.SourceId, StringComparer.Ordinal))
			{
				if (num2 <= 0)
				{
					break;
				}
				int num3 = Math.Min(item.FreeSpace, num2);
				if (num3 > 0)
				{
					depositPlan.Deposits.Add(new PlannedDeposit
					{
						SourceId = item.SourceId,
						Amount = num3
					});
					num2 -= num3;
				}
			}
			depositPlan.PlannedAmount = num - num2;
			return depositPlan;
		}
	}
	public readonly struct ItemKey : IEquatable<ItemKey>
	{
		public string PrefabName { get; }

		public int Quality { get; }

		public int Variant { get; }

		public ItemKey(string prefabName, int quality, int variant)
		{
			PrefabName = prefabName ?? string.Empty;
			Quality = quality;
			Variant = variant;
		}

		public bool Equals(ItemKey other)
		{
			if (string.Equals(PrefabName, other.PrefabName, StringComparison.Ordinal) && Quality == other.Quality)
			{
				return Variant == other.Variant;
			}
			return false;
		}

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

		public override int GetHashCode()
		{
			return ((17 * 23 + (PrefabName?.GetHashCode() ?? 0)) * 23 + Quality.GetHashCode()) * 23 + Variant.GetHashCode();
		}
	}
	public sealed class AggregatedItem
	{
		public ItemKey Key { get; set; }

		public string DisplayName { get; set; } = string.Empty;


		public int TotalAmount { get; set; }

		public int SourceCount { get; set; }

		public int StackSize { get; set; }
	}
	public sealed class SearchResult
	{
		public IReadOnlyList<AggregatedItem> Items { get; set; } = Array.Empty<AggregatedItem>();

	}
	public sealed class SourceStack
	{
		public ItemKey Key { get; set; }

		public string DisplayName { get; set; } = string.Empty;


		public int Amount { get; set; }

		public int StackSize { get; set; }

		public float Distance { get; set; }

		public string SourceId { get; set; } = string.Empty;

	}
	public sealed class PlannedTake
	{
		public string SourceId { get; set; } = string.Empty;


		public int Amount { get; set; }
	}
	public sealed class WithdrawPlan
	{
		public List<PlannedTake> Takes { get; set; } = new List<PlannedTake>();


		public int RequestedAmount { get; set; }

		public int PlannedAmount { get; set; }
	}
	public sealed class PlannedDeposit
	{
		public string SourceId { get; set; } = string.Empty;


		public int Amount { get; set; }
	}
	public sealed class DepositPlan
	{
		public List<PlannedDeposit> Deposits { get; set; } = new List<PlannedDeposit>();


		public int RequestedAmount { get; set; }

		public int PlannedAmount { get; set; }
	}
	public static class SearchService
	{
		public static SearchResult Filter(IReadOnlyList<AggregatedItem> items, string query)
		{
			if (items.Count == 0)
			{
				return new SearchResult();
			}
			if (string.IsNullOrWhiteSpace(query))
			{
				return new SearchResult
				{
					Items = items
				};
			}
			string trimmed = query.Trim();
			List<AggregatedItem> items2 = items.Where((AggregatedItem item) => item.DisplayName.IndexOf(trimmed, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
			return new SearchResult
			{
				Items = items2
			};
		}
	}
	public static class WithdrawPlanner
	{
		public static WithdrawPlan Plan(IReadOnlyList<SourceStack> sourceStacks, ItemKey key, int requestedAmount, int maxReceivable)
		{
			int val = Math.Max(0, requestedAmount);
			int num = Math.Max(0, Math.Min(val, maxReceivable));
			int num2 = num;
			WithdrawPlan withdrawPlan = new WithdrawPlan
			{
				RequestedAmount = requestedAmount
			};
			if (num2 <= 0)
			{
				return withdrawPlan;
			}
			foreach (SourceStack item in (from s in sourceStacks
				where s.Key.Equals(key) && s.Amount > 0
				orderby s.Distance
				select s).ThenBy<SourceStack, string>((SourceStack s) => s.SourceId, StringComparer.Ordinal))
			{
				if (num2 == 0)
				{
					break;
				}
				int num3 = Math.Min(item.Amount, num2);
				if (num3 > 0)
				{
					withdrawPlan.Takes.Add(new PlannedTake
					{
						SourceId = item.SourceId,
						Amount = num3
					});
					num2 -= num3;
				}
			}
			withdrawPlan.PlannedAmount = num - num2;
			return withdrawPlan;
		}
	}
}

plugins\UnifiedStorage\UnifiedStorage.dll

Decompiled 5 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 Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using TMPro;
using UnifiedStorage.Core;
using UnifiedStorage.Mod.Config;
using UnifiedStorage.Mod.Diagnostics;
using UnifiedStorage.Mod.Domain;
using UnifiedStorage.Mod.Models;
using UnifiedStorage.Mod.Network;
using UnifiedStorage.Mod.Patches;
using UnifiedStorage.Mod.Pieces;
using UnifiedStorage.Mod.Server;
using UnifiedStorage.Mod.Session;
using UnifiedStorage.Mod.Shared;
using UnifiedStorage.Mod.UI;
using UnityEngine;
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("UnifiedStorage")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1bfd7610fb9c5d1c7a459055f53ca382a8873b38")]
[assembly: AssemblyProduct("UnifiedStorage")]
[assembly: AssemblyTitle("UnifiedStorage")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UnifiedStorage.Mod
{
	[BepInPlugin("andre.valheim.unifiedstorage", "Unified Storage", "1.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class UnifiedStoragePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "andre.valheim.unifiedstorage";

		public const string PluginName = "Unified Storage";

		public const string PluginVersion = "1.1.2";

		private const float WorldChangeRefreshMinInterval = 0.2f;

		private static bool _blockGameInput;

		private StorageConfig? _config;

		private StorageTrace? _trace;

		private Harmony? _harmony;

		private IContainerScanner? _scanner;

		private TerminalSessionService? _session;

		private TerminalAuthorityService? _authority;

		private TerminalRpcRoutes? _rpcRoutes;

		private UnifiedTerminalRegistrar? _pieceRegistrar;

		private TerminalUIManager? _ui;

		private bool _trackedInventoryDirty;

		private bool _isApplyingRefresh;

		private bool _wasDragging;

		private float _nextAllowedWorldRefreshAt;

		private string _lastSearch = string.Empty;

		private int _lastUiRevision = -1;

		internal static UnifiedStoragePlugin? Instance { get; private set; }

		private void Awake()
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			Instance = this;
			_config = new StorageConfig(((BaseUnityPlugin)this).Config);
			_trace = new StorageTrace(((BaseUnityPlugin)this).Logger, _config);
			_scanner = new ContainerScanner(_config);
			_authority = new TerminalAuthorityService(_config, _scanner, ((BaseUnityPlugin)this).Logger);
			_rpcRoutes = new TerminalRpcRoutes(_authority, ((BaseUnityPlugin)this).Logger);
			_rpcRoutes.EnsureRegistered();
			_session = new TerminalSessionService(_config, _scanner, _rpcRoutes, ((BaseUnityPlugin)this).Logger, _trace);
			_pieceRegistrar = new UnifiedTerminalRegistrar(_config, ((BaseUnityPlugin)this).Logger);
			_ui = new TerminalUIManager();
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable;
			_harmony = new Harmony("andre.valheim.unifiedstorage.patches");
			_harmony.PatchAll(typeof(ContainerInteractPatch));
			_harmony.PatchAll(typeof(InventoryAddItemPatch));
			_harmony.PatchAll(typeof(InventoryAddItemAtPositionPatch));
			_harmony.PatchAll(typeof(InventoryMoveItemToThisPatch));
			_harmony.PatchAll(typeof(InventoryGuiHidePatch));
			_harmony.PatchAll(typeof(InventoryGuiAwakePatch));
			_harmony.PatchAll(typeof(InventoryGuiUpdatePatch));
			_harmony.PatchAll(typeof(InventoryGridOnLeftClickPatch));
			_harmony.PatchAll(typeof(InventoryGridOnRightClickPatch));
			_harmony.PatchAll(typeof(InventoryGuiOnTakeAllPatch));
			_harmony.PatchAll(typeof(InventoryGuiOnStackAllPatch));
			_harmony.PatchAll(typeof(InventoryGuiOnDropOutsidePatch));
			_harmony.PatchAll(typeof(InventoryChangedPatch));
			_harmony.PatchAll(typeof(ZInputGetButtonPatch));
			_harmony.PatchAll(typeof(ZInputGetButtonDownPatch));
			_harmony.PatchAll(typeof(ZInputGetButtonUpPatch));
			_harmony.PatchAll(typeof(ZInputGetKeyPatch));
			_harmony.PatchAll(typeof(ZInputGetKeyDownPatch));
			_harmony.PatchAll(typeof(ZInputGetKeyUpPatch));
			_harmony.PatchAll(typeof(ContainerHoverTextPatch));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Unified Storage v1.1.2 loaded.");
		}

		private void Update()
		{
			_rpcRoutes?.EnsureRegistered();
			_authority?.Tick();
			_session?.Tick();
			TryProcessWorldChangeRefresh();
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable;
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_session?.EndSession();
			_blockGameInput = false;
			Instance = null;
		}

		private void OnVanillaPrefabsAvailable()
		{
			_pieceRegistrar?.RegisterPiece();
		}

		internal void TryHandleChestInteract(Container container, Humanoid character)
		{
			if (_session != null)
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && _session.HandleContainerInteract(container, val))
				{
					_lastSearch = string.Empty;
					_ui?.ClearSearch();
				}
			}
		}

		internal void OnInventoryGuiClosing()
		{
			_blockGameInput = false;
			_lastSearch = string.Empty;
			_trackedInventoryDirty = false;
			_isApplyingRefresh = false;
			_wasDragging = false;
			_lastUiRevision = -1;
			_session?.EndSession();
			_ui?.Reset();
		}

		internal void OnInventoryGuiAwake(InventoryGui gui)
		{
			_ui?.EnsureNativeUi(gui);
		}

		internal void OnInventoryGuiUpdate(InventoryGui gui)
		{
			if (_ui == null || _session == null)
			{
				return;
			}
			_ui.EnsureNativeUi(gui);
			_ui.UpdateChestInclusionToggle(gui);
			if (!_session.IsActive || !InventoryGui.IsVisible() || !gui.IsContainerOpen())
			{
				_blockGameInput = false;
				_wasDragging = false;
				_ui.SetTakeAllButtonEnabled(gui, enabled: true);
				_ui.SetNativeUiVisible(visible: false);
				_ui.RestoreLayout();
				_lastUiRevision = -1;
				return;
			}
			_ui.SetTakeAllButtonEnabled(gui, enabled: false);
			_ui.SetNativeUiVisible(visible: true);
			_ui.UpdateContainerName(gui);
			bool flag = ReflectionHelpers.IsDragInProgress();
			if (flag && !_wasDragging)
			{
				OnContainerInteraction();
			}
			_wasDragging = flag;
			_ui.ApplyExpandedLayout(gui, _session.ContentRows);
			if (_session.UiRevision != _lastUiRevision)
			{
				_lastUiRevision = _session.UiRevision;
				_ui.UpdateMetaText(_session.SlotsUsedVirtual, _session.SlotsTotalPhysical, _session.ChestsInRange);
				_ui.RefreshContainerGrid(gui);
			}
			_ui.UpdateSearchBinding(gui);
			UpdateSearchFocusState();
			ProcessSearchChanges(gui);
		}

		internal static bool ShouldBlockGameInput()
		{
			return _blockGameInput;
		}

		internal bool IsUnifiedSessionActive()
		{
			if (_session != null)
			{
				return _session.IsActive;
			}
			return false;
		}

		internal bool ShouldBlockDeposit(Inventory targetInventory)
		{
			if (_session == null || !_session.IsActive || _session.IsApplyingProjection)
			{
				return false;
			}
			if (!_session.IsTerminalInventory(targetInventory))
			{
				return false;
			}
			return _session.IsStorageFull;
		}

		internal int GetNearbyChestCount(Vector3 position)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (_scanner == null || _config == null)
			{
				return 0;
			}
			return _scanner.GetNearbyContainers(position, _config.ScanRadius.Value).Count;
		}

		internal void OnTrackedInventoryChanged(Inventory inventory)
		{
			if (!_isApplyingRefresh && _session != null && _session.IsActive && inventory != null && !_session.IsApplyingProjection && _session.IsTrackedInventory(inventory))
			{
				_trackedInventoryDirty = true;
			}
		}

		internal void OnContainerInteraction()
		{
			if (_session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null))
			{
				_trackedInventoryDirty = false;
				ExecuteSessionRefresh(delegate
				{
					_session.NotifyContainerInteraction();
				});
				_ui?.UpdateMetaText(_session.SlotsUsedVirtual, _session.SlotsTotalPhysical, _session.ChestsInRange);
				_ui?.RefreshContainerGrid(InventoryGui.instance);
			}
		}

		internal void OnInventoryGridInteraction(InventoryGrid grid)
		{
			if (_session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null) && !((Object)(object)grid == (Object)null))
			{
				OnContainerInteraction();
			}
		}

		private void ProcessSearchChanges(InventoryGui gui)
		{
			if (_ui != null && _session != null)
			{
				string searchText = _ui.SearchText;
				if (!string.Equals(searchText, _lastSearch, StringComparison.Ordinal))
				{
					_lastSearch = searchText;
					_session.SetSearchQuery(_lastSearch);
					_ui.RefreshContainerGrid(gui);
				}
			}
		}

		private void UpdateSearchFocusState()
		{
			_blockGameInput = _ui != null && _ui.IsSearchFocused;
		}

		private void TryProcessWorldChangeRefresh()
		{
			if (_trackedInventoryDirty && _session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null) && InventoryGui.IsVisible() && InventoryGui.instance.IsContainerOpen() && !(Time.unscaledTime < _nextAllowedWorldRefreshAt) && !ReflectionHelpers.IsDragInProgress())
			{
				_nextAllowedWorldRefreshAt = Time.unscaledTime + 0.2f;
				_trackedInventoryDirty = false;
				ExecuteSessionRefresh(delegate
				{
					_session.RefreshFromWorldChange();
				});
				_ui?.UpdateMetaText(_session.SlotsUsedVirtual, _session.SlotsTotalPhysical, _session.ChestsInRange);
				_ui?.RefreshContainerGrid(InventoryGui.instance);
			}
		}

		private void ExecuteSessionRefresh(Action refreshAction)
		{
			if (_isApplyingRefresh)
			{
				return;
			}
			_isApplyingRefresh = true;
			try
			{
				refreshAction();
			}
			finally
			{
				_isApplyingRefresh = false;
			}
		}
	}
}
namespace UnifiedStorage.Mod.UI
{
	public sealed class TerminalUIManager
	{
		private const int VisibleGridRows = 7;

		private const float FooterPanelBottomOffset = -70f;

		private const float FooterPanelHeight = 52f;

		private const float GridTopReserve = 124f;

		private const float ChestToggleTopOffset = -8f;

		private RectTransform? _nativeUiRoot;

		private TMP_Text? _metaText;

		private Image? _slotBarFill;

		private TMP_InputField? _searchInputField;

		private RectTransform? _chestToggleRoot;

		private Toggle? _chestIncludeToggle;

		private RectTransform? _containerRect;

		private Container? _boundChestForToggle;

		private Vector2 _originalContainerSize;

		private int _originalGridHeight;

		private Vector2 _originalGridOffsetMin;

		private Vector2 _originalGridOffsetMax;

		private bool _layoutCaptured;

		private bool _nativeBuilt;

		private bool _isApplyingChestToggle;

		private int _lastAppliedVisibleRows;

		public bool IsSearchFocused
		{
			get
			{
				if ((Object)(object)_searchInputField != (Object)null)
				{
					return _searchInputField.isFocused;
				}
				return false;
			}
		}

		public string SearchText
		{
			get
			{
				TMP_InputField? searchInputField = _searchInputField;
				return ((searchInputField != null) ? searchInputField.text : null) ?? string.Empty;
			}
		}

		public bool IsLayoutCaptured => _layoutCaptured;

		public event Action<string>? SearchValueChanged;

		public event Action<bool>? ChestToggleChanged;

		public void EnsureNativeUi(InventoryGui gui)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_006f: 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_00a3: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			if (!_nativeBuilt)
			{
				RectTransform containerRect = ReflectionHelpers.GetContainerRect(gui);
				if (!((Object)(object)containerRect == (Object)null))
				{
					_containerRect = containerRect;
					TMP_Text containerName = ReflectionHelpers.GetContainerName(gui);
					GameObject val = new GameObject("US_NativePanel", new Type[1] { typeof(RectTransform) });
					val.transform.SetParent((Transform)(object)containerRect, false);
					_nativeUiRoot = val.GetComponent<RectTransform>();
					_nativeUiRoot.anchorMin = new Vector2(0f, 0f);
					_nativeUiRoot.anchorMax = new Vector2(1f, 0f);
					_nativeUiRoot.pivot = new Vector2(0.5f, 0f);
					_nativeUiRoot.anchoredPosition = new Vector2(0f, -70f);
					_nativeUiRoot.sizeDelta = new Vector2(0f, 52f);
					BuildSlotBar(val.transform, containerName);
					BuildSearchField(val.transform, containerName);
					EnsureChestToggleUi(containerRect, containerName);
					_nativeBuilt = true;
					SetNativeUiVisible(visible: false);
					SetChestToggleVisible(visible: false);
				}
			}
		}

		private void BuildSlotBar(Transform parent, TMP_Text? containerName)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_004f: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			float num = 20f;
			GameObject val = new GameObject("US_SlotBarBg", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(0f, 0f);
			component.sizeDelta = new Vector2(-16f, num);
			Image component2 = val.GetComponent<Image>();
			((Graphic)component2).color = new Color(0f, 0f, 0f, 0.55f);
			((Graphic)component2).raycastTarget = false;
			GameObject val2 = new GameObject("US_SlotBarFill", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val2.transform.SetParent(val.transform, false);
			RectTransform component3 = val2.GetComponent<RectTransform>();
			component3.anchorMin = new Vector2(0f, 0f);
			component3.anchorMax = new Vector2(0f, 1f);
			component3.pivot = new Vector2(0f, 0.5f);
			component3.offsetMin = new Vector2(2f, 2f);
			component3.offsetMax = new Vector2(2f, -2f);
			_slotBarFill = val2.GetComponent<Image>();
			((Graphic)_slotBarFill).color = new Color(0.85f, 0.75f, 0.2f, 0.85f);
			((Graphic)_slotBarFill).raycastTarget = false;
			GameObject val3 = new GameObject("US_SlotLabel", new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val3.transform.SetParent(val.transform, false);
			RectTransform component4 = val3.GetComponent<RectTransform>();
			component4.anchorMin = Vector2.zero;
			component4.anchorMax = Vector2.one;
			component4.offsetMin = new Vector2(6f, 0f);
			component4.offsetMax = new Vector2(-6f, 0f);
			_metaText = (TMP_Text?)(object)val3.GetComponent<TextMeshProUGUI>();
			if ((Object)(object)containerName != (Object)null)
			{
				_metaText.font = containerName.font;
				_metaText.fontSharedMaterial = containerName.fontSharedMaterial;
			}
			_metaText.fontSize = 14f;
			((Graphic)_metaText).color = new Color(1f, 1f, 1f, 0.95f);
			_metaText.alignment = (TextAlignmentOptions)514;
			((Graphic)_metaText).raycastTarget = false;
		}

		private void BuildSearchField(Transform parent, TMP_Text? containerName)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: 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_0174: Expected O, but got Unknown
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			float num = 22f;
			float num2 = -24f;
			GameObject val = new GameObject("US_SearchRow", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(0f, num2);
			component.sizeDelta = new Vector2(-16f, num);
			GameObject val2 = new GameObject("US_SearchInput", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(TMP_InputField)
			});
			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 = Vector2.zero;
			((Graphic)val2.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.5f);
			_searchInputField = val2.GetComponent<TMP_InputField>();
			GameObject val3 = new GameObject("Viewport", new Type[2]
			{
				typeof(RectTransform),
				typeof(RectMask2D)
			});
			val3.transform.SetParent(val2.transform, false);
			RectTransform component3 = val3.GetComponent<RectTransform>();
			component3.anchorMin = Vector2.zero;
			component3.anchorMax = Vector2.one;
			component3.offsetMin = new Vector2(8f, 2f);
			component3.offsetMax = new Vector2(-8f, -2f);
			GameObject val4 = new GameObject("Text", new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val4.transform.SetParent(val3.transform, false);
			RectTransform component4 = val4.GetComponent<RectTransform>();
			component4.anchorMin = Vector2.zero;
			component4.anchorMax = Vector2.one;
			component4.offsetMin = Vector2.zero;
			component4.offsetMax = Vector2.zero;
			TextMeshProUGUI component5 = val4.GetComponent<TextMeshProUGUI>();
			if ((Object)(object)containerName != (Object)null)
			{
				((TMP_Text)component5).font = containerName.font;
				((TMP_Text)component5).fontSharedMaterial = containerName.fontSharedMaterial;
			}
			((TMP_Text)component5).fontSize = 14f;
			((Graphic)component5).color = new Color(1f, 1f, 1f, 0.9f);
			((TMP_Text)component5).alignment = (TextAlignmentOptions)4097;
			GameObject val5 = new GameObject("Placeholder", new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val5.transform.SetParent(val3.transform, false);
			RectTransform component6 = val5.GetComponent<RectTransform>();
			component6.anchorMin = Vector2.zero;
			component6.anchorMax = Vector2.one;
			component6.offsetMin = Vector2.zero;
			component6.offsetMax = Vector2.zero;
			TextMeshProUGUI component7 = val5.GetComponent<TextMeshProUGUI>();
			((TMP_Text)component7).text = "Search items...";
			if ((Object)(object)containerName != (Object)null)
			{
				((TMP_Text)component7).font = containerName.font;
				((TMP_Text)component7).fontSharedMaterial = containerName.fontSharedMaterial;
			}
			((TMP_Text)component7).fontSize = 13f;
			((Graphic)component7).color = new Color(1f, 1f, 1f, 0.35f);
			((TMP_Text)component7).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)component7).fontStyle = (FontStyles)2;
			_searchInputField.textViewport = component3;
			_searchInputField.textComponent = (TMP_Text)(object)component5;
			_searchInputField.placeholder = (Graphic)(object)component7;
			_searchInputField.lineType = (LineType)0;
			((UnityEvent<string>)(object)_searchInputField.onValueChanged).AddListener((UnityAction<string>)delegate(string value)
			{
				this.SearchValueChanged?.Invoke(value ?? string.Empty);
			});
		}

		private void EnsureChestToggleUi(RectTransform container, TMP_Text? containerName)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: 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_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: 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_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_chestToggleRoot != (Object)null) || !((Object)(object)_chestIncludeToggle != (Object)null))
			{
				GameObject val = new GameObject("US_ChestIncludeToggle", new Type[2]
				{
					typeof(RectTransform),
					typeof(Toggle)
				});
				val.transform.SetParent((Transform)(object)container, false);
				_chestToggleRoot = val.GetComponent<RectTransform>();
				_chestToggleRoot.anchorMin = new Vector2(1f, 1f);
				_chestToggleRoot.anchorMax = new Vector2(1f, 1f);
				_chestToggleRoot.pivot = new Vector2(1f, 1f);
				_chestToggleRoot.anchoredPosition = new Vector2(-14f, -8f);
				_chestToggleRoot.sizeDelta = new Vector2(190f, 20f);
				Toggle component = val.GetComponent<Toggle>();
				((Selectable)component).transition = (Transition)0;
				component.isOn = true;
				_chestIncludeToggle = component;
				GameObject val2 = new GameObject("Background", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val2.transform.SetParent(val.transform, false);
				RectTransform component2 = val2.GetComponent<RectTransform>();
				component2.anchorMin = new Vector2(0f, 0.5f);
				component2.anchorMax = new Vector2(0f, 0.5f);
				component2.pivot = new Vector2(0f, 0.5f);
				component2.anchoredPosition = Vector2.zero;
				component2.sizeDelta = new Vector2(16f, 16f);
				Image component3 = val2.GetComponent<Image>();
				((Graphic)component3).color = new Color(0f, 0f, 0f, 0.62f);
				GameObject val3 = new GameObject("Checkmark", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val3.transform.SetParent(val2.transform, false);
				RectTransform component4 = val3.GetComponent<RectTransform>();
				component4.anchorMin = new Vector2(0.5f, 0.5f);
				component4.anchorMax = new Vector2(0.5f, 0.5f);
				component4.pivot = new Vector2(0.5f, 0.5f);
				component4.anchoredPosition = Vector2.zero;
				component4.sizeDelta = new Vector2(11f, 11f);
				Image component5 = val3.GetComponent<Image>();
				((Graphic)component5).color = new Color(0.82f, 0.93f, 0.66f, 1f);
				GameObject val4 = new GameObject("Label", new Type[2]
				{
					typeof(RectTransform),
					typeof(TextMeshProUGUI)
				});
				val4.transform.SetParent(val.transform, false);
				RectTransform component6 = val4.GetComponent<RectTransform>();
				component6.anchorMin = new Vector2(0f, 0.5f);
				component6.anchorMax = new Vector2(1f, 0.5f);
				component6.pivot = new Vector2(0f, 0.5f);
				component6.anchoredPosition = new Vector2(22f, 0f);
				component6.sizeDelta = new Vector2(-22f, 18f);
				TextMeshProUGUI component7 = val4.GetComponent<TextMeshProUGUI>();
				((TMP_Text)component7).text = "Include In Unified";
				if ((Object)(object)containerName != (Object)null)
				{
					((TMP_Text)component7).font = containerName.font;
					((TMP_Text)component7).fontSize = containerName.fontSize * 0.52f;
					((Graphic)component7).color = ((Graphic)containerName).color;
					((TMP_Text)component7).fontSharedMaterial = containerName.fontSharedMaterial;
				}
				else
				{
					((TMP_Text)component7).fontSize = 14f;
					((Graphic)component7).color = Color.white;
				}
				((TMP_Text)component7).alignment = (TextAlignmentOptions)4097;
				((Selectable)component).targetGraphic = (Graphic)(object)component3;
				component.graphic = (Graphic)(object)component5;
				((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)OnChestIncludeToggleChanged);
			}
		}

		public void UpdateChestInclusionToggle(InventoryGui gui)
		{
			if ((Object)(object)_chestToggleRoot == (Object)null || (Object)(object)_chestIncludeToggle == (Object)null || !InventoryGui.IsVisible() || !gui.IsContainerOpen())
			{
				_boundChestForToggle = null;
				SetChestToggleVisible(visible: false);
				return;
			}
			Container currentContainer = ReflectionHelpers.GetCurrentContainer(gui);
			if ((Object)(object)currentContainer == (Object)null || UnifiedTerminal.IsTerminal(currentContainer))
			{
				_boundChestForToggle = null;
				SetChestToggleVisible(visible: false);
				return;
			}
			SetChestToggleVisible(visible: true);
			bool flag = ChestInclusionRules.IsIncludedInUnified(currentContainer);
			if (_boundChestForToggle != currentContainer || _chestIncludeToggle.isOn != flag)
			{
				_isApplyingChestToggle = true;
				_chestIncludeToggle.isOn = flag;
				_isApplyingChestToggle = false;
				_boundChestForToggle = currentContainer;
			}
		}

		private void OnChestIncludeToggleChanged(bool includeInUnified)
		{
			if (!_isApplyingChestToggle)
			{
				InventoryGui instance = InventoryGui.instance;
				Container val = (((Object)(object)instance != (Object)null) ? ReflectionHelpers.GetCurrentContainer(instance) : null);
				if (!((Object)(object)val == (Object)null) && !UnifiedTerminal.IsTerminal(val))
				{
					ChestInclusionRules.TrySetIncludedInUnified(val, includeInUnified);
					this.ChestToggleChanged?.Invoke(includeInUnified);
				}
			}
		}

		public void UpdateContainerName(InventoryGui gui)
		{
			TMP_Text containerName = ReflectionHelpers.GetContainerName(gui);
			if ((Object)(object)containerName != (Object)null && !string.Equals(containerName.text, "Storage Interface", StringComparison.Ordinal))
			{
				containerName.text = "Storage Interface";
			}
		}

		public void UpdateMetaText(int slotsUsed, int slotsTotal, int chestCount)
		{
			//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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_metaText != (Object)null)
			{
				_metaText.text = $"{slotsUsed} / {slotsTotal}";
			}
			if ((Object)(object)_slotBarFill != (Object)null)
			{
				float num = ((slotsTotal > 0) ? Mathf.Clamp01((float)slotsUsed / (float)slotsTotal) : 0f);
				Transform parent = ((Component)_slotBarFill).transform.parent;
				RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				float num2;
				if (!((Object)(object)val != (Object)null))
				{
					num2 = 100f;
				}
				else
				{
					Rect rect = val.rect;
					num2 = ((Rect)(ref rect)).width - 4f;
				}
				float num3 = num2;
				((Component)_slotBarFill).GetComponent<RectTransform>().sizeDelta = new Vector2(num3 * num, 0f);
				if (num >= 0.95f)
				{
					((Graphic)_slotBarFill).color = new Color(0.85f, 0.25f, 0.2f, 0.85f);
				}
				else if (num >= 0.75f)
				{
					((Graphic)_slotBarFill).color = new Color(0.9f, 0.6f, 0.15f, 0.85f);
				}
				else
				{
					((Graphic)_slotBarFill).color = new Color(0.85f, 0.75f, 0.2f, 0.85f);
				}
			}
		}

		public void UpdateSearchBinding(InventoryGui gui)
		{
			if (!((Object)(object)_searchInputField == (Object)null) && _searchInputField.isFocused && (Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271) || Input.GetKeyDown((KeyCode)27)))
			{
				_searchInputField.DeactivateInputField(false);
			}
		}

		public void ClearSearch()
		{
			if ((Object)(object)_searchInputField != (Object)null)
			{
				_searchInputField.text = string.Empty;
			}
		}

		public void ApplyExpandedLayout(InventoryGui gui, int contentRows)
		{
			//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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_containerRect == (Object)null)
			{
				return;
			}
			InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(gui);
			if ((Object)(object)containerGrid == (Object)null)
			{
				return;
			}
			if (!_layoutCaptured)
			{
				_originalContainerSize = _containerRect.sizeDelta;
				_originalGridHeight = ReflectionHelpers.GetGridHeight(containerGrid);
				RectTransform gridRoot = ReflectionHelpers.GetGridRoot(containerGrid);
				if ((Object)(object)gridRoot != (Object)null)
				{
					_originalGridOffsetMin = gridRoot.offsetMin;
					_originalGridOffsetMax = gridRoot.offsetMax;
				}
				_layoutCaptured = true;
				_lastAppliedVisibleRows = -1;
			}
			int num = Math.Max(1, Math.Min(contentRows, 7));
			if (num != _lastAppliedVisibleRows)
			{
				_lastAppliedVisibleRows = num;
				ReflectionHelpers.SetGridHeight(containerGrid, num);
				float gridElementSpace = ReflectionHelpers.GetGridElementSpace(containerGrid);
				GameObject gridElementPrefab = ReflectionHelpers.GetGridElementPrefab(containerGrid);
				float num2 = 64f;
				RectTransform val = default(RectTransform);
				if ((Object)(object)gridElementPrefab != (Object)null && gridElementPrefab.TryGetComponent<RectTransform>(ref val))
				{
					num2 = val.sizeDelta.y;
				}
				int num3 = Math.Max(0, num - _originalGridHeight);
				float num4 = (num2 + gridElementSpace) * (float)num3;
				_containerRect.sizeDelta = new Vector2(_originalContainerSize.x, _originalContainerSize.y + num4);
				RectTransform gridRoot2 = ReflectionHelpers.GetGridRoot(containerGrid);
				if ((Object)(object)gridRoot2 != (Object)null)
				{
					gridRoot2.offsetMax = new Vector2(_originalGridOffsetMax.x, -124f);
					gridRoot2.offsetMin = _originalGridOffsetMin;
				}
			}
		}

		public void RestoreLayout()
		{
			//IL_008a: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			if (!_layoutCaptured || (Object)(object)InventoryGui.instance == (Object)null)
			{
				return;
			}
			InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(InventoryGui.instance);
			if ((Object)(object)containerGrid != (Object)null)
			{
				ReflectionHelpers.SetGridHeight(containerGrid, _originalGridHeight);
				RectTransform gridRoot = ReflectionHelpers.GetGridRoot(containerGrid);
				if ((Object)(object)gridRoot != (Object)null)
				{
					gridRoot.offsetMin = _originalGridOffsetMin;
					gridRoot.offsetMax = _originalGridOffsetMax;
				}
				Inventory inventory = containerGrid.GetInventory();
				if (inventory != null)
				{
					containerGrid.UpdateInventory(inventory, Player.m_localPlayer, (ItemData)null);
				}
			}
			if ((Object)(object)_containerRect != (Object)null)
			{
				_containerRect.sizeDelta = _originalContainerSize;
			}
			_layoutCaptured = false;
			_lastAppliedVisibleRows = -1;
		}

		public void RefreshContainerGrid(InventoryGui gui)
		{
			InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(gui);
			if (!((Object)(object)containerGrid == (Object)null))
			{
				Inventory inventory = containerGrid.GetInventory();
				if (inventory != null)
				{
					containerGrid.UpdateInventory(inventory, Player.m_localPlayer, (ItemData)null);
				}
			}
		}

		public void SetNativeUiVisible(bool visible)
		{
			if ((Object)(object)_nativeUiRoot != (Object)null)
			{
				((Component)_nativeUiRoot).gameObject.SetActive(visible);
			}
		}

		public void SetChestToggleVisible(bool visible)
		{
			if ((Object)(object)_chestToggleRoot != (Object)null)
			{
				((Component)_chestToggleRoot).gameObject.SetActive(visible);
			}
		}

		public void SetTakeAllButtonEnabled(InventoryGui gui, bool enabled)
		{
			Button takeAllButton = ReflectionHelpers.GetTakeAllButton(gui);
			if ((Object)(object)takeAllButton != (Object)null)
			{
				((Selectable)takeAllButton).interactable = enabled;
			}
		}

		public void Reset()
		{
			ClearSearch();
			_boundChestForToggle = null;
			SetNativeUiVisible(visible: false);
			SetChestToggleVisible(visible: false);
			RestoreLayout();
		}
	}
}
namespace UnifiedStorage.Mod.Shared
{
	public static class ReflectionHelpers
	{
		private static readonly FieldInfo? InventoryWidthField = typeof(Inventory).GetField("m_width", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo? InventoryHeightField = typeof(Inventory).GetField("m_height", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo? InventoryGetWidthMethod = typeof(Inventory).GetMethod("GetWidth", BindingFlags.Instance | BindingFlags.Public);

		private static readonly MethodInfo? InventoryGetHeightMethod = typeof(Inventory).GetMethod("GetHeight", BindingFlags.Instance | BindingFlags.Public);

		private static readonly MethodInfo? ContainerCheckAccessMethod = typeof(Container).GetMethod("CheckAccess", BindingFlags.Instance | BindingFlags.Public);

		private static readonly FieldInfo? ContainerNameField = typeof(Container).GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo? ContainerSetInUseMethod = typeof(Container).GetMethod("SetInUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(bool) }, null);

		private static readonly FieldInfo? ContainerInUseField = typeof(Container).GetField("m_inUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo? DragItemField = typeof(InventoryGui).GetField("m_dragItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo? ContainerRectField = AccessTools.Field(typeof(InventoryGui), "m_container");

		private static readonly FieldInfo? CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

		private static readonly FieldInfo? ContainerGridField = AccessTools.Field(typeof(InventoryGui), "m_containerGrid");

		private static readonly FieldInfo? ContainerNameTextField = AccessTools.Field(typeof(InventoryGui), "m_containerName");

		private static readonly FieldInfo? TakeAllButtonField = AccessTools.Field(typeof(InventoryGui), "m_takeAllButton");

		private static readonly FieldInfo? GridRootField = AccessTools.Field(typeof(InventoryGrid), "m_gridRoot");

		private static readonly FieldInfo? GridHeightField = AccessTools.Field(typeof(InventoryGrid), "m_height");

		private static readonly FieldInfo? GridElementPrefabField = AccessTools.Field(typeof(InventoryGrid), "m_elementPrefab");

		private static readonly FieldInfo? GridElementSpaceField = AccessTools.Field(typeof(InventoryGrid), "m_elementSpace");

		private static readonly FieldInfo? InventoryItemListField = AccessTools.Field(typeof(Inventory), "m_inventory");

		private static readonly MethodInfo? InventoryChangedMethod = typeof(Inventory).GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo?[] DropItemMethods = (from m in typeof(ItemDrop).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
			where string.Equals(m.Name, "DropItem", StringComparison.Ordinal)
			select m).ToArray();

		public static int GetInventoryWidth(Inventory inventory)
		{
			object obj;
			if (InventoryGetWidthMethod != null)
			{
				obj = InventoryGetWidthMethod.Invoke(inventory, null);
				if (obj is int)
				{
					return (int)obj;
				}
			}
			obj = InventoryWidthField?.GetValue(inventory);
			if (obj is int)
			{
				return (int)obj;
			}
			return 1;
		}

		public static int GetInventoryHeight(Inventory inventory)
		{
			object obj;
			if (InventoryGetHeightMethod != null)
			{
				obj = InventoryGetHeightMethod.Invoke(inventory, null);
				if (obj is int)
				{
					return (int)obj;
				}
			}
			obj = InventoryHeightField?.GetValue(inventory);
			if (obj is int)
			{
				return (int)obj;
			}
			return 1;
		}

		public static void SetInventorySize(Inventory inventory, int width, int height)
		{
			InventoryWidthField?.SetValue(inventory, width);
			InventoryHeightField?.SetValue(inventory, height);
		}

		public static bool CanAccess(Container container, Player player)
		{
			if (ContainerCheckAccessMethod == null)
			{
				return true;
			}
			ParameterInfo[] parameters = ContainerCheckAccessMethod.GetParameters();
			if (parameters.Length == 1 && parameters[0].ParameterType == typeof(long))
			{
				return (bool)ContainerCheckAccessMethod.Invoke(container, new object[1] { player.GetPlayerID() });
			}
			if (parameters.Length == 0)
			{
				return (bool)ContainerCheckAccessMethod.Invoke(container, null);
			}
			return true;
		}

		public static void SetContainerDisplayName(Container container, string displayName)
		{
			if (ContainerNameField?.FieldType == typeof(string))
			{
				ContainerNameField.SetValue(container, displayName);
			}
		}

		public static void ForceReleaseContainerUse(Container container)
		{
			try
			{
				ContainerSetInUseMethod?.Invoke(container, new object[1] { false });
			}
			catch
			{
			}
			try
			{
				if (ContainerInUseField?.FieldType == typeof(bool))
				{
					ContainerInUseField.SetValue(container, false);
				}
			}
			catch
			{
			}
		}

		public static string BuildContainerUid(Container container)
		{
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return ((Object)container).GetInstanceID().ToString();
			}
			return ((object)(ZDOID)(ref val.m_uid)).ToString();
		}

		public static bool MatchKey(ItemData item, ItemKey key)
		{
			if ((Object)(object)item?.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == ((ItemKey)(ref key)).PrefabName && item.m_quality == ((ItemKey)(ref key)).Quality && item.m_variant == ((ItemKey)(ref key)).Variant)
			{
				return item.m_stack > 0;
			}
			return false;
		}

		public static int GetTotalAmount(Inventory inventory, ItemKey key)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (MatchKey(allItem, key))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		public static int TryAddItemMeasured(Inventory inventory, ItemKey key, ItemData stack, int requestedAmount)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			int totalAmount = GetTotalAmount(inventory, key);
			inventory.AddItem(stack);
			int totalAmount2 = GetTotalAmount(inventory, key);
			return ChunkedTransfer.ClampMeasuredMove(requestedAmount, totalAmount, totalAmount2);
		}

		public static ItemData? CreateItemStack(ItemKey key, int amount)
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null);
			ItemDrop val = ((obj != null) ? obj.GetComponent<ItemDrop>() : null);
			if (val?.m_itemData == null)
			{
				return null;
			}
			ItemData obj2 = val.m_itemData.Clone();
			obj2.m_quality = ((ItemKey)(ref key)).Quality;
			obj2.m_variant = ((ItemKey)(ref key)).Variant;
			obj2.m_stack = amount;
			return obj2;
		}

		public static int GetMaxStackSize(ItemKey key)
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null);
			return (((obj != null) ? obj.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_maxStackSize).GetValueOrDefault(1);
		}

		public static int ResolveMaxStackSize(string prefabName)
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject obj = ((instance != null) ? instance.GetItemPrefab(prefabName) : null);
			return (((obj != null) ? obj.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_maxStackSize).GetValueOrDefault(1);
		}

		public static bool TryDropItem(ItemData item, int amount, Vector3 position, Quaternion rotation)
		{
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo[] dropItemMethods = DropItemMethods;
			foreach (MethodInfo methodInfo in dropItemMethods)
			{
				if (methodInfo == null)
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				try
				{
					if (parameters.Length == 4 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(Vector3) && parameters[3].ParameterType == typeof(Quaternion))
					{
						methodInfo.Invoke(null, new object[4] { item, amount, position, rotation });
						return true;
					}
					if (parameters.Length == 3 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(Vector3))
					{
						methodInfo.Invoke(null, new object[3] { item, amount, position });
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		public static int GetSubgroupOrder(ItemKey key)
		{
			if (string.IsNullOrWhiteSpace(((ItemKey)(ref key)).PrefabName))
			{
				return 999;
			}
			string text = ((ItemKey)(ref key)).PrefabName.ToLowerInvariant();
			if (text.Contains("ore") || text.Contains("scrap") || text.Contains("metal") || text.Contains("ingot") || text.Contains("bar"))
			{
				return 10;
			}
			if (text.Contains("wood") || text.Contains("stone"))
			{
				return 20;
			}
			if (text.Contains("hide") || text.Contains("leather") || text.Contains("scale") || text.Contains("chitin"))
			{
				return 30;
			}
			if (text.Contains("food") || text.Contains("mead") || text.Contains("stew") || text.Contains("soup") || text.Contains("bread"))
			{
				return 40;
			}
			return 100;
		}

		public static List<ItemData>? GetInventoryItemList(Inventory inventory)
		{
			return InventoryItemListField?.GetValue(inventory) as List<ItemData>;
		}

		public static void AddItemDirectly(Inventory inventory, ItemData item, int x, int y)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			item.m_gridPos = new Vector2i(x, y);
			List<ItemData> inventoryItemList = GetInventoryItemList(inventory);
			if (inventoryItemList != null)
			{
				inventoryItemList.Add(item);
			}
			else
			{
				inventory.AddItem(item);
			}
		}

		public static void NotifyInventoryChanged(Inventory inventory)
		{
			InventoryChangedMethod?.Invoke(inventory, null);
		}

		public static void ClearInventory(Inventory? inventory)
		{
			if (inventory == null)
			{
				return;
			}
			MethodInfo method = typeof(Inventory).GetMethod("RemoveAll", BindingFlags.Instance | BindingFlags.Public);
			if (method != null)
			{
				method.Invoke(inventory, null);
				return;
			}
			foreach (ItemData item in inventory.GetAllItems().ToList())
			{
				inventory.RemoveItem(item, item.m_stack);
			}
		}

		public static bool IsDragInProgress()
		{
			if ((Object)(object)InventoryGui.instance == (Object)null || DragItemField == null)
			{
				return false;
			}
			object? value = DragItemField.GetValue(InventoryGui.instance);
			ItemData val = (ItemData)((value is ItemData) ? value : null);
			if (val != null)
			{
				return val.m_stack > 0;
			}
			return false;
		}

		public static RectTransform? GetContainerRect(InventoryGui gui)
		{
			object? obj = ContainerRectField?.GetValue(gui);
			return (RectTransform?)((obj is RectTransform) ? obj : null);
		}

		public static Container? GetCurrentContainer(InventoryGui gui)
		{
			object? obj = CurrentContainerField?.GetValue(gui);
			return (Container?)((obj is Container) ? obj : null);
		}

		public static InventoryGrid? GetContainerGrid(InventoryGui gui)
		{
			object? obj = ContainerGridField?.GetValue(gui);
			return (InventoryGrid?)((obj is InventoryGrid) ? obj : null);
		}

		public static TMP_Text? GetContainerName(InventoryGui gui)
		{
			object? obj = ContainerNameTextField?.GetValue(gui);
			return (TMP_Text?)((obj is TMP_Text) ? obj : null);
		}

		public static Button? GetTakeAllButton(InventoryGui gui)
		{
			object? obj = TakeAllButtonField?.GetValue(gui);
			return (Button?)((obj is Button) ? obj : null);
		}

		public static RectTransform? GetGridRoot(InventoryGrid grid)
		{
			object? obj = GridRootField?.GetValue(grid);
			return (RectTransform?)((obj is RectTransform) ? obj : null);
		}

		public static int GetGridHeight(InventoryGrid grid)
		{
			object obj = GridHeightField?.GetValue(grid);
			if (obj is int)
			{
				return (int)obj;
			}
			return 4;
		}

		public static void SetGridHeight(InventoryGrid grid, int height)
		{
			GridHeightField?.SetValue(grid, height);
		}

		public static float GetGridElementSpace(InventoryGrid grid)
		{
			object obj = GridElementSpaceField?.GetValue(grid);
			if (obj is float)
			{
				return (float)obj;
			}
			return 2f;
		}

		public static GameObject? GetGridElementPrefab(InventoryGrid grid)
		{
			object? obj = GridElementPrefabField?.GetValue(grid);
			return (GameObject?)((obj is GameObject) ? obj : null);
		}
	}
}
namespace UnifiedStorage.Mod.Session
{
	public sealed class TerminalSessionService
	{
		private sealed class PendingReservation
		{
			public string TokenId { get; set; } = string.Empty;


			public ItemKey Key { get; set; }

			public int ReservedAmount { get; set; }

			public float ExpiresAt { get; set; }

			public bool CommitRequested { get; set; }
		}

		private sealed class PendingDeposit
		{
			public string RequestId { get; set; } = string.Empty;


			public ItemKey Key { get; set; }

			public int Amount { get; set; }
		}

		private struct CachedSortEntry
		{
			public string DisplayName;

			public int TypeOrder;

			public int SubgroupOrder;
		}

		private const float CloseGraceSeconds = 0.35f;

		private const float ReservationTtlSeconds = 3f;

		private const float TrackedChestRefreshSeconds = 1f;

		private const float OpenSessionRetryBaseSeconds = 0.5f;

		private const float OpenSessionRetryMaxSeconds = 8f;

		private readonly StorageConfig _config;

		private readonly IContainerScanner _scanner;

		private readonly TerminalRpcRoutes _routes;

		private readonly ManualLogSource _logger;

		private readonly StorageTrace _trace;

		private readonly Dictionary<ItemKey, int> _authoritativeTotals = new Dictionary<ItemKey, int>();

		private readonly Dictionary<ItemKey, int> _displayedTotals = new Dictionary<ItemKey, int>();

		private readonly Dictionary<ItemKey, ItemData> _prototypes = new Dictionary<ItemKey, ItemData>();

		private readonly Dictionary<ItemKey, int> _stackSizes = new Dictionary<ItemKey, int>();

		private readonly List<PendingReservation> _pendingReservations = new List<PendingReservation>();

		private readonly List<PendingDeposit> _pendingDeposits = new List<PendingDeposit>();

		private readonly Dictionary<ItemKey, CachedSortEntry> _sortCache = new Dictionary<ItemKey, CachedSortEntry>();

		private List<KeyValuePair<ItemKey, int>> _sortedItems = new List<KeyValuePair<ItemKey, int>>();

		private long _lastProjectedContentHash;

		private Container? _terminal;

		private Player? _player;

		private List<ChestHandle> _trackedChests = new List<ChestHandle>();

		private string _sessionId = string.Empty;

		private string _terminalUid = string.Empty;

		private string _searchQuery = string.Empty;

		private float _scanRadius;

		private float _pendingCloseSince = -1f;

		private float _nextTrackedChestRefreshAt;

		private float _nextSnapshotRetryAt;

		private bool _isApplyingProjection;

		private bool _hasAuthoritativeSnapshot;

		private int _slotsTotalPhysical;

		private int _chestCount;

		private long _revision;

		private int _openSessionFailureCount;

		private int _originalInventoryWidth;

		private int _originalInventoryHeight;

		private int _contentRows;

		private int _uiRevision;

		public bool IsActive
		{
			get
			{
				if ((Object)(object)_terminal != (Object)null)
				{
					return (Object)(object)_player != (Object)null;
				}
				return false;
			}
		}

		public bool IsApplyingProjection => _isApplyingProjection;

		public int SlotsTotalPhysical => _slotsTotalPhysical;

		public int ChestsInRange => _chestCount;

		public int UiRevision => _uiRevision;

		public int ContentRows => _contentRows;

		public bool IsStorageFull
		{
			get
			{
				if (_slotsTotalPhysical > 0)
				{
					return SlotsUsedVirtual >= _slotsTotalPhysical;
				}
				return false;
			}
		}

		public int SlotsUsedVirtual
		{
			get
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				int num = 0;
				foreach (KeyValuePair<ItemKey, int> authoritativeTotal in _authoritativeTotals)
				{
					if (authoritativeTotal.Value > 0)
					{
						int value;
						int num2 = ((!_stackSizes.TryGetValue(authoritativeTotal.Key, out value) || value <= 0) ? 1 : value);
						num += (int)Math.Ceiling((double)authoritativeTotal.Value / (double)num2);
					}
				}
				return num;
			}
		}

		public TerminalSessionService(StorageConfig config, IContainerScanner scanner, TerminalRpcRoutes routes, ManualLogSource logger, StorageTrace trace)
		{
			_config = config;
			_scanner = scanner;
			_routes = routes;
			_logger = logger;
			_trace = trace;
			_routes.SessionSnapshotReceived += OnSessionSnapshotReceived;
			_routes.ReserveResultReceived += OnReserveResultReceived;
			_routes.ApplyResultReceived += OnApplyResultReceived;
			_routes.SessionDeltaReceived += OnSessionDeltaReceived;
		}

		public bool IsTerminalInventory(Inventory inventory)
		{
			if (!IsActive || (Object)(object)_terminal == (Object)null || inventory == null)
			{
				return false;
			}
			return inventory == _terminal.GetInventory();
		}

		public bool IsTrackedInventory(Inventory inventory)
		{
			Inventory inventory2 = inventory;
			if (!IsActive || inventory2 == null || (Object)(object)_terminal == (Object)null)
			{
				return false;
			}
			Inventory inventory3 = _terminal.GetInventory();
			if (inventory2 == inventory3)
			{
				return false;
			}
			return _trackedChests.Any((ChestHandle chest) => inventory2 == chest.Container.GetInventory());
		}

		public bool HandleContainerInteract(Container container, Player player)
		{
			if (!UnifiedTerminal.IsTerminal(container))
			{
				return false;
			}
			BeginSession(container, player);
			return true;
		}

		public void BeginSession(Container terminal, Player player)
		{
			EndSession();
			ReflectionHelpers.ForceReleaseContainerUse(terminal);
			_terminal = terminal;
			_player = player;
			_searchQuery = string.Empty;
			_sessionId = string.Empty;
			_terminalUid = ReflectionHelpers.BuildContainerUid(terminal);
			_pendingCloseSince = -1f;
			_scanRadius = ((_config.TerminalRangeOverride.Value > 0f) ? _config.TerminalRangeOverride.Value : _config.ScanRadius.Value);
			CaptureOriginalInventorySize(terminal.GetInventory());
			_revision = 0L;
			_slotsTotalPhysical = 0;
			_chestCount = 0;
			_hasAuthoritativeSnapshot = false;
			_openSessionFailureCount = 0;
			_nextSnapshotRetryAt = Time.unscaledTime;
			_pendingReservations.Clear();
			_pendingDeposits.Clear();
			_authoritativeTotals.Clear();
			_displayedTotals.Clear();
			_prototypes.Clear();
			_stackSizes.Clear();
			StorageTrace trace = _trace;
			Player? player2 = _player;
			trace.Dev(string.Format("Terminal opened by {0} (radius {1:0.0}m).", ((player2 != null) ? player2.GetPlayerName() : null) ?? "unknown", _scanRadius));
			RefreshTrackedChestHandles();
			RefreshTerminalInventoryFromAuthoritative();
			RequestSessionSnapshot();
			_uiRevision++;
		}

		public void Tick()
		{
			if (!IsActive || (Object)(object)_terminal == (Object)null)
			{
				return;
			}
			if ((Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || !InventoryGui.instance.IsContainerOpen())
			{
				if (_pendingCloseSince < 0f)
				{
					_pendingCloseSince = Time.unscaledTime;
				}
				else if (Time.unscaledTime - _pendingCloseSince >= 0.35f)
				{
					EndSession();
				}
				return;
			}
			_pendingCloseSince = -1f;
			if (Time.unscaledTime >= _nextTrackedChestRefreshAt)
			{
				RefreshTrackedChestHandles();
			}
			if (!_hasAuthoritativeSnapshot && Time.unscaledTime >= _nextSnapshotRetryAt)
			{
				RequestSessionSnapshot();
			}
			ExpireLocalReservations();
			if (!ReflectionHelpers.IsDragInProgress())
			{
				CommitPendingReservations();
			}
		}

		public void SetSearchQuery(string query)
		{
			if (IsActive && !((Object)(object)_terminal == (Object)null))
			{
				string text = query?.Trim() ?? string.Empty;
				if (!string.Equals(text, _searchQuery, StringComparison.Ordinal))
				{
					_searchQuery = text;
					_lastProjectedContentHash = 0L;
					RefreshTerminalInventoryFromAuthoritative();
					_uiRevision++;
				}
			}
		}

		public void NotifyContainerInteraction()
		{
			if (!IsActive || (Object)(object)_terminal == (Object)null)
			{
				return;
			}
			if (!IsSessionTerminal(_terminal))
			{
				EndSession();
				return;
			}
			Dictionary<ItemKey, int> dictionary = CaptureCurrentDisplayedTotals();
			ApplyDisplayedDeltaAsOperations(dictionary);
			ReplaceDisplayedTotals(dictionary);
			if (!ReflectionHelpers.IsDragInProgress())
			{
				CommitPendingReservations();
			}
		}

		public void RefreshFromWorldChange()
		{
			if (IsActive && !((Object)(object)_terminal == (Object)null))
			{
				if (!IsSessionTerminal(_terminal))
				{
					EndSession();
				}
				else
				{
					RequestSessionSnapshot();
				}
			}
		}

		public void EndSession()
		{
			if (IsActive)
			{
				_trace.Dev("Terminal closed.");
			}
			if ((Object)(object)_terminal != (Object)null && !string.IsNullOrWhiteSpace(_terminalUid))
			{
				_routes.RequestCloseSession(new CloseSessionRequestDto
				{
					RequestId = Guid.NewGuid().ToString("N"),
					SessionId = _sessionId,
					TerminalUid = _terminalUid,
					PlayerId = ResolveLocalPlayerId()
				});
			}
			if ((Object)(object)_terminal != (Object)null)
			{
				ReflectionHelpers.ForceReleaseContainerUse(_terminal);
				ReflectionHelpers.ClearInventory(_terminal.GetInventory());
				RestoreTerminalInventorySize(_terminal.GetInventory());
			}
			_terminal = null;
			_player = null;
			_trackedChests.Clear();
			_sessionId = string.Empty;
			_terminalUid = string.Empty;
			_searchQuery = string.Empty;
			_scanRadius = 0f;
			_pendingCloseSince = -1f;
			_nextTrackedChestRefreshAt = 0f;
			_nextSnapshotRetryAt = 0f;
			_pendingReservations.Clear();
			_pendingDeposits.Clear();
			_authoritativeTotals.Clear();
			_displayedTotals.Clear();
			_prototypes.Clear();
			_stackSizes.Clear();
			_sortCache.Clear();
			_sortedItems.Clear();
			_lastProjectedContentHash = 0L;
			_slotsTotalPhysical = 0;
			_chestCount = 0;
			_revision = 0L;
			_hasAuthoritativeSnapshot = false;
			_openSessionFailureCount = 0;
			_contentRows = 0;
			_uiRevision++;
		}

		private void OnSessionSnapshotReceived(OpenSessionResponseDto response)
		{
			if (!IsActive)
			{
				return;
			}
			if (!response.Success)
			{
				if (IsIdentityFailure(response.Reason))
				{
					EndSession();
					return;
				}
				_openSessionFailureCount = Math.Min(_openSessionFailureCount + 1, 8);
				_nextSnapshotRetryAt = Time.unscaledTime + GetRetryDelay(_openSessionFailureCount);
			}
			else if (CanApplySnapshot(response.Snapshot))
			{
				ApplySnapshot(response.Snapshot);
			}
		}

		private void OnReserveResultReceived(ReserveWithdrawResultDto result)
		{
			//IL_0050: 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)
			if (IsActive && CanApplySnapshot(result.Snapshot))
			{
				if (result.Success && !string.IsNullOrWhiteSpace(result.TokenId) && result.ReservedAmount > 0)
				{
					_trace.Dev($"Withdrew {result.ReservedAmount}x {GetDisplayName(result.Key)}.");
					_pendingReservations.Add(new PendingReservation
					{
						TokenId = result.TokenId,
						Key = result.Key,
						ReservedAmount = result.ReservedAmount,
						ExpiresAt = Time.unscaledTime + 3f
					});
				}
				ApplySnapshot(result.Snapshot);
			}
		}

		private void OnApplyResultReceived(ApplyResultDto result)
		{
			if (!IsActive)
			{
				return;
			}
			if (string.Equals(result.OperationType, "deposit", StringComparison.Ordinal))
			{
				HandleDepositResult(result);
			}
			if (string.Equals(result.OperationType, "commit", StringComparison.Ordinal))
			{
				if (result.Success)
				{
					RemovePendingToken(result.TokenId);
				}
				else
				{
					MarkPendingCommitAsRetryable(result.TokenId);
					RequestSessionSnapshot();
				}
			}
			else if (string.Equals(result.OperationType, "cancel", StringComparison.Ordinal) && !result.Success)
			{
				RemovePendingToken(result.TokenId);
				RequestSessionSnapshot();
			}
			if (CanApplySnapshot(result.Snapshot))
			{
				ApplySnapshot(result.Snapshot);
			}
			else if (!result.Success)
			{
				RequestSessionSnapshot();
			}
		}

		private void OnSessionDeltaReceived(SessionDeltaDto delta)
		{
			if (IsActive && string.Equals(delta.TerminalUid, _terminalUid, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(delta.SessionId) || string.IsNullOrWhiteSpace(_sessionId) || string.Equals(delta.SessionId, _sessionId, StringComparison.Ordinal)) && delta.Revision >= _revision)
			{
				ApplySnapshot(delta.Snapshot);
			}
		}

		private bool CanApplySnapshot(SessionSnapshotDto snapshot)
		{
			if (!IsActive || (Object)(object)_terminal == (Object)null)
			{
				return false;
			}
			if (!string.Equals(snapshot.TerminalUid, _terminalUid, StringComparison.Ordinal))
			{
				return false;
			}
			if (!string.IsNullOrWhiteSpace(snapshot.SessionId) && !string.IsNullOrWhiteSpace(_sessionId) && !string.Equals(snapshot.SessionId, _sessionId, StringComparison.Ordinal))
			{
				return false;
			}
			return true;
		}

		private void ApplySnapshot(SessionSnapshotDto snapshot)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			if (!CanApplySnapshot(snapshot) || snapshot.Revision < _revision)
			{
				return;
			}
			if (!string.IsNullOrWhiteSpace(snapshot.SessionId))
			{
				_sessionId = snapshot.SessionId;
			}
			_revision = snapshot.Revision;
			_slotsTotalPhysical = snapshot.SlotsTotalPhysical;
			_chestCount = snapshot.ChestCount;
			_hasAuthoritativeSnapshot = true;
			_openSessionFailureCount = 0;
			_nextSnapshotRetryAt = Time.unscaledTime + 60f;
			int count = _authoritativeTotals.Count;
			_authoritativeTotals.Clear();
			_stackSizes.Clear();
			foreach (AggregatedItem item in snapshot.Items)
			{
				if (item.TotalAmount <= 0)
				{
					continue;
				}
				_authoritativeTotals[item.Key] = item.TotalAmount;
				_stackSizes[item.Key] = ((item.StackSize <= 0) ? 1 : item.StackSize);
				if (!_prototypes.ContainsKey(item.Key))
				{
					ObjectDB instance = ObjectDB.instance;
					object obj;
					ItemKey key;
					if (instance == null)
					{
						obj = null;
					}
					else
					{
						key = item.Key;
						obj = instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName);
					}
					ItemDrop val = ((obj != null) ? ((GameObject)obj).GetComponent<ItemDrop>() : null);
					if (val?.m_itemData != null)
					{
						ItemData val2 = val.m_itemData.Clone();
						key = item.Key;
						val2.m_quality = ((ItemKey)(ref key)).Quality;
						key = item.Key;
						val2.m_variant = ((ItemKey)(ref key)).Variant;
						_prototypes[item.Key] = val2;
					}
				}
			}
			if (_authoritativeTotals.Count != count)
			{
				_sortCache.Clear();
			}
			_trace.Dev($"Storage updated: {_chestCount} chest(s), {_authoritativeTotals.Count} item type(s).");
			RefreshTrackedChestHandles();
			RefreshTerminalInventoryFromAuthoritative();
			_uiRevision++;
		}

		private void RefreshTerminalInventoryFromAuthoritative()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_terminal == (Object)null)
			{
				return;
			}
			Inventory inventory = _terminal.GetInventory();
			if (inventory == null)
			{
				return;
			}
			long num = ComputeContentHash();
			if (num == _lastProjectedContentHash)
			{
				return;
			}
			_lastProjectedContentHash = num;
			_isApplyingProjection = true;
			try
			{
				ReflectionHelpers.ClearInventory(inventory);
				_displayedTotals.Clear();
				int num2 = Math.Max(1, ReflectionHelpers.GetInventoryWidth(inventory));
				RebuildSortedItems();
				int num3 = 0;
				foreach (KeyValuePair<ItemKey, int> sortedItem in _sortedItems)
				{
					int value;
					int num4 = ((!_stackSizes.TryGetValue(sortedItem.Key, out value) || value <= 0) ? 1 : value);
					num3 += (int)Math.Ceiling((double)sortedItem.Value / (double)num4);
				}
				int num5 = ((_slotsTotalPhysical > SlotsUsedVirtual) ? 1 : 0);
				int num6 = Math.Max(1, num3 + num5);
				_contentRows = Math.Max(1, (int)Math.Ceiling((float)num6 / (float)num2));
				ReflectionHelpers.SetInventorySize(inventory, num2, _contentRows);
				int num7 = 0;
				foreach (KeyValuePair<ItemKey, int> sortedItem2 in _sortedItems)
				{
					int num8 = sortedItem2.Value;
					int value2;
					int num9 = ((!_stackSizes.TryGetValue(sortedItem2.Key, out value2) || value2 <= 0) ? 1 : value2);
					while (num8 > 0)
					{
						int num10 = Math.Min(num9, num8);
						ItemData val = CreateProjectedItem(sortedItem2.Key, num10, num9);
						if (val == null)
						{
							break;
						}
						int x = num7 % num2;
						int y = num7 / num2;
						ReflectionHelpers.AddItemDirectly(inventory, val, x, y);
						num7++;
						num8 -= num10;
					}
					_displayedTotals[sortedItem2.Key] = sortedItem2.Value - num8;
				}
				ReflectionHelpers.NotifyInventoryChanged(inventory);
			}
			finally
			{
				_isApplyingProjection = false;
			}
		}

		private long ComputeContentHash()
		{
			//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)
			long num = 17L;
			foreach (KeyValuePair<ItemKey, int> authoritativeTotal in _authoritativeTotals)
			{
				long num2 = num * 31;
				ItemKey key = authoritativeTotal.Key;
				num = num2 + ((object)(ItemKey)(ref key)).GetHashCode();
				num = num * 31 + authoritativeTotal.Value;
			}
			return num * 31 + _searchQuery.GetHashCode();
		}

		private void RebuildSortedItems()
		{
			//IL_002e: 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)
			_sortedItems.Clear();
			foreach (KeyValuePair<ItemKey, int> authoritativeTotal in _authoritativeTotals)
			{
				if (authoritativeTotal.Value > 0)
				{
					EnsureSortCacheEntry(authoritativeTotal.Key);
					if (MatchesSearch(_sortCache[authoritativeTotal.Key].DisplayName))
					{
						_sortedItems.Add(authoritativeTotal);
					}
				}
			}
			_sortedItems.Sort(delegate(KeyValuePair<ItemKey, int> a, KeyValuePair<ItemKey, int> b)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				CachedSortEntry cachedSortEntry = _sortCache[a.Key];
				CachedSortEntry cachedSortEntry2 = _sortCache[b.Key];
				int num = cachedSortEntry.TypeOrder.CompareTo(cachedSortEntry2.TypeOrder);
				if (num != 0)
				{
					return num;
				}
				num = cachedSortEntry.SubgroupOrder.CompareTo(cachedSortEntry2.SubgroupOrder);
				if (num != 0)
				{
					return num;
				}
				num = string.Compare(cachedSortEntry.DisplayName, cachedSortEntry2.DisplayName, StringComparison.OrdinalIgnoreCase);
				return (num != 0) ? num : b.Value.CompareTo(a.Value);
			});
		}

		private void EnsureSortCacheEntry(ItemKey key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!_sortCache.ContainsKey(key))
			{
				_sortCache[key] = new CachedSortEntry
				{
					DisplayName = GetDisplayName(key),
					TypeOrder = GetItemTypeOrder(key),
					SubgroupOrder = ReflectionHelpers.GetSubgroupOrder(key)
				};
			}
		}

		private ItemData? CreateProjectedItem(ItemKey key, int amount, int maxStackSize)
		{
			//IL_0006: 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)
			if (!_prototypes.TryGetValue(key, out ItemData value))
			{
				ObjectDB instance = ObjectDB.instance;
				GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null);
				ItemDrop val = ((obj != null) ? obj.GetComponent<ItemDrop>() : null);
				if (val?.m_itemData == null)
				{
					return null;
				}
				value = val.m_itemData.Clone();
				value.m_quality = ((ItemKey)(ref key)).Quality;
				value.m_variant = ((ItemKey)(ref key)).Variant;
				_prototypes[key] = value;
			}
			ItemData obj2 = value.Clone();
			obj2.m_stack = amount;
			return obj2;
		}

		private bool MatchesSearch(string displayName)
		{
			if (!string.IsNullOrWhiteSpace(_searchQuery))
			{
				return displayName.IndexOf(_searchQuery, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private void RefreshTrackedChestHandles()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_terminal == (Object)null)
			{
				_trackedChests.Clear();
				_nextTrackedChestRefreshAt = Time.unscaledTime + 1f;
			}
			else
			{
				_trackedChests = _scanner.GetNearbyContainers(((Component)_terminal).transform.position, _scanRadius, _terminal).ToList();
				_nextTrackedChestRefreshAt = Time.unscaledTime + 1f;
			}
		}

		private void ApplyDisplayedDeltaAsOperations(Dictionary<ItemKey, int> currentDisplayed)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			HashSet<ItemKey> hashSet = new HashSet<ItemKey>(_displayedTotals.Keys);
			hashSet.UnionWith(currentDisplayed.Keys);
			foreach (ItemKey item in hashSet)
			{
				int value;
				int num = (_displayedTotals.TryGetValue(item, out value) ? value : 0);
				int value2;
				int num2 = (currentDisplayed.TryGetValue(item, out value2) ? value2 : 0) - num;
				if (num2 < 0)
				{
					RequestReserveWithdraw(item, -num2);
				}
				else if (num2 > 0)
				{
					RequestCancelAndOrDeposit(item, num2);
				}
			}
		}

		private void RequestReserveWithdraw(ItemKey key, int amount)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (amount > 0)
			{
				_routes.RequestReserveWithdraw(new ReserveWithdrawRequestDto
				{
					RequestId = Guid.NewGuid().ToString("N"),
					SessionId = _sessionId,
					OperationId = Guid.NewGuid().ToString("N"),
					TerminalUid = _terminalUid,
					PlayerId = ResolveLocalPlayerId(),
					ExpectedRevision = _revision,
					Key = key,
					Amount = amount
				});
			}
		}

		private void RequestCancelAndOrDeposit(ItemKey key, int amount)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			if (amount <= 0)
			{
				return;
			}
			int num = amount;
			foreach (PendingReservation item in _pendingReservations.Where(delegate(PendingReservation r)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				ItemKey key2 = r.Key;
				return ((ItemKey)(ref key2)).Equals(key) && r.ReservedAmount > 0;
			}).ToList())
			{
				if (num <= 0)
				{
					break;
				}
				int num2 = Math.Min(num, item.ReservedAmount);
				if (num2 > 0)
				{
					_routes.RequestCancelReservation(new CancelReservationRequestDto
					{
						RequestId = Guid.NewGuid().ToString("N"),
						SessionId = _sessionId,
						OperationId = Guid.NewGuid().ToString("N"),
						TerminalUid = _terminalUid,
						PlayerId = ResolveLocalPlayerId(),
						TokenId = item.TokenId,
						Amount = num2
					});
					item.ReservedAmount -= num2;
					num -= num2;
					if (item.ReservedAmount <= 0)
					{
						_pendingReservations.Remove(item);
					}
				}
			}
			if (num > 0)
			{
				string requestId = Guid.NewGuid().ToString("N");
				_pendingDeposits.Add(new PendingDeposit
				{
					RequestId = requestId,
					Key = key,
					Amount = num
				});
				_routes.RequestDeposit(new DepositRequestDto
				{
					RequestId = requestId,
					SessionId = _sessionId,
					OperationId = Guid.NewGuid().ToString("N"),
					TerminalUid = _terminalUid,
					PlayerId = ResolveLocalPlayerId(),
					ExpectedRevision = _revision,
					Key = key,
					Amount = num
				});
			}
		}

		private void CommitPendingReservations()
		{
			foreach (PendingReservation item in _pendingReservations.Where((PendingReservation r) => !r.CommitRequested && r.ReservedAmount > 0).ToList())
			{
				_routes.RequestCommitReservation(new CommitReservationRequestDto
				{
					RequestId = Guid.NewGuid().ToString("N"),
					SessionId = _sessionId,
					OperationId = Guid.NewGuid().ToString("N"),
					TerminalUid = _terminalUid,
					TokenId = item.TokenId
				});
				item.CommitRequested = true;
			}
		}

		private void ExpireLocalReservations()
		{
			float unscaledTime = Time.unscaledTime;
			bool flag = false;
			for (int num = _pendingReservations.Count - 1; num >= 0; num--)
			{
				if (!(_pendingReservations[num].ExpiresAt > unscaledTime))
				{
					_pendingReservations.RemoveAt(num);
					flag = true;
				}
			}
			if (flag)
			{
				RequestSessionSnapshot();
			}
		}

		private void RequestSessionSnapshot()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			if (IsActive && !((Object)(object)_terminal == (Object)null))
			{
				_routes.RequestOpenSession(new OpenSessionRequestDto
				{
					RequestId = Guid.NewGuid().ToString("N"),
					SessionId = _sessionId,
					TerminalUid = _terminalUid,
					PlayerId = ResolveLocalPlayerId(),
					AnchorX = ((Component)_terminal).transform.position.x,
					AnchorY = ((Component)_terminal).transform.position.y,
					AnchorZ = ((Component)_terminal).transform.position.z,
					Radius = _scanRadius
				});
				_nextSnapshotRetryAt = Time.unscaledTime + 0.5f;
			}
		}

		private Dictionary<ItemKey, int> CaptureCurrentDisplayedTotals()
		{
			//IL_0079: 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)
			Dictionary<ItemKey, int> dictionary = new Dictionary<ItemKey, int>();
			if ((Object)(object)_terminal == (Object)null)
			{
				return dictionary;
			}
			Inventory inventory = _terminal.GetInventory();
			if (inventory == null)
			{
				return dictionary;
			}
			ItemKey key = default(ItemKey);
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && allItem.m_stack > 0)
				{
					((ItemKey)(ref key))..ctor(((Object)allItem.m_dropPrefab).name, allItem.m_quality, allItem.m_variant);
					dictionary[key] = (dictionary.TryGetValue(key, out var value) ? (value + allItem.m_stack) : allItem.m_stack);
				}
			}
			return dictionary;
		}

		private void ReplaceDisplayedTotals(Dictionary<ItemKey, int> totals)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			_displayedTotals.Clear();
			foreach (KeyValuePair<ItemKey, int> total in totals)
			{
				_displayedTotals[total.Key] = total.Value;
			}
		}

		private bool IsSessionTerminal(Container container)
		{
			if ((Object)(object)container != (Object)null && !string.IsNullOrWhiteSpace(_terminalUid))
			{
				return string.Equals(ReflectionHelpers.BuildContainerUid(container), _terminalUid, StringComparison.Ordinal);
			}
			return false;
		}

		private void HandleDepositResult(ApplyResultDto result)
		{
			//IL_0060: 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)
			ApplyResultDto result2 = result;
			PendingDeposit pendingDeposit = _pendingDeposits.FirstOrDefault((PendingDeposit p) => string.Equals(p.RequestId, result2.RequestId, StringComparison.Ordinal));
			if (pendingDeposit != null)
			{
				_pendingDeposits.Remove(pendingDeposit);
				if (result2.Success)
				{
					_trace.Dev($"Deposited {result2.AppliedAmount}x {GetDisplayName(pendingDeposit.Key)}.");
				}
				else if (ShouldRestoreFailedDeposit(result2.Reason))
				{
					RestoreToLocalPlayerInventory(pendingDeposit.Key, pendingDeposit.Amount);
				}
			}
		}

		private void RestoreToLocalPlayerInventory(ItemKey key, int amount)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (amount > 0 && !((Object)(object)_player == (Object)null))
			{
				int num = ReflectionHelpers.GetMaxStackSize(key);
				if (num <= 0)
				{
					num = 1;
				}
				Inventory inventory = ((Humanoid)_player).GetInventory();
				int num2 = ChunkedTransfer.Move(amount, num, (Func<int, int>)delegate(int chunkAmount)
				{
					//IL_0001: Unknown result type (might be due to invalid IL or missing references)
					//IL_0019: Unknown result type (might be due to invalid IL or missing references)
					ItemData val = ReflectionHelpers.CreateItemStack(key, chunkAmount);
					return (val != null) ? ReflectionHelpers.TryAddItemMeasured(inventory, key, val, chunkAmount) : 0;
				});
				int num3 = amount - num2;
				if (num3 > 0)
				{
					DropNearPlayer(key, num3);
				}
			}
		}

		private void DropNearPlayer(ItemKey key, int amount)
		{
			//IL_0013: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_player == (Object)null || amount <= 0)
			{
				return;
			}
			int num = ReflectionHelpers.GetMaxStackSize(key);
			if (num <= 0)
			{
				num = 1;
			}
			int num2 = amount;
			while (num2 > 0)
			{
				int num3 = Math.Min(num, num2);
				ItemData val = ReflectionHelpers.CreateItemStack(key, num3);
				if (val != null && ReflectionHelpers.TryDropItem(val, num3, ((Component)_player).transform.position + ((Component)_player).transform.forward + Vector3.up, Quaternion.identity))
				{
					num2 -= num3;
					continue;
				}
				break;
			}
		}

		private void RemovePendingToken(string tokenId)
		{
			string tokenId2 = tokenId;
			if (!string.IsNullOrWhiteSpace(tokenId2))
			{
				_pendingReservations.RemoveAll((PendingReservation r) => string.Equals(r.TokenId, tokenId2, StringComparison.Ordinal));
			}
		}

		private void MarkPendingCommitAsRetryable(string tokenId)
		{
			string tokenId2 = tokenId;
			foreach (PendingReservation item in _pendingReservations.Where((PendingReservation r) => string.Equals(r.TokenId, tokenId2, StringComparison.Ordinal)))
			{
				item.CommitRequested = false;
			}
		}

		private long ResolveLocalPlayerId()
		{
			if (!((Object)(object)_player != (Object)null))
			{
				return 0L;
			}
			return _player.GetPlayerID();
		}

		private string GetDisplayName(ItemKey key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (_prototypes.TryGetValue(key, out ItemData value))
			{
				string name = value.m_shared.m_name;
				Localization instance = Localization.instance;
				string text = ((instance != null) ? instance.Localize(name) : null);
				if (string.IsNullOrEmpty(text))
				{
					return name;
				}
				return text;
			}
			return ((ItemKey)(ref key)).PrefabName;
		}

		private int GetItemTypeOrder(ItemKey key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected I4, but got Unknown
			if (_prototypes.TryGetValue(key, out ItemData value))
			{
				return (int)value.m_shared.m_itemType;
			}
			return int.MaxValue;
		}

		private void CaptureOriginalInventorySize(Inventory? inventory)
		{
			if (inventory == null)
			{
				_originalInventoryWidth = 8;
				_originalInventoryHeight = 4;
			}
			else
			{
				_originalInventoryWidth = ReflectionHelpers.GetInventoryWidth(inventory);
				_originalInventoryHeight = ReflectionHelpers.GetInventoryHeight(inventory);
			}
		}

		private void RestoreTerminalInventorySize(Inventory? inventory)
		{
			if (inventory != null && _originalInventoryWidth > 0 && _originalInventoryHeight > 0)
			{
				ReflectionHelpers.SetInventorySize(inventory, _originalInventoryWidth, _originalInventoryHeight);
			}
		}

		private static bool ShouldRestoreFailedDeposit(string reason)
		{
			if (!string.IsNullOrWhiteSpace(reason))
			{
				switch (reason)
				{
				default:
					return reason == "Unable to resolve player identity";
				case "Conflict":
				case "Player not found":
				case "Player has no matching item":
				case "Player/terminal has no matching item":
				case "Session not found":
				case "No storage space":
				case "Player identity mismatch":
					return true;
				}
			}
			return false;
		}

		private static bool IsIdentityFailure(string reason)
		{
			if (!(reason == "Player identity mismatch"))
			{
				return reason == "Unable to resolve player identity";
			}
			return true;
		}

		private static float GetRetryDelay(int failureCount)
		{
			if (failureCount <= 0)
			{
				return 0.5f;
			}
			int num = Math.Min(6, failureCount - 1);
			float num2 = 0.5f * Mathf.Pow(2f, (float)num);
			return Mathf.Min(8f, num2);
		}
	}
}
namespace UnifiedStorage.Mod.Server
{
	public static class ChestInclusionRules
	{
		private const string IncludeInUnifiedKey = "US_IncludeInUnified";

		private static readonly HashSet<string> VanillaChestPrefabNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "piece_chest", "piece_chest_wood", "piece_chest_private", "piece_chest_blackmetal" };

		public static bool IsVanillaChest(Container? container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			string item = NormalizePrefabName(((Object)((Component)container).gameObject).name);
			return VanillaChestPrefabNames.Contains(item);
		}

		public static bool IsIncludedInUnified(Container? container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return true;
			}
			return val.GetBool("US_IncludeInUnified", true);
		}

		public static bool TrySetIncludedInUnified(Container? container, bool includeInUnified)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return false;
			}
			try
			{
				if ((Object)(object)component != (Object)null && !component.IsOwner())
				{
					component.ClaimOwnership();
				}
			}
			catch
			{
			}
			val.Set("US_IncludeInUnified", includeInUnified);
			return true;
		}

		private static string NormalizePrefabName(string name)
		{
			if (name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name.Substring(0, name.Length - "(Clone)".Length);
			}
			return name;
		}
	}
	public sealed class ContainerScanner : IContainerScanner
	{
		private readonly StorageConfig _config;

		public ContainerScanner(StorageConfig config)
		{
			_config = config;
		}

		public IReadOnlyList<ChestHandle> GetNearbyContainers(Vector3 center, float radius, Container? ignoreContainer = null)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			Container ignoreContainer2 = ignoreContainer;
			int value = _config.MaxContainersScanned.Value;
			return (from handle in (from container in (from container in Object.FindObjectsByType<Container>((FindObjectsSortMode)0)
						where (Object)(object)container != (Object)null
						select container).Where(IsStaticChest)
					where (Object)(object)ignoreContainer2 == (Object)null || (Object)(object)container != (Object)(object)ignoreContainer2
					where !UnifiedTerminal.IsTerminal(container)
					select container).Where(ChestInclusionRules.IsIncludedInUnified).Select(delegate(Container container)
				{
					//IL_0001: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					float distance = Vector3.Distance(center, ((Component)container).transform.position);
					return new ChestHandle(BuildSourceId(container), container, distance);
				})
				where handle.Distance <= radius
				orderby handle.Distance, handle.SourceId
				select handle).Take(value).ToList();
		}

		private static bool IsStaticChest(Container container)
		{
			if (!((Component)container).gameObject.activeInHierarchy)
			{
				return false;
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Vagon>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
			{
				return false;
			}
			return true;
		}

		private static string BuildSourceId(Container container)
		{
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val != null)
			{
				return ((object)(ZDOID)(ref val.m_uid)).ToString();
			}
			return ((Object)container).GetInstanceID().ToString();
		}
	}
	public sealed class TerminalAuthorityService
	{
		private sealed class TerminalState
		{
			public string SessionId { get; set; } = string.Empty;


			public string TerminalUid { get; set; } = string.Empty;


			public Vector3 AnchorPosition { get; set; }

			public float Radius { get; set; }

			public long Revision { get; set; }

			public int SlotsTotalPhysical { get; set; }

			public int ChestCount { get; set; }

			public List<ChestHandle> Chests { get; set; } = new List<ChestHandle>();


			public HashSet<long> Subscribers { get; } = new HashSet<long>();


			public Dictionary<long, long> PeerPlayerIds { get; } = new Dictionary<long, long>();


			public Dictionary<string, ReservationRecord> Reservations { get; } = new Dictionary<string, ReservationRecord>(StringComparer.Ordinal);


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


			public Queue<string> OperationOrder { get; } = new Queue<string>();


			public bool SnapshotDirty { get; set; } = true;


			public float CachedSnapshotAt { get; set; }

			public SessionSnapshotDto? CachedSnapshot { get; set; }
		}

		private sealed class ReservationRecord
		{
			public string TokenId { get; set; } = string.Empty;


			public string SessionId { get; set; } = string.Empty;


			public long PeerId { get; set; }

			public ItemKey Key { get; set; }

			public int Amount { get; set; }

			public float ExpiresAt { get; set; }
		}

		private const float ReservationTtlSeconds = 3f;

		private const float SnapshotCacheSeconds = 0.2f;

		private const int MaxOperationHistory = 2048;

		private readonly object _sync = new object();

		private readonly StorageConfig _config;

		private readonly IContainerScanner _scanner;

		private readonly ManualLogSource _logger;

		private readonly Dictionary<string, TerminalState> _states = new Dictionary<string, TerminalState>(StringComparer.Ordinal);

		public event Action<IReadOnlyCollection<long>, SessionDeltaDto>? DeltaReady;

		public TerminalAuthorityService(StorageConfig config, IContainerScanner scanner, ManualLogSource logger)
		{
			_config = config;
			_scanner = scanner;
			_logger = logger;
		}

		public void Tick()
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			List<(IReadOnlyCollection<long>, SessionDeltaDto)> list = new List<(IReadOnlyCollection<long>, SessionDeltaDto)>();
			lock (_sync)
			{
				if (_states.Count == 0)
				{
					return;
				}
				float now = Time.unscaledTime;
				foreach (TerminalState item in _states.Values.ToList())
				{
					item.Subscribers.RemoveWhere((long peer) => !IsPeerConnected(peer));
					foreach (long item2 in item.PeerPlayerIds.Keys.Where((long peer) => !IsPeerConnected(peer)).ToList())
					{
						item.PeerPlayerIds.Remove(item2);
					}
					List<ReservationRecord> list2 = item.Reservations.Values.Where((ReservationRecord r) => r.ExpiresAt <= now || !IsPeerConnected(r.PeerId)).ToList();
					bool flag = false;
					foreach (ReservationRecord item3 in list2)
					{
						int num = AddToChests(item, item3.Key, item3.Amount, null);
						int num2 = item3.Amount - num;
						if (num2 > 0)
						{
							DropNearTerminal(item, item3.Key, num2);
						}
						item.Reservations.Remove(item3.TokenId);
						flag = flag || num > 0 || num2 > 0;
					}
					if (flag)
					{
						MarkSnapshotDirty(item);
						item.Revision++;
						SessionSnapshotDto snapshot = BuildSnapshot(item);
						list.Add((item.Subscribers.ToList(), BuildDelta(item, snapshot)));
					}
					if (item.Subscribers.Count == 0 && item.Reservations.Count == 0)
					{
						_states.Remove(item.TerminalUid);
					}
				}
			}
			EmitDeltas(list);
		}

		public OpenSessionResponseDto HandleOpenSession(long sender, OpenSessionRequestDto request)
		{
			lock (_sync)
			{
				if (string.IsNullOrWhiteSpace(request.TerminalUid))
				{
					return new OpenSessionResponseDto
					{
						RequestId = request.RequestId,
						Success = false,
						Reason = "Invalid terminal"
					};
				}
				TerminalState orCreateState = GetOrCreateState(request);
				if (!TryResolveAndBindPlayerId(orCreateState, sender, request.PlayerId, out var resolvedPlayerId))
				{
					return new OpenSessionResponseDto
					{
						RequestId = request.RequestId,
						Success = false,
						Reason = "Player identity mismatch"
					};
				}
				if (resolvedPlayerId <= 0)
				{
					return new OpenSessionResponseDto
					{
						RequestId = request.RequestId,
						Success = false,
						Reason = "Unable to resolve player identity"
					};
				}
				orCreateState.Subscribers.Add(sender);
				SessionSnapshotDto snapshot = BuildSnapshot(orCreateState);
				return new OpenSessionResponseDto
				{
					RequestId = request.RequestId,
					Success = true,
					Snapshot = snapshot
				};
			}
		}

		public ReserveWithdrawResultDto HandleReserveWithdraw(long sender, ReserveWithdrawRequestDto request)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			List<(IReadOnlyCollection<long>, SessionDeltaDto)> list = new List<(IReadOnlyCollection<long>, SessionDeltaDto)>();
			ReserveWithdrawResultDto result;
			lock (_sync)
			{
				TerminalState state = GetState(request.TerminalUid);
				if (state == null)
				{
					return ReserveFailure(request, "Session not found", null, 0L);
				}
				if (IsDuplicateOperation(state, request.OperationId))
				{
					return new ReserveWithdrawResultDto
					{
						RequestId = request.RequestId,
						Success = true,
						Reason = "Duplicate",
						Key = request.Key,
						Revision = state.Revision,
						Snapshot = BuildSnapshot(state)
					};
				}
				if (request.ExpectedRevision >= 0 && request.ExpectedRevision != state.Revision)
				{
					return ReserveFailure(request, "Conflict", BuildSnapshot(state), state.Revision);
				}
				if (!TryGetMappedPlayerId(state, sender, request.PlayerId, out var playerId))
				{
					return ReserveFailure(request, "Player identity mismatch", BuildSnapshot(state), state.Revision);
				}
				Player player = FindPlayerById(playerId);
				int num = RemoveFromChests(state, request.Key, request.Amount, player);
				if (num <= 0)
				{
					RememberOperation(state, request.OperationId);
					return ReserveFailure(request, "Not enough items", BuildSnapshot(state), state.Revision);
				}
				string text = Guid.NewGuid().ToString("N");
				state.Reservations[text] = new ReservationRecord
				{
					TokenId = text,
					PeerId = sender,
					SessionId = (request.SessionId ?? string.Empty),
					Key = request.Key,
					Amount = num,
					ExpiresAt = Time.unscaledTime + 3f
				};
				state.Revision++;
				MarkSnapshotDirty(state);
				RememberOperation(state, request.OperationId);
				SessionSnapshotDto snapshot = BuildSnapshot(state);
				list.Add((state.Subscribers.ToList(), BuildDelta(state, snapshot)));
				result = new ReserveWithdrawResultDto
				{
					RequestId = request.RequestId,
					Success = true,
					TokenId = text,
					Key = request.Key,
					ReservedAmount = num,
					Revision = state.Revision,
					Snapshot = snapshot
				};
			}
			EmitDeltas(list);
			return result;
		}

		public ApplyResultDto HandleCommitReservation(long sender, CommitReservationRequestDto request)
		{
			lock (_sync)
			{
				TerminalState state = GetState(request.TerminalUid);
				if (state == null)
				{
					return ApplyFailure(request.RequestId, request.TokenId ?? "", "Session not found", null, 0L, "commit");
				}
				if (IsDuplicateOperation(state, request.OperationId))
				{
					return new ApplyResultDto
					{
						RequestId = request.RequestId,
						Success = true,