Decompiled source of BestAutoSort v0.6.28

plugins/BestAutoSort.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BestAutoSort;
using BestAutoSort.Core;
using BestAutoSort.Patches;
using BestAutoSort.Runtime;
using BestAutoSort.Tx;
using BestAutoSort.TxCore;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("maks2204")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.6.1.0")]
[assembly: AssemblyInformationalVersion("0.6.1")]
[assembly: AssemblyProduct("BestAutoSort")]
[assembly: AssemblyTitle("BestAutoSort")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.6.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[HarmonyPatch(typeof(Container), "RPC_OpenResponse")]
internal static class TxContainerOpenResponsePatch
{
	private static void Postfix(Container __instance, long uid, bool granted)
	{
		if (!granted && Plugin.IsActive)
		{
			Plugin.LogInstance.LogInfo((object)"[ChestTX] open denied by manager (busy/lease/access?)");
		}
	}
}
namespace BestAutoSort
{
	[BepInPlugin("dev.maks2204.bestautosort", "BestAutoSort", "0.6.1")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	[BepInIncompatibility("goldenrevolver.quick_stack_store")]
	[BepInIncompatibility("com.maxsch.valheim.MultiUserChest")]
	[BepInIncompatibility("Narolith.AutoFeed")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal const string PluginGuid = "dev.maks2204.bestautosort";

		internal const string PluginName = "BestAutoSort";

		internal const string PluginVersion = "0.6.28";

		private Harmony? _harmony;

		private bool _shutDown;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource LogInstance { get; private set; }

		internal QuickStackService QuickStackService { get; private set; }

		internal static bool IsActive { get; private set; }

		private void Awake()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			IsActive = false;
			_shutDown = false;
			Instance = this;
			LogInstance = ((BaseUnityPlugin)this).Logger;
			try
			{
				ModConfig.Bind(ConfigFileMigration.Open((BaseUnityPlugin)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger));
				QuickStackService = new QuickStackService(this);
				StorageCommands.Register();
				_harmony = new Harmony("dev.maks2204.bestautosort");
				PatchAllSafely();
				IsActive = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"BestAutoSort 0.6.28 (2867975) loaded.");
				try
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[ChestTX] config: TxVerbose=" + ModConfig.TxVerbose.Value + " ChestAuthorityMode=" + ModConfig.ChestAuthorityMode.Value.ToString() + " effective=" + ServerAuthority.EffectiveMode()));
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[ChestTX] config dump failed: " + ex.Message));
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} failed to initialize safely: {1}", "BestAutoSort", arg));
				Shutdown();
				throw;
			}
		}

		private void PatchAllSafely()
		{
			Type[] array = (from type2 in typeof(Plugin).Assembly.GetTypes()
				where type2.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0
				select type2).OrderBy<Type, string>((Type type2) => type2.FullName, StringComparer.Ordinal).ToArray();
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					_harmony.PatchAll(type);
					num++;
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to apply patch class {type.FullName}; other BestAutoSort features will remain available: {arg}");
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied {num} of {array.Length} BestAutoSort patch classes.");
			if (ServerReleaseGuard.PatchedSites != 2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("ServerReleaseGuard transpiler applied to " + ServerReleaseGuard.PatchedSites + " of " + 2 + " ReleaseNearbyZDOS ownership sites - redistribution exclusion DISABLED, game update likely changed (SetOwner backstop still holds fail-closed)."));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("ServerReleaseGuard rerouted " + ServerReleaseGuard.PatchedSites + " ReleaseNearbyZDOS ownership site(s) through the server-authority guard."));
			}
			if (TxRenderPatch.ReplacedCount == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"TxRenderPatch transpiler found no Container.IsOwner call in InventoryGui.UpdateContainer — multi-viewer rendering is DISABLED, game update likely changed.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"TxRenderPatch replaced {TxRenderPatch.ReplacedCount} owner-check(s) for shared rendering.");
			}
		}

		private void Update()
		{
			//IL_0046: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			if (!IsActive)
			{
				return;
			}
			ChestTxService.Pump();
			AutoFeedService.Update();
			NearbyPlaceIntent.Pump();
			NearbyResourceService.RepairInvalidPlayerInventoryPositions();
			if (ChestUpgradeService.UpdateLegacyMigration())
			{
				return;
			}
			ChestRuleEditor.Update();
			InventoryButtons.UpdateVisibility();
			if (ModConfig.Enabled.Value && CanAcceptShortcut())
			{
				if (IsShortcutDownAllowingOtherKeys(ModConfig.QuickStackShortcut.Value))
				{
					QuickStackNearby();
				}
				KeyboardShortcut value = ModConfig.SortChestShortcut.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					SortOpenChest();
				}
			}
		}

		internal void QuickStackNearby()
		{
			if (!IsActive)
			{
				return;
			}
			try
			{
				QuickStackService.BeginNearbyQuickStack(RestockProfileService.RestockNearby);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Nearby quick stack failed: {arg}");
			}
		}

		internal void SortOpenChest()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			if (!IsActive)
			{
				return;
			}
			try
			{
				Container val = InventoryAccess.CurrentContainer(InventoryGui.instance);
				if ((Object)val == (Object)null)
				{
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer != (Object)null)
					{
						((Character)localPlayer).Message((MessageType)2, "Open a chest to sort it.", 0, (Sprite)null, false);
					}
				}
				else if (!ChestTxService.IsShared(val))
				{
					if (ChestTxService.IsManager(val) && !ChestTxService.IsQuarantined(val))
					{
						SortLocal(val);
					}
					else if (ChestTxService.IsQuarantined(val))
					{
						TxLog.Warn("sort refused: chest quarantined (fail closed)");
						Player localPlayer2 = Player.m_localPlayer;
						if ((Object)localPlayer2 != (Object)null)
						{
							((Character)localPlayer2).Message((MessageType)2, "Chest state is still being verified. Try sorting again shortly.", 0, (Sprite)null, false);
						}
					}
				}
				else
				{
					ChestTxService.RequestSort(val, (int)ModConfig.ChestSortMode.Value, ModConfig.SortDescending.Value);
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Chest sort failed: {arg}");
			}
		}

		internal static void SortLocal(Container container)
		{
			int num = InventorySorter.Sort(container.GetInventory(), ModConfig.ChestSortMode.Value, ModConfig.SortDescending.Value);
			TxReflect.UpdateRows(container);
			TxReflect.SaveContainer(container);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)2, string.Format("Sorted {0} item stack{1} by {2}.", num, (num == 1) ? string.Empty : "s", ModConfig.ChestSortMode.Value), 0, (Sprite)null, false);
			}
		}

		internal void UpgradeOpenChest(int targetTier)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			if (!IsActive)
			{
				return;
			}
			try
			{
				Container val = InventoryAccess.CurrentContainer(InventoryGui.instance);
				if ((Object)val == (Object)null)
				{
					return;
				}
				ServerAuthority.EnsureServerOwnership(val, "upgrade-open");
				if (ServerAuthority.IsAuthorityMode() && ServerAuthority.IsServerManagedContainer(val))
				{
					ChestUpgradeService.UpgradeOpenChest(targetTier);
					return;
				}
				if (!ChestTxService.IsShared(val) || ChestTxService.IsManager(val))
				{
					ChestUpgradeService.UpgradeOpenChest(targetTier);
					return;
				}
				if (ChestTxService.AcquireForStructural(val))
				{
					ChestUpgradeService.UpgradeOpenChest(targetTier);
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).Message((MessageType)2, "Could not acquire the chest for upgrade. Try again.", 0, (Sprite)null, false);
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Chest upgrade failed: {arg}");
				Player localPlayer2 = Player.m_localPlayer;
				if ((Object)(object)localPlayer2 != (Object)null)
				{
					((Character)localPlayer2).Message((MessageType)2, "Chest upgrade failed. See the log for details.", 0, (Sprite)null, false);
				}
			}
		}

		private static bool CanAcceptShortcut()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			if ((Object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			if (ChestRuleEditor.IsOpen)
			{
				return false;
			}
			if ((Object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return false;
			}
			if (Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible())
			{
				return false;
			}
			if ((Object)Minimap.instance != (Object)null && Minimap.InTextInput())
			{
				return false;
			}
			return true;
		}

		private static bool IsShortcutDownAllowingOtherKeys(KeyboardShortcut shortcut)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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)
			KeyCode mainKey = ((KeyboardShortcut)(ref shortcut)).MainKey;
			if ((int)mainKey == 0 || !UnityInput.Current.GetKeyDown(mainKey))
			{
				return false;
			}
			return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((KeyCode modifier) => modifier == mainKey || UnityInput.Current.GetKey(modifier));
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void Shutdown()
		{
			if (!_shutDown)
			{
				_shutDown = true;
				IsActive = false;
				QuickStackService?.Reset();
				ChestTxService.Reset();
				TxDestroy.Reset();
				ChestTxService.ResetUpgradeRefunds();
				TxNet.Reset();
				AutoFeedService.Reset();
				InventoryButtons.Detach();
				TransferContext.End();
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				_harmony = null;
			}
		}
	}
	internal static class BuildInfo
	{
		internal const string Commit = "2867975";
	}
}
namespace BestAutoSort.Tx
{
	internal static class ChestTxService
	{
		internal sealed class TransientRefusal
		{
			public long TxId;

			public TxOp Op;

			public long Sender;

			public ZDOID ZdoId;
		}

		private sealed class ServerUpgradeHost : ITxUpgradeHost
		{
			public string SpawnGhost(string sourceId, int tier)
			{
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0125: Unknown result type (might be due to invalid IL or missing references)
				if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null)
				{
					throw new InvalidOperationException("source chest not loaded");
				}
				if ((Object)(object)ZNetScene.instance == (Object)null)
				{
					throw new InvalidOperationException("network scene unavailable");
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(ChestUpgradePath.PrefabForTier(tier));
				if ((Object)(object)prefab == (Object)null)
				{
					throw new InvalidOperationException("target prefab unavailable");
				}
				GameObject val = Object.Instantiate<GameObject>(prefab, ((Component)container).transform.position, ((Component)container).transform.rotation);
				Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Container>() : null);
				ZNetView val3 = (((Object)(object)val != (Object)null) ? val.GetComponent<ZNetView>() : null);
				Piece val4 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
				ZDO val5 = (((Object)(object)val3 != (Object)null) ? val3.GetZDO() : null);
				if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || val5 == null)
				{
					throw new InvalidOperationException("replacement chest did not initialize");
				}
				if (!val3.IsOwner())
				{
					throw new InvalidOperationException("replacement chest not owned by the server");
				}
				try
				{
					Piece component = ((Component)container).GetComponent<Piece>();
					TxReflect.GetNetView(container);
					if ((Object)(object)component != (Object)null)
					{
						val4.SetCreator(component.GetCreator(), PlatformUserID.None);
					}
				}
				catch
				{
				}
				val5.Set("BestAutoSort.ChestTier", tier);
				try
				{
					ChestRuleStore.TryWrite(val2, ChestRuleStore.Read(container), out var _);
					ChestReserveStore.Write(val2, ChestReserveStore.ReadRaw(container));
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException("rule migration failed: " + ex.Message);
				}
				string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref val5.m_uid)/*cast due to .constrained prefix*/).ToString();
				UpgradeLive[text] = val2;
				return text;
			}

			public void WriteReverseLink(string ghostId, string opId)
			{
				if (!UpgradeResolve(ghostId, out var container) || (Object)(object)container == (Object)null)
				{
					throw new InvalidOperationException("ghost not loaded for link-write");
				}
				ZNetView netView = TxReflect.GetNetView(container);
				ZDO obj = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null) ?? throw new InvalidOperationException("ghost has no ZDO for link-write");
				obj.Set("BestAutoSort.UpgradeOp", opId);
				obj.Set("BestAutoSort.UpgradeIncomplete", 1);
			}

			public string FindGhostByLink(string opId)
			{
				try
				{
					foreach (KeyValuePair<string, Container> item in UpgradeLive)
					{
						if (!((Object)(object)item.Value == (Object)null))
						{
							ZNetView netView = TxReflect.GetNetView(item.Value);
							ZDO val = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null);
							if (val != null && string.Equals(val.GetString("BestAutoSort.UpgradeOp", string.Empty), opId, StringComparison.Ordinal) && val.GetInt("BestAutoSort.UpgradeIncomplete", 0) == 1)
							{
								return item.Key;
							}
						}
					}
				}
				catch
				{
				}
				return null;
			}

			public void CopyInventoryNonDestructive(string sourceId, string ghostId)
			{
				if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null)
				{
					throw new InvalidOperationException("source not loaded for copy");
				}
				if (!UpgradeResolve(ghostId, out var container2) || (Object)(object)container2 == (Object)null)
				{
					throw new InvalidOperationException("ghost not loaded for copy");
				}
				Inventory inventory = container.GetInventory();
				Inventory inventory2 = container2.GetInventory();
				if (inventory == null || inventory2 == null)
				{
					throw new InvalidOperationException("missing inventory for copy");
				}
				foreach (ItemData item in new List<ItemData>(inventory.GetAllItems()))
				{
					if (item != null && item.m_shared != null)
					{
						ItemData val = item.Clone();
						if (!inventory2.AddItem(val))
						{
							throw new InvalidOperationException("ghost cannot hold the copied contents");
						}
					}
				}
			}

			public int CountItems(string chestId)
			{
				return UpgradeItemUnits(chestId);
			}

			public int HashItems(string chestId)
			{
				return UpgradeItemHash(chestId);
			}

			public bool ValidateReady(string ghostId, int wantCount, int wantHash)
			{
				if (CountItems(ghostId) == wantCount)
				{
					return HashItems(ghostId) == wantHash;
				}
				return false;
			}

			public string PointerSwitchOneWay(string sourceId, string ghostId)
			{
				if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null)
				{
					throw new InvalidOperationException("source not loaded for pointer switch");
				}
				if (!UpgradeResolve(ghostId, out var container2) || (Object)(object)container2 == (Object)null)
				{
					throw new InvalidOperationException("ghost not loaded for pointer switch");
				}
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					foreach (ItemData item in new List<ItemData>(inventory.GetAllItems()))
					{
						if (item != null)
						{
							inventory.RemoveItem(item, item.m_stack);
						}
					}
				}
				try
				{
					ChestUpgradeService.ApplyState(container2);
				}
				catch (Exception ex)
				{
					Plugin.LogInstance.LogWarning((object)("Upgrade ghost compact failed (contents safe): " + ex));
				}
				TxReflect.UpdateRows(container2);
				TxReflect.SaveContainer(container2);
				ZNetView netView = TxReflect.GetNetView(container2);
				ZDO obj = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null) ?? throw new InvalidOperationException("new chest lost its ZDO at pointer switch");
				string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref obj.m_uid)/*cast due to .constrained prefix*/).ToString();
				obj.Set("BestAutoSort.UpgradeIncomplete", 0);
				obj.Set("BestAutoSort.UpgradeReceipt", text);
				ZNetView netView2 = TxReflect.GetNetView(container);
				ZDO val = (((Object)(object)netView2 != (Object)null) ? netView2.GetZDO() : null);
				if (val != null)
				{
					val.Set("BestAutoSort.UpgradeReceipt", text);
				}
				return text;
			}

			public void RetireSource(string sourceId, string newId)
			{
				if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null)
				{
					return;
				}
				ZNetView netView = TxReflect.GetNetView(container);
				try
				{
					if ((Object)(object)netView != (Object)null && netView.IsValid())
					{
						netView.Destroy();
					}
					else
					{
						Object.Destroy((Object)(object)((Component)container).gameObject);
					}
				}
				catch (Exception ex)
				{
					throw new InvalidOperationException("source retire failed: " + ex.Message);
				}
				UpgradeLive.Remove(sourceId);
			}

			public void DestroyGhost(string ghostId)
			{
				if (UpgradeResolve(ghostId, out var container) && (Object)(object)container != (Object)null)
				{
					ZNetView netView = TxReflect.GetNetView(container);
					try
					{
						if ((Object)(object)netView != (Object)null && netView.IsValid() && netView.IsOwner())
						{
							netView.Destroy();
						}
						else
						{
							Object.Destroy((Object)(object)((Component)container).gameObject);
						}
					}
					catch
					{
					}
				}
				UpgradeLive.Remove(ghostId);
			}

			public void LogLoud(string message)
			{
				try
				{
					Plugin.LogInstance.LogWarning((object)message);
				}
				catch
				{
				}
				TxLog.Warn(message);
			}

			public void CrashHook(TxUpgradeCrashPoint point)
			{
			}
		}

		internal const int BatchChunkSize = 4;

		private const float RequestTimeout = 3f;

		private const int PayloadAttempts = 4;

		private const int QueryAttempts = 2;

		private const float FailDeadline = 16f;

		private const float PresenceHeartbeat = 5f;

		private const float PresenceTimeout = 15f;

		private const float RefreshPoll = 0.25f;

		private const float SlowPump = 0.5f;

		private const int DrainPerFrame = 64;

		private const float AuthoritySweepInterval = 15f;

		private const float AuthoritySweepHeartbeat = 300f;

		internal const string RingKey = "BestAutoSort_TxRing";

		internal const string FloorKey = "BestAutoSort_TxFloor";

		internal const string ItemsKey = "items";

		private static readonly Dictionary<int, ChestState> States = new Dictionary<int, ChestState>();

		private static readonly Dictionary<long, PendingTx> Pending = new Dictionary<long, PendingTx>();

		private static uint _clientCounter = 1u;

		internal static long SItemsNullDenials;

		private static uint _counterReservedCeil;

		private const uint CounterReserveBlock = 4096u;

		private static float _nextSlowPump;

		private static float _nextRefreshPoll;

		private static float _nextAuthoritySweep;

		private static float _nextAuthoritySweepHeartbeat;

		private static int _openInstanceId;

		private static float _nextPresenceAt;

		internal static TxCounterFile.CounterLoadState CounterLoadState = TxCounterFile.CounterLoadState.Fresh;

		private static bool _counterFailClosed;

		private static readonly TxClaimSet<ItemData> InFlightAdds = new TxClaimSet<ItemData>();

		private static readonly Dictionary<long, TransientRefusal> TransientRefusals = new Dictionary<long, TransientRefusal>();

		private static readonly Dictionary<string, TxUpgradeManager> UpgradeManagers = new Dictionary<string, TxUpgradeManager>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Container> UpgradeLive = new Dictionary<string, Container>(StringComparer.Ordinal);

		private const string UpOpKey = "BestAutoSort.UpgradeOp";

		private static readonly HashSet<string> AppliedUpgradeRefunds = new HashSet<string>(StringComparer.Ordinal);

		private const int UpgradeRefundCap = 256;

		private const string UpPhaseKey = "BestAutoSort.UpgradePhase";

		private const string UpGhostKey = "BestAutoSort.UpgradeGhost";

		private const string UpCountKey = "BestAutoSort.UpgradeCount";

		private const string UpHashKey = "BestAutoSort.UpgradeHash";

		private const string UpSpendKey = "BestAutoSort.UpgradeSpend";

		private const string UpSpendRecKey = "BestAutoSort.UpgradeSpendRec";

		private const string UpRefundKey = "BestAutoSort.UpgradeRefund";

		private const string UpRefundDoneKey = "BestAutoSort.UpgradeRefundDone";

		private const string UpReceiptKey = "BestAutoSort.UpgradeReceipt";

		private const string UpNonceKey = "BestAutoSort.UpgradeNonce";

		private const string UpSenderKey = "BestAutoSort.UpgradeSender";

		private const string UpTierKey = "BestAutoSort.UpgradeTier";

		private const string UpIncompleteKey = "BestAutoSort.UpgradeIncomplete";

		private const int UpgradePumpGuard = 32;

		internal static bool CounterIssuanceBlocked => _counterFailClosed;

		internal static int TransientRefusalCount => TransientRefusals.Count;

		private static void Respond(Container container, long peer, long txId, TxStatus status, uint revision, ZPackage body, bool totalsOnly, TxOp op)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			ZNetView netView = TxReflect.GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return;
			}
			if (peer == ZNet.GetUID())
			{
				TxLog.Info("tx=" + txId + " respond loopback (manager==requester)");
			}
			ZPackage val = new ZPackage();
			TxCodec.WriteResponseHeader(val, txId, status, revision, totalsOnly);
			val.Write(body);
			if (body != null)
			{
				try
				{
					int num = val.GetArray().Length;
					if (num > 4096)
					{
						TxLog.Info("tx=" + txId + " respond bytes=" + num + " (large)");
					}
				}
				catch
				{
				}
			}
			try
			{
				netView.InvokeRPC(peer, "BestAutoSort_TxResponse", new object[1] { val });
			}
			catch (Exception ex)
			{
				TxLog.Warn("tx=" + txId + " respond failed: " + ex.Message);
			}
		}

		internal static ZPackage EncodeResultBody(TxOpCall call, StoredResult r)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			switch (call.Op)
			{
			case TxOp.Add:
			case TxOp.AddBatch:
			case TxOp.Move:
			case TxOp.Sort:
			case TxOp.Upgrade:
			case TxOp.SetRule:
			{
				val.Write(r.Accepted.Count);
				for (int j = 0; j < r.Accepted.Count; j++)
				{
					val.Write(r.Accepted[j]);
				}
				break;
			}
			case TxOp.UpgradeRequest:
			{
				val.Write(r.Accepted.Count);
				for (int k = 0; k < r.Accepted.Count; k++)
				{
					val.Write(r.Accepted[k]);
				}
				val.Write(r.Receipt ?? string.Empty);
				try
				{
					int num = 0;
					try
					{
						if (r.RefundPrefabs != null && r.RefundAmounts != null)
						{
							num = Math.Min(r.RefundPrefabs.Count, r.RefundAmounts.Count);
						}
					}
					catch
					{
						num = 0;
					}
					if (num < 0)
					{
						num = 0;
					}
					val.Write(num);
					for (int l = 0; l < num; l++)
					{
						val.Write(r.RefundPrefabs[l]);
						val.Write(r.RefundAmounts[l]);
					}
				}
				catch
				{
				}
				break;
			}
			case TxOp.Take:
			case TxOp.TakeBatch:
			{
				val.Write(r.Takes.Count);
				for (int i = 0; i < r.Takes.Count; i++)
				{
					TakeEntry takeEntry = r.Takes[i];
					if (takeEntry == null)
					{
						val.Write(0);
						val.Write(new ZPackage());
						val.Write(0);
					}
					else
					{
						ZPackage val2 = new ZPackage();
						takeEntry.Item.Save(val2);
						val.Write(takeEntry.PrefabHash);
						val.Write(val2);
						val.Write(takeEntry.Accepted);
					}
				}
				break;
			}
			}
			return val;
		}

		private static void RespondQuery(Container container, long sender, long txId, bool isTransientRetry)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Expected O, but got Unknown
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Expected O, but got Unknown
			//IL_004a: 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_0094: Expected O, but got Unknown
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Expected O, but got Unknown
			ChestState state = GetState(container);
			if (state == null)
			{
				return;
			}
			TransientRefusal value2;
			if (state.Processed.TryGetValue(txId, out var value))
			{
				if (value.Sender != 0L && TxIdGen.PeerKey(value.Sender) != TxIdGen.PeerKey(sender))
				{
					TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " QUERY sender mismatch");
					Respond(container, sender, txId, TxStatus.Rejected, value.Revision, new ZPackage(), totalsOnly: false, value.Op);
				}
				else
				{
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " QUERY hit status=" + value.Status);
					Respond(container, sender, txId, value.Status, value.Revision, EncodeCachedBody(value), value.TotalsOnly, value.Op);
				}
			}
			else if (TransientRefusals.TryGetValue(txId, out value2))
			{
				if (sender != 0L && !TxIdGen.MatchesPeer(sender, txId))
				{
					TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " TRANSIENT-QUERY sender mismatch");
					Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query);
				}
				else
				{
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " TRANSIENT-QUERY hit (same-tx retry retained)");
					Respond(container, sender, txId, TxStatus.TransientUnavailable, CurrentRevision(container), new ZPackage(), totalsOnly: false, value2.Op);
				}
			}
			else if (isTransientRetry)
			{
				if (sender != 0L && !TxIdGen.MatchesPeer(sender, txId))
				{
					TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " FLAGGED-QUERY sender mismatch");
					Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query);
				}
				else
				{
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " FLAGGED-QUERY no record (same-tx retry retained)");
					Respond(container, sender, txId, TxStatus.TransientUnavailable, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query);
				}
			}
			else
			{
				Respond(container, sender, txId, TxStatus.UnknownTx, CurrentRevision(container), new ZPackage(), totalsOnly: true, TxOp.Query);
			}
		}

		internal static ZPackage EncodeCachedBody(StoredResult cached)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			if (cached.TotalsOnly)
			{
				if (cached.Op == TxOp.Take || cached.Op == TxOp.TakeBatch)
				{
					val.Write(0);
					return val;
				}
				val.Write(1);
				val.Write(cached.AcceptedTotal());
				return val;
			}
			return EncodeResultBody(new TxOpCall
			{
				Op = cached.Op
			}, cached);
		}

		internal static void HandleServerTxResponse(long sender, ZDOID chestId, ZPackage inner)
		{
			//IL_0070: 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_0040: Expected O, but got Unknown
			try
			{
				if (!Plugin.IsActive || inner == null || ((ZDOID)(ref chestId)).IsNone())
				{
					return;
				}
				long num = 0L;
				try
				{
					ZPackage val = new ZPackage(inner.GetArray());
					long txId = 0L;
					TxStatus status = TxStatus.UnknownTx;
					uint revision = 0u;
					bool totalsOnly = false;
					if (TxCodec.ReadResponseHeader(val, out txId, out status, out revision, out totalsOnly))
					{
						num = txId;
					}
				}
				catch
				{
				}
				if (num == 0L)
				{
					return;
				}
				PendingTx value = null;
				bool flag = false;
				try
				{
					flag = Pending.TryGetValue(num, out value);
				}
				catch
				{
					flag = false;
				}
				Container val2 = null;
				try
				{
					val2 = ((flag && value != null) ? value.Container : FindContainerByZdo(chestId));
				}
				catch
				{
					val2 = null;
				}
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				try
				{
					inner.SetPos(0);
				}
				catch
				{
					return;
				}
				try
				{
					OnTxResponse(val2, sender, inner);
				}
				catch
				{
				}
			}
			catch
			{
			}
		}

		private static Container FindContainerByZdo(ZDOID chestId)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				foreach (KeyValuePair<int, ChestState> state in States)
				{
					ChestState value = state.Value;
					if (value == null || (Object)(object)value.Container == (Object)null)
					{
						continue;
					}
					try
					{
						if (value.ZdoId == chestId)
						{
							return value.Container;
						}
					}
					catch
					{
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void OnTxResponse(Container container, long sender, ZPackage pkg)
		{
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Expected O, but got Unknown
			if (!Plugin.IsActive)
			{
				return;
			}
			if (!TxCodec.ReadResponseHeader(pkg, out var txId, out var status, out var revision, out var totalsOnly))
			{
				TxLog.Warn("bad response packet from " + sender);
				return;
			}
			if (!Pending.TryGetValue(txId, out var value))
			{
				RefreshNow(container);
				TxLog.Info("tx=" + txId + " late-duplicate ignored (already completed)");
				return;
			}
			TxLog.Info("tx=" + txId + " response status=" + status.ToString() + " rev=" + revision + (totalsOnly ? " totals-only" : ""));
			ZPackage val;
			try
			{
				val = pkg.ReadPackage();
			}
			catch (Exception ex)
			{
				TxLog.Warn("tx=" + txId + " response body unreadable: " + ex.Message);
				RefreshNow(value.Container);
				if (!ForceRefetch(txId))
				{
					FinalizeUnknownTx(txId);
				}
				return;
			}
			pkg = val;
			TxCompletionKind txCompletionKind = TxResponsePolicy.Classify(status, totalsOnly, value.Op, value.ExpectedItems);
			switch (txCompletionKind)
			{
			case TxCompletionKind.TransientRetrySameTx:
			{
				value.IsTransientRetry = true;
				float num = TxPendingDrain.TransientBackoffSeconds(value.TransientAttempts + 1);
				value.NextTryAt = Time.realtimeSinceStartup + num;
				value.Deadline = Time.realtimeSinceStartup + 16f;
				TxLog.Warn("tx=" + txId + " TRANSIENT op=" + value.Op.ToString() + " (same-tx retry retained, backoff " + num + "s — nothing credited/removed)");
				RefreshNow(value.Container);
				return;
			}
			case TxCompletionKind.Indeterminate:
				TxLog.Warn("tx=" + txId + " outcome UNKNOWN op=" + value.Op.ToString() + " (manager has no record, nothing credited/removed, no cascade)");
				TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind);
				return;
			case TxCompletionKind.FailedNotCommitted:
				TxLog.Warn("tx=" + txId + " REJECTED op=" + value.Op.ToString() + " (nothing applied, nothing credited/removed)");
				CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind);
				return;
			case TxCompletionKind.CommittedTakeDetailsUnavailable:
				TxLog.Warn("tx=" + txId + " COMMITTED Take totals-only: applied but items unrecoverable from cache (nothing credited)");
				TellPlayer("Chest applied the move but the items cannot be recovered. Check the chest.");
				CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind);
				return;
			case TxCompletionKind.CommittedMultiAddDetailsUnavailable:
				TxLog.Warn("tx=" + txId + " COMMITTED multi-add totals-only: applied but per-item counts unknown (nothing removed, no auto-retry)");
				TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying.");
				CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind);
				return;
			}
			if (!totalsOnly && IsTakeOp(value.Op) && !TakeBodyReadable(pkg))
			{
				TxLog.Warn("tx=" + txId + " take body corrupt: re-querying instead of voiding");
				RefreshNow(value.Container);
				if (!ForceRefetch(txId))
				{
					FinalizeUnknownTx(txId);
				}
			}
			else if (!totalsOnly && !IsTakeOp(value.Op) && value.ExpectedItems >= 0 && !AddBodyMatchesArity(pkg, value.ExpectedItems))
			{
				int num2 = -1;
				int num3 = -2147483647;
				try
				{
					ZPackage val2 = new ZPackage(pkg.GetArray());
					num2 = val2.GetArray().Length;
					try
					{
						num3 = val2.ReadInt();
					}
					catch
					{
					}
				}
				catch
				{
				}
				TxLog.Warn("tx=" + txId + " add body corrupt: expected=" + value.ExpectedItems + " bodyLen=" + num2 + " bodyFirst=" + num3 + ", re-querying instead of misattributing");
				RefreshNow(value.Container);
				if (!ForceRefetch(txId))
				{
					FinalizeUnknownTx(txId);
				}
			}
			else
			{
				CompletePendingTerminal(txId, pkg, status, revision, txCompletionKind);
			}
		}

		private static void CompletePendingTerminal(long txId, ZPackage body, TxStatus status, uint rev, TxCompletionKind disp)
		{
			if (!Pending.TryGetValue(txId, out var value))
			{
				return;
			}
			Pending.Remove(txId);
			ReleaseClaimed(value.Claimed);
			try
			{
				if (value.OnResponse != null)
				{
					value.OnResponse(body, status, rev, disp);
				}
			}
			catch (Exception ex)
			{
				TxLog.Error("tx=" + txId + " completion failed: " + ex.Message);
			}
			RefreshNow(value.Container);
		}

		private static ZPackage EmptyCountBody()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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)
			//IL_0014: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(0);
			val.SetPos(0);
			return val;
		}

		internal static bool ForceRefetch(long txId)
		{
			if (!Pending.TryGetValue(txId, out var value) || value.FinalQuerySent)
			{
				return false;
			}
			if ((Object)(object)value.Container == (Object)null)
			{
				return false;
			}
			value.FinalQuerySent = true;
			value.QuerySent = true;
			SendQuery(value);
			value.Attempts++;
			value.Deadline = Time.realtimeSinceStartup + 3f;
			value.NextTryAt = Time.realtimeSinceStartup + 3f;
			TxLog.Warn("tx=" + txId + " forced re-query (decode/timeout), deadline extended once");
			return true;
		}

		internal static void FinalizeUnknownTx(long txId)
		{
			if (Pending.TryGetValue(txId, out var value))
			{
				TxLog.Warn("tx=" + value.TxId + " UNKNOWN op=" + value.Op.ToString() + " (applied-or-not indeterminate, no queries left)");
				TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				CompletePendingTerminal(txId, null, TxStatus.UnknownTx, 0u, TxCompletionKind.Indeterminate);
			}
		}

		private static bool TakeBodyReadable(ZPackage body)
		{
			try
			{
				if (body == null)
				{
					return false;
				}
				int num = body.ReadInt();
				if (num < 0 || num > 4096)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					body.ReadInt();
					body.ReadPackage();
					body.ReadInt();
				}
				body.SetPos(0);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool AddBodyMatchesArity(ZPackage body, int expected)
		{
			try
			{
				if (body == null)
				{
					return false;
				}
				int num = body.ReadInt();
				if (num != expected)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					body.ReadInt();
				}
				body.SetPos(0);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static int ReadAcceptedAt(ZPackage pkg, int index)
		{
			try
			{
				if (pkg == null)
				{
					return 0;
				}
				int num = pkg.ReadInt();
				int result = 0;
				for (int i = 0; i < num; i++)
				{
					int num2 = pkg.ReadInt();
					if (i == index)
					{
						result = num2;
					}
				}
				return result;
			}
			catch (Exception)
			{
				return 0;
			}
		}

		private static List<int> ReadAcceptedList(ZPackage pkg, int count)
		{
			List<int> list = new List<int>();
			try
			{
				if (pkg == null)
				{
					return list;
				}
				int num = pkg.ReadInt();
				for (int i = 0; i < num && i < count; i++)
				{
					list.Add(pkg.ReadInt());
				}
			}
			catch (Exception)
			{
			}
			while (list.Count < count)
			{
				list.Add(0);
			}
			return list;
		}

		private static void CompleteAdd(Inventory srcInv, ItemData itemRef, TxOpItem sent, int accepted, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			CompleteAdd(srcInv, itemRef, sent, accepted, status, rev, container, onDone, alreadyRemoved: false, TxCompletionKind.Normal);
		}

		private static void LogNonMainInventory(string op, Inventory inv)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && inv != null && inv != ((Humanoid)localPlayer).GetInventory())
				{
					TxLog.Info(op + " uses non-main inventory (dedicated slots?)");
				}
			}
			catch
			{
			}
		}

		private static void CompleteAdd(Inventory srcInv, ItemData itemRef, TxOpItem sent, int accepted, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, bool alreadyRemoved, TxCompletionKind disp)
		{
			LogNonMainInventory("add", srcInv);
			if (alreadyRemoved)
			{
				if (!TxDecision.ShouldRestoreDragRemainder(disp))
				{
					TxLog.Warn("drag-restore withheld: outcome " + status.ToString() + "/" + disp.ToString() + " is not known-safe — chest may already hold the items, nothing restored");
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				}
				else
				{
					RestoreDragRemainder(srcInv, itemRef, accepted, disp);
				}
				if (status == TxStatus.UnknownTx && TxDecision.ShouldRestoreDragRemainder(disp))
				{
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				}
				else if (status == TxStatus.Rejected)
				{
					TellPlayer("The shared chest refused the move. Try again.");
				}
				onDone?.Invoke(null, status, rev, disp);
				return;
			}
			if (accepted > 0 && srcInv != null && sent != null)
			{
				string name = ((sent.Snapshot != null && sent.Snapshot.m_shared != null) ? sent.Snapshot.m_shared.m_name : null);
				int quality = ((sent.Snapshot != null) ? sent.Snapshot.m_quality : (-1));
				int variant = ((sent.Snapshot != null) ? sent.Snapshot.m_variant : (-1));
				float world = ((sent.Snapshot != null) ? ((float)sent.Snapshot.m_worldLevel) : (-1f));
				int num = TxInventory.RemoveForTake(srcInv, itemRef, name, quality, accepted, variant, world);
				TxLog.Info("add-remove accepted=" + accepted + " removed=" + num);
				if (num < accepted)
				{
					int amount = accepted - num;
					TxLog.Warn("tx short-removal: accepted=" + accepted + " removed=" + num + " compensating " + amount);
					CompensateAddBack(container, sent, amount);
				}
			}
			switch (status)
			{
			case TxStatus.UnknownTx:
				TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				break;
			case TxStatus.Rejected:
				TellPlayer("The shared chest refused the move. Try again.");
				break;
			}
			onDone?.Invoke(null, status, rev, disp);
		}

		private static void RestoreDragRemainder(Inventory srcInv, ItemData itemRef, int accepted, TxCompletionKind disp)
		{
			if (srcInv == null || itemRef == null)
			{
				return;
			}
			CustomDataTags.StripBenign(itemRef);
			TxLog.Info("drag-restore stack=" + itemRef.m_stack + " accepted=" + accepted + " disp=" + disp);
			int num = TxDecision.DragRestoreAmount(itemRef.m_stack, accepted, disp);
			if (num <= 0)
			{
				return;
			}
			itemRef.m_stack = num;
			if (srcInv.AddItem(itemRef))
			{
				srcInv.m_onChanged?.Invoke();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Humanoid)localPlayer).DropItem(srcInv, itemRef, num);
			}
			else
			{
				TxLog.Error("drag restore failed: no player, items lost: " + num);
			}
		}

		private static void CompensateAddBack(Container container, TxOpItem sent, int amount)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (amount <= 0 || sent == null || sent.Snapshot == null || sent.Snapshot.m_shared == null)
			{
				return;
			}
			TxOpItem txOpItem = SnapshotAuto(sent.Snapshot, amount);
			if (txOpItem == null)
			{
				return;
			}
			List<TxOpItem> list = new List<TxOpItem>();
			list.Add(txOpItem);
			RequestTakeCustom(container, list, respectReserves: false, delegate(List<DecodedTake> decoded, TxStatus status, uint rev, TxCompletionKind burnDisp)
			{
				int num = 0;
				if (decoded != null)
				{
					foreach (DecodedTake item in decoded)
					{
						if (item != null && item.Accepted > 0)
						{
							num += item.Accepted;
						}
					}
				}
				if (burnDisp == TxCompletionKind.Indeterminate || (status != TxStatus.Accepted && status != TxStatus.Partial && status != TxStatus.Duplicate))
				{
					TxLog.Warn("short-removal burn not applied (" + status.ToString() + "/" + burnDisp.ToString() + "), excess stays in chest — check the chest");
					TellPlayer("Chest could not correct an over-deposit. Check the chest.");
				}
				else if (num < amount)
				{
					TxLog.Warn("short-removal burn partial: burned=" + num + " of " + amount);
					TellPlayer("Chest could not correct an over-deposit. Check the chest.");
				}
				else
				{
					TxLog.Info("short-removal burn ok: " + num);
				}
				RefreshNow(container);
			}, 0L);
		}

		private static void CompleteTake(Inventory dstInv, ZPackage pkg, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, int wantDstX = -1, int wantDstY = -1, Action<Dictionary<string, int>> onPlaced = null, TxCompletionKind disp = TxCompletionKind.Normal)
		{
			LogNonMainInventory("take", dstInv);
			if ((status == TxStatus.Accepted || status == TxStatus.Partial || status == TxStatus.Duplicate) && dstInv != null && pkg != null)
			{
				int num = 0;
				try
				{
					num = pkg.ReadInt();
				}
				catch (Exception ex)
				{
					int num2 = -1;
					try
					{
						num2 = pkg.GetArray().Length;
					}
					catch
					{
					}
					TxLog.Error("take completion decode failed: " + ex.Message + " (body bytes=" + num2 + ")");
				}
				Dictionary<string, int> dictionary = null;
				for (int i = 0; i < num; i++)
				{
					int num3 = 0;
					ZPackage val = null;
					int num4 = 0;
					try
					{
						num3 = pkg.ReadInt();
						val = pkg.ReadPackage();
						num4 = pkg.ReadInt();
					}
					catch (Exception ex2)
					{
						TxLog.Error("take completion entry " + i + " unreadable (" + ex2.Message + "), aborting rest");
						break;
					}
					if (num4 <= 0)
					{
						continue;
					}
					try
					{
						ItemData val2 = TxCodec.ResolvePrefab(num3, val);
						CustomDataTags.StripBenign(val2);
						if (val2 == null)
						{
							TxLog.Error("take completion: prefab " + num3 + " missing, compensating " + num4);
							CompensateTakeBack(container, num3, val, num4);
							continue;
						}
						int num5 = TxInventory.AddTakeAndCount(dstInv, val2, wantDstX, wantDstY);
						if (num5 > 0 && onPlaced != null && val2.m_shared != null)
						{
							if (dictionary == null)
							{
								dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
							}
							dictionary.TryGetValue(val2.m_shared.m_name, out var value);
							dictionary[val2.m_shared.m_name] = value + num5;
						}
						if (num5 < num4)
						{
							int stack = num4 - num5;
							TxLog.Warn("take completion: player full, compensating " + stack);
							ItemData val3 = val2.Clone();
							val3.m_stack = stack;
							CompensateTakeBackItem(container, num3, val3);
						}
					}
					catch (Exception ex3)
					{
						TxLog.Error("take completion entry " + i + " failed (" + ex3.Message + "), compensating " + num4);
						try
						{
							CompensateTakeBack(container, num3, val, num4);
						}
						catch (Exception ex4)
						{
							TxLog.Error("take completion entry " + i + " compensation failed: " + ex4.Message);
						}
					}
				}
				if (dictionary != null && dictionary.Count > 0 && onPlaced != null)
				{
					try
					{
						onPlaced(dictionary);
					}
					catch
					{
					}
				}
			}
			switch (status)
			{
			case TxStatus.UnknownTx:
				TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
				break;
			case TxStatus.Rejected:
				TellPlayer("The shared chest changed. Try again.");
				break;
			}
			onDone?.Invoke(pkg, status, rev, disp);
		}

		private static void CompensateTakeBack(Container container, int prefabHash, ZPackage inner, int amount)
		{
			ItemData val = TxCodec.ResolvePrefab(prefabHash, inner);
			if (val == null)
			{
				TxLog.Error("compensation failed: prefab missing");
			}
			else
			{
				CompensateTakeBackItem(container, prefabHash, val);
			}
		}

		internal static void CompensateTakeBackItem(Container container, int prefabHash, ItemData item)
		{
			int wanted = ((item != null) ? item.m_stack : 0);
			TxOpItem txOpItem = SnapshotAuto(item, item.m_stack);
			if (txOpItem == null)
			{
				return;
			}
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.Add;
			txOpCall.Items.Add(txOpItem);
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				if (disp == TxCompletionKind.Indeterminate)
				{
					TxLog.Warn("compensate-add indeterminate: outcome unknown, nothing restored — check the chest");
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					RefreshNow(container);
				}
				else
				{
					int num = ReadAcceptedAt(pkg, 0);
					TxLog.Info("compensate-add status=" + status.ToString() + " accepted=" + num + " of " + wanted);
					if (num < wanted)
					{
						RestoreCompensateLeftover(item, num);
					}
					RefreshNow(container);
				}
			}, 0L);
		}

		private static void RestoreCompensateLeftover(ItemData item, int accepted)
		{
			if (item == null)
			{
				return;
			}
			int num = item.m_stack - accepted;
			if (num <= 0)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null);
			if (val == null)
			{
				TxLog.Error("compensate restore failed: no player inventory, items lost: " + num);
				return;
			}
			ItemData val2 = item.Clone();
			val2.m_stack = num;
			CustomDataTags.StripBenign(val2);
			if (TxInventory.AddAndCount(val, val2) < num)
			{
				((Humanoid)localPlayer).DropItem(val, val2, num);
				TxLog.Warn("compensate leftover did not fit, dropped at feet: " + num);
			}
			else
			{
				TxLog.Warn("compensate-add short, restored to player: " + num);
			}
			TellPlayer("Chest could not take back some items. They were returned to you.");
		}

		internal static void SubmitTakePrefetch(Container container, TxOpCall call, Inventory dstInv, Action<Dictionary<string, int>> onLanded = null)
		{
			Submit(container, call, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				CompleteTake(dstInv, pkg, status, rev, container, null, -1, -1, onLanded, disp);
			}, 0L);
		}

		internal static void RequestTakeCustom(Container container, List<TxOpItem> items, bool respectReserves, Action<List<DecodedTake>, TxStatus, uint, TxCompletionKind> onDone, long playerId = 0L, Vector3? actorPos = null, ZDOID feederId = default(ZDOID))
		{
			//IL_0057: 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)
			if (items == null || items.Count == 0)
			{
				return;
			}
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.TakeBatch;
			txOpCall.Items.AddRange(items);
			txOpCall.RespectReserves = respectReserves;
			txOpCall.FeederId = feederId;
			if (IsManager(container))
			{
				MutateLocal(container, txOpCall, delegate(StoredResult r)
				{
					RefreshNow(container);
					List<DecodedTake> list = new List<DecodedTake>();
					if (r != null && r.Takes != null)
					{
						foreach (TakeEntry take in r.Takes)
						{
							if (take != null && take.Accepted > 0)
							{
								list.Add(new DecodedTake
								{
									PrefabHash = take.PrefabHash,
									Item = take.Item,
									Accepted = take.Accepted
								});
							}
						}
					}
					TxStatus txStatus = r?.Status ?? TxStatus.UnknownTx;
					uint arg = r?.Revision ?? CurrentRevision(container);
					TxCompletionKind arg2 = ((r != null) ? TxResponsePolicy.Classify(txStatus, r.TotalsOnly, TxOp.TakeBatch, items.Count) : TxCompletionKind.Indeterminate);
					if (onDone != null)
					{
						onDone(list, txStatus, arg, arg2);
					}
				}, playerId, actorPos);
				return;
			}
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				List<DecodedTake> list = TxCodec.ReadTakeResults(pkg);
				if (list == null)
				{
					if (disp == TxCompletionKind.Indeterminate)
					{
						TxLog.Warn("take-custom completion decode failed (indeterminate, nothing credited, no retry)");
						if (onDone != null)
						{
							onDone(new List<DecodedTake>(), status, rev, disp);
						}
					}
					else if (status == TxStatus.Accepted || status == TxStatus.Partial || status == TxStatus.Duplicate)
					{
						TxLog.Warn("take-custom completion decode failed (committed, payload lost: nothing credited, no retry)");
						if (onDone != null)
						{
							onDone(new List<DecodedTake>(), status, rev, TxCompletionKind.CommittedTakeDetailsUnavailable);
						}
					}
					else
					{
						TxLog.Warn("take-custom completion decode failed");
						if (onDone != null)
						{
							onDone(new List<DecodedTake>(), status, rev, disp);
						}
					}
				}
				else if (onDone != null)
				{
					onDone(list, status, rev, disp);
				}
			}, playerId, actorPos);
		}

		internal static void SubmitCall(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, TxCompletionKind> onDone)
		{
			SubmitCallDetailed(container, call, srcInv, delegate(List<TransferRecord> records, List<int> accepted, TxStatus status, TxCompletionKind disp)
			{
				if (onDone != null)
				{
					onDone(records, disp);
				}
			});
		}

		internal static void SubmitCallDetailed(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, List<int>, TxStatus, TxCompletionKind> onDone)
		{
			if (call != null && call.Items.Count > 4)
			{
				for (int i = 0; i < call.Items.Count; i += 4)
				{
					TxOpCall txOpCall = new TxOpCall();
					txOpCall.Op = call.Op;
					txOpCall.EnforceRule = call.EnforceRule;
					txOpCall.RespectReserves = call.RespectReserves;
					for (int j = i; j < call.Items.Count && j < i + 4; j++)
					{
						txOpCall.Items.Add(call.Items[j]);
					}
					SubmitCallDetailed(container, txOpCall, srcInv, onDone);
				}
				return;
			}
			if (IsManager(container))
			{
				MutateLocal(container, call, delegate(StoredResult r)
				{
					List<int> list = new List<int>();
					for (int k = 0; k < call.Items.Count; k++)
					{
						list.Add((r != null && r.Accepted != null && k < r.Accepted.Count) ? r.Accepted[k] : 0);
					}
					TxStatus txStatus = r?.Status ?? TxStatus.UnknownTx;
					List<TransferRecord> arg = FinishBatchCompletion(container, srcInv, call, r);
					TxCompletionKind arg2 = ((r != null) ? TxResponsePolicy.Classify(txStatus, r.TotalsOnly, call.Op, call.Items.Count) : TxCompletionKind.Indeterminate);
					if (onDone != null)
					{
						onDone(arg, list, txStatus, arg2);
					}
				}, 0L);
				return;
			}
			Submit(container, call, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				switch (disp)
				{
				case TxCompletionKind.Indeterminate:
					TxLog.Warn("batch indeterminate: outcome unknown, nothing removed, no cascade — check the chest before retrying");
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(new List<TransferRecord>(), ZeroAccepted(call.Items.Count), status, disp);
					}
					break;
				case TxCompletionKind.CommittedMultiAddDetailsUnavailable:
					TxLog.Warn("batch details-unavailable: committed but unattributed, nothing removed, no cascade");
					TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying.");
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(new List<TransferRecord>(), ZeroAccepted(call.Items.Count), status, disp);
					}
					break;
				default:
				{
					List<int> list = ReadAcceptedList(pkg, call.Items.Count);
					List<TransferRecord> list2 = new List<TransferRecord>();
					for (int k = 0; k < call.Items.Count && k < list.Count; k++)
					{
						TxOpItem txOpItem = call.Items[k];
						CompleteAdd(srcInv, txOpItem.SourceRef, txOpItem, list[k], status, rev, container, null, alreadyRemoved: false, disp);
						if (list[k] > 0 && txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null)
						{
							list2.Add(new TransferRecord(txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.GetIcon(), list[k], txOpItem.Snapshot.m_shared.m_maxStackSize));
						}
					}
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(list2, list, status, disp);
					}
					break;
				}
				}
			}, 0L, null, call.Items.Count);
		}

		private static List<int> ZeroAccepted(int count)
		{
			List<int> list = new List<int>((count >= 0) ? count : 0);
			for (int i = 0; i < count; i++)
			{
				list.Add(0);
			}
			return list;
		}

		private static List<TransferRecord> FinishBatchCompletion(Container container, Inventory srcInv, TxOpCall call, StoredResult r)
		{
			List<TransferRecord> list = new List<TransferRecord>();
			for (int i = 0; i < call.Items.Count && i < r.Accepted.Count; i++)
			{
				TxOpItem txOpItem = call.Items[i];
				int num = r.Accepted[i];
				if (num > 0 && srcInv != null)
				{
					string name = ((txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) ? txOpItem.Snapshot.m_shared.m_name : null);
					int quality = ((txOpItem.Snapshot != null) ? txOpItem.Snapshot.m_quality : (-1));
					int variant = ((txOpItem.Snapshot != null) ? txOpItem.Snapshot.m_variant : (-1));
					float world = ((txOpItem.Snapshot != null) ? ((float)txOpItem.Snapshot.m_worldLevel) : (-1f));
					int num2 = TxInventory.RemoveForTake(srcInv, txOpItem.SourceRef, name, quality, num, variant, world);
					if (num2 < num)
					{
						int amount = num - num2;
						TxLog.Warn("local batch short-removal: compensating " + amount);
						CompensateAddBack(container, txOpItem, amount);
					}
					if (txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null)
					{
						list.Add(new TransferRecord(txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.GetIcon(), num, txOpItem.Snapshot.m_shared.m_maxStackSize));
					}
				}
			}
			RefreshNow(container);
			return list;
		}

		private static void PumpPending()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			float now = Time.realtimeSinceStartup;
			TxPendingDrain.Drain(Pending, delegate(long txId, PendingTx p)
			{
				if ((Object)(object)p.Container == (Object)null)
				{
					return TxPendingDrain.Step.Terminal;
				}
				return p.IsTransientRetry ? TxPendingDrain.DecideTransient(now, p.NextTryAt) : TxPendingDrain.Decide(now, p.NextTryAt, p.Deadline, p.Attempts, p.QuerySent, p.FinalQuerySent, 4, 2);
			}, delegate(long txId, PendingTx p, TxPendingDrain.Step step)
			{
				switch (step)
				{
				case TxPendingDrain.Step.Resend:
					if (p.IsTransientRetry)
					{
						SendTransientRetry(p);
					}
					else if (CanSendStoredMutationPayload(p))
					{
						SendPayload(p.Container, p.Payload);
					}
					else
					{
						TxLog.Warn("tx=" + p.TxId + " resend skipped: no stored payload (entry retained)");
					}
					p.Attempts++;
					p.NextTryAt = now + (p.IsTransientRetry ? TxPendingDrain.TransientBackoffSeconds(p.TransientAttempts) : 3f);
					break;
				case TxPendingDrain.Step.Query:
					p.QuerySent = true;
					SendQuery(p);
					p.Attempts++;
					p.NextTryAt = now + 3f;
					break;
				case TxPendingDrain.Step.FinalQuery:
					p.FinalQuerySent = true;
					p.QuerySent = true;
					SendQuery(p);
					p.Attempts++;
					p.Deadline = now + 3f;
					p.NextTryAt = now + 3f;
					TxLog.Warn("tx=" + p.TxId + " TIMEOUT op=" + p.Op.ToString() + " final query sent");
					break;
				}
			}, delegate(long txId)
			{
				FinalizeUnknownTx(txId);
			});
		}

		private static bool CanSendStoredMutationPayload(PendingTx p)
		{
			if (p != null && TxPendingDrain.CanSendStoredMutationPayload(p.IsTransientRetry))
			{
				return p.Payload != null;
			}
			return false;
		}

		private static void SendTransientRetry(PendingTx p)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			bool encodeSucceeded = false;
			try
			{
				if (p.Call != null)
				{
					p.Call.IsTransientRetry = true;
					ZPackage val = EncodeCall(p.Call, CurrentRevision(p.Container), LocalPlayerId(), LocalActorPos());
					ZPackage val2 = new ZPackage();
					val2.Write(p.TxId);
					val2.Write(val);
					p.Payload = val2;
					encodeSucceeded = true;
				}
				else
				{
					TxLog.Warn("tx=" + p.TxId + " transient re-encode skipped (no stored call), sending flagged query instead");
				}
			}
			catch (Exception ex)
			{
				TxLog.Warn("tx=" + p.TxId + " transient re-encode failed, sending flagged query instead (stored bytes never resent): " + ex.Message);
			}
			p.TransientAttempts++;
			if (TxPendingDrain.DecideTransientSend(encodeSucceeded) == TxPendingDrain.TransientSendAction.FlaggedMutation)
			{
				TxLog.Info("tx=" + p.TxId + " TRANSIENT-RETRY (flagged same-tx resend #" + p.TransientAttempts + ")");
				SendPayload(p.Container, p.Payload);
			}
			else
			{
				TxLog.Info("tx=" + p.TxId + " TRANSIENT-QUERY (flagged same-tx query #" + p.TransientAttempts + ", re-encode unavailable)");
				SendQuery(p);
			}
		}

		private static void SendQuery(PendingTx p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(3);
			val.Write(102);
			val.Write(0u);
			val.Write(LocalPlayerId());
			val.Write(LocalActorPos());
			val.Write(false);
			val.Write(false);
			val.Write(p.IsTransientRetry);
			ZPackage val2 = new ZPackage();
			val2.Write(p.TxId);
			val2.Write(val);
			TxLog.Info("tx=" + p.TxId + " QUERY (response was lost)");
			SendPayload(p.Container, val2);
		}

		internal static void RefreshNow(Container container)
		{
			ChestState state = GetState(container);
			if (state != null)
			{
				state.SeenRev = 0u;
				state.SeenOnce = false;
				state.SeenBytes = null;
			}
		}

		private static void PumpViewerRefresh()
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Expected O, but got Unknown
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup < _nextRefreshPoll)
			{
				return;
			}
			_nextRefreshPoll = Time.realtimeSinceStartup + 0.25f;
			InventoryGui instance = InventoryGui.instance;
			Container val = InventoryAccess.CurrentContainer(instance);
			int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0);
			if (num != _openInstanceId)
			{
				ChestState value = null;
				if (_openInstanceId != 0)
				{
					States.TryGetValue(_openInstanceId, out value);
				}
				if (value != null && (Object)(object)value.Container != (Object)null)
				{
					SendPresence(value.Container, TxOp.ViewerClose);
				}
				_openInstanceId = num;
				if ((Object)(object)val != (Object)null)
				{
					RefreshNow(val);
					SendPresence(val, TxOp.ViewerOpen);
					_nextPresenceAt = Time.realtimeSinceStartup + 5f;
				}
			}
			if ((Object)(object)val == (Object)null || !IsShared(val))
			{
				return;
			}
			if (Time.realtimeSinceStartup >= _nextPresenceAt)
			{
				SendPresence(val, TxOp.ViewerOpen);
				_nextPresenceAt = Time.realtimeSinceStartup + 5f;
			}
			ChestState state = GetState(val);
			if (state == null)
			{
				return;
			}
			ZNetView netView = TxReflect.GetNetView(val);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return;
			}
			uint dataRevision = netView.GetZDO().DataRevision;
			if (!TxHandoffGuard.ShouldApplyViewerRefresh(state.SeenOnce, state.SeenRev, dataRevision))
			{
				if (dataRevision < state.SeenRev)
				{
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer stale rev=" + dataRevision + " (seen " + state.SeenRev + "), skipped");
				}
				return;
			}
			byte[] array;
			try
			{
				array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null));
			}
			catch (Exception)
			{
				return;
			}
			if (array == null)
			{
				TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer null s_items at rev=" + dataRevision + " (keeping last-good, never presumed empty)");
				return;
			}
			if (SameBytes(array, state.SeenBytes))
			{
				state.SeenRev = dataRevision;
				return;
			}
			if (!TxSItemsGuard.TryValidate(array, out var reason))
			{
				TxLog.Warn("viewer refresh refused invalid s_items (" + reason + "), keeping last-good");
				return;
			}
			if ((Object)(object)instance != (Object)null)
			{
				TxReflect.CancelDragFrom(instance, val.GetInventory());
			}
			TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer dims before=" + val.GetInventory().GetWidth() + "x" + val.GetInventory().GetHeight());
			try
			{
				val.GetInventory().Load(new ZPackage(TxSItemsGuard.CloneBytes(array)));
				TxReflect.SetLastRevision(val, dataRevision);
				TxReflect.UpdateRows(val);
			}
			catch (Exception ex2)
			{
				TxLog.Warn("viewer refresh failed: " + ex2.Message);
				return;
			}
			state.SeenRev = dataRevision;
			state.SeenOnce = true;
			state.SeenBytes = TxSItemsGuard.CloneBytes(array);
			TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer refresh rev=" + dataRevision);
		}

		internal static bool SameBytes(byte[] a, byte[] b)
		{
			if (a == null || b == null)
			{
				return a == b;
			}
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		private static void SendPresence(Container container, TxOp op)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			if (!IsShared(container) || IsManager(container))
			{
				return;
			}
			ZNetView netView = TxReflect.GetNetView(container);
			if (!((Object)(object)netView == (Object)null) && netView.IsValid())
			{
				long num = IssueTxId();
				if (num != 0L)
				{
					ZPackage val = new ZPackage();
					val.Write(3);
					val.Write((int)op);
					val.Write(0u);
					val.Write(LocalPlayerId());
					val.Write(LocalActorPos());
					val.Write(false);
					val.Write(false);
					val.Write(false);
					ZPackage val2 = new ZPackage();
					val2.Write(num);
					val2.Write(val);
					SendPayload(container, val2);
				}
			}
		}

		private static void HandlePresence(Container container, long sender, TxOp op)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			ChestState state = GetState(container);
			if (state != null)
			{
				if (op == TxOp.ViewerOpen)
				{
					state.Viewers[sender] = Time.realtimeSinceStartup;
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer open peer=" + sender);
				}
				else
				{
					state.Viewers.Remove(sender);
					TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer close peer=" + sender);
				}
			}
		}

		private static void PumpManagerSlow()
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup < _nextSlowPump)
			{
				return;
			}
			_nextSlowPump = Time.realtimeSinceStartup + 0.5f;
			List<int> list = null;
			foreach (KeyValuePair<int, ChestState> state in States)
			{
				ChestState value = state.Value;
				if ((Object)(object)value.Container == (Object)null)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(state.Key);
					continue;
				}
				ZNetView netView = TxReflect.GetNetView(value.Container);
				if ((Object)(object)netView == (Object)null || !netView.IsValid())
				{
					continue;
				}
				bool flag = ServerAuthority.IsServerManagedContainer(value.Container);
				if (flag)
				{
					ServerAuthority.EnsureServerOwnership(value.Container, "pump");
				}
				long owner = netView.GetZDO().GetOwner();
				if (owner == ZNet.GetUID() && value.LastOwner != owner)
				{
					bool flag2 = false;
					try
					{
						flag2 = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
					}
					catch
					{
						flag2 = false;
					}
					if (TxAuthorityRouting.TakeoverAllowed(ServerAuthority.IsAuthorityMode(), flag, flag2))
					{
						Takeover(value);
					}
					else
					{
						TxLog.Warn("container=" + TxLog.Zid(value.ZdoId) + " server-authority: remote Takeover refused (no client handoff; manager stays the server)");
					}
				}
				value.LastOwner = owner;
				if (owner != ZNet.GetUID())
				{
					continue;
				}
				if (value.TxQuarantined && !TryReloadAuthoritative(value))
				{
					bool flag3 = false;
					try
					{
						flag3 = TxNullEscape.ShouldWarnQuarantine(value.QuarantineWarns);
					}
					catch
					{
						flag3 = true;
					}
					value.QuarantineWarns++;
					if (flag3)
					{
						TxLog.Warn("container=" + TxLog.Zid(value.ZdoId) + " still quarantined (pump reload failed, queue fail-closed, attempt=" + value.QuarantineWarns + ")");
					}
				}
				Drain(value);
				try
				{
					TxReflect.UpdateRows(value.Container);
				}
				catch (Exception)
				{
				}
				PruneViewers(value);
				EnforceLid(value);
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					States.Remove(list[i]);
				}
			}
		}

		private static void Takeover(ChestState state)
		{
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			long oldOwner = 0L;
			try
			{
				oldOwner = state.LastOwner;
			}
			catch
			{
			}
			try
			{
				ZNetView netView = TxReflect.GetNetView(state.Container);
				byte[] array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null));
				bool flag = false;
				string reason = null;
				if ((Object)(object)InventoryGui.instance != (Object)null)
				{
					TxReflect.CancelDragFrom(InventoryGui.instance, state.Container.GetInventory());
				}
				if (TxSItemsLoad.TryLoad(state.Container, array, out reason))
				{
					TxReflect.SetLastRevision(state.Container, netView.GetZDO().DataRevision);
					TxReflect.UpdateRows(state.Container);
					flag = true;
				}
				List<TxJob> list = DequeueAll(state);
				state.Processed.Clear();
				state.ProcOrder.Clear();
				RingData data = ReadRing(state.Container);
				SeedFromRing(state, data, ReadFloor(state.Container));
				TxStatus txStatus = TxStatus.Rejected;
				if (list.Count > 0)
				{
					List<long> list2 = new List<long>();
					foreach (TxJob item in list)
					{
						if (SeedReject(state, item.TxId, (item.Call != null) ? item.Call.Op : TxOp.Query, item.Sender, advanceFloor: true))
						{
							list2.Add(item.TxId);
						}
					}
					bool ringPersisted = WriteRing(state);
					bool floorPersisted = WriteFloor(state);
					txStatus = TxDecision.HandoffDropTerminal(ringPersisted, floorPersisted);
					if (txStatus == TxStatus.UnknownTx)
					{
						TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover drops not persisted (indeterminate)");
						foreach (long item2 in list2)
						{
							EvictSeededReject(state, item2);
						}
					}
					RequestPropagation(state);
				}
				foreach (TxJob item3 in list)
				{
					AnswerReject(state, item3, txStatus);
				}
				state.Viewers.Clear();
				state.SeenRev = 0u;
				state.SeenOnce = false;
				state.SeenBytes = null;
				if (flag)
				{
					state.TxQuarantined = false;
					state.QuarantinedSince = 0f;
					state.QuarantineOldOwner = 0L;
					state.QuarantineWarns = 0;
				}
				else
				{
					StampQuarantine(state, oldOwner);
					if (array == null)
					{
						NoteSItemsNull("takeover");
						TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover without authoritative reload (null s_items), staying quarantined");
					}
					else
					{
						TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover without authoritative reload (invalid s_items: " + reason + "), staying quarantined");
					}
				}
				TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " manager changed old=" + state.LastOwner + " new=" + ZNet.GetUID() + " revision=" + netView.GetZDO().DataRevision);
			}
			catch (Exception ex)
			{
				TxLog.Error("takeover failed: " + ex.Message);
				try
				{
					StampQuarantine(state, oldOwner);
				}
				catch
				{
				}
			}
		}

		private static void PruneViewers(ChestState state)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			List<long> list = null;
			foreach (KeyValuePair<long, float> viewer in state.Viewers)
			{
				if (realtimeSinceStartup - viewer.Value > 15f)
				{
					if (list == null)
					{
						list = new List<long>();
					}
					list.Add(viewer.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					state.Viewers.Remove(list[i]);
				}
			}
		}

		private static void EnforceLid(ChestState state)
		{
			try
			{
				bool inUse = state.Viewers.Count > 0 || IsOpenByMe(state.Container);
				state.Container.SetInUse(inUse);
			}
			catch (Exception)
			{
			}
		}

		private static bool IsOpenByMe(Container container)
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible())
			{
				return false;
			}
			return (Object)(object)InventoryAccess.CurrentContainer(instance) == (Object)(object)container;
		}

		internal static void Pump()
		{
			if (Plugin.IsActive)
			{
				TxNet.PumpHello();
				ServerChestDirector.PumpPing();
				TxDestroy.Pump();
				PumpAuthoritySweep();
				PumpManagerSlow();
				ServerChestManager.Pump();
				PumpPending();
				PumpViewerRefresh();
			}
		}

		private static uint CurrentRevision(Container container)
		{
			ZNetView netView = TxReflect.GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return 0u;
			}
			return netView.GetZDO().DataRevision;
		}

		private static Vector3 LocalActorPos()
		{
			//IL_001b: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Component)localPlayer).transform.position;
		}

		private static long LocalPlayerId()
		{
			Game instance = Game.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0L;
			}
			PlayerProfile playerProfile = instance.GetPlayerProfile();
			if (playerProfile == null)
			{
				return 0L;
			}
			return playerProfile.GetPlayerID();
		}

		private static void TellPlayer(string message)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		internal static void Reset()
		{
			States.Clear();
			Pending.Clear();
			_clientCounter = 1u;
			_counterReservedCeil = 0u;
			SItemsNullDenials = 0L;
			LoadReservedCounter();
			_nextSlowPump = 0f;
			_nextRefreshPoll = 0f;
			_nextAuthoritySweep = 0f;
			_nextAuthoritySweepHeartbeat = 0f;
			_openInstanceId = 0;
			_nextPresenceAt = 0f;
		}

		internal static void OnContainerAwake(Container container)
		{
			OnContainerAwake(container, fromSweep: false);
		}

		internal static void OnContainerAwake(Container container, bool fromSweep)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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)
			try
			{
				bool flag = false;
				try
				{
					flag = ModConfig.TxVerbose.Value;
				}
				catch
				{
					flag = false;
				}
				if (flag)
				{
					Plugin.LogInstance.LogInfo((object)("[ChestTX] container awake: " + (((Object)(object)container == (Object)null) ? "null" : ((Object)container).name)));
				}
			}
			catch
			{
			}
			if ((Object)(object)container == (Object)null)
			{
				return;
			}
			ZNetView netView = TxReflect.GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return;
			}
			try
			{
				netView.Register<ZPackage>("BestAutoSort_TxRequest", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
				{
					OnTxRequest(container, sender, pkg);
				});
				netView.Register<ZPackage>("BestAutoSort_TxResponse", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
				{
					OnTxResponse(container, sender, pkg);
				});
			}
			catch (Exception ex)
			{
				TxLog.Warn("container " + TxLog.Zid(netView.GetZDO().m_uid) + " RPC register failed: " + ex.Message);
			}
			int instanceID = ((Object)container).GetInstanceID();
			if (!States.ContainsKey(instanceID))
			{
				ChestState chestState = new ChestState();
				chestState.Container = container;
				chestState.ZdoId = netView.GetZDO().m_uid;
				chestState.LastOwner = netView.GetZDO().GetOwner();
				States[instanceID] = chestState;
			}
			AutoFeedService.Attach(container);
			ServerAuthority.EnsureOnAwake(container, fromSweep);
			TxRemoteUpgradeOnAwake(container);
		}

		private static void PumpAuthoritySweep()
		{
			try
			{
				if (!Plugin.IsActive)
				{
					return;
				}
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (realtimeSinceStartup < _nextAuthoritySweep)
				{
					return;
				}
				_nextAuthoritySweep = realtimeSinceStartup + 15f;
				bool flag = false;
				try
				{
					ZNet instance = ZNet.instance;
					flag = (Object)(object)instance != (Object)null && instance.IsServer();
				}
				catch
				{
					flag = false;
				}
				if (!flag)
				{
					return;
				}
				bool flag2 = false;
				try
				{
					flag2 = ServerAuthority.IsAuthorityMode();
				}
				catch
				{
					flag2 = false;
				}
				if (!flag2)
				{
					return;
				}
				Container[] array = null;
				try
				{
					array = Object.FindObjectsOfType<Container>();
				}
				catch
				{
					array = null;
				}
				if (array == null)
				{
					return;
				}
				int num = array.Length;
				int num2 = 0;
				int num3 = 0;
				Container[] array2 = array;
				foreach (Container val in array2)
				{
					try
					{
						if ((Object)(object)val == (Object)null || !ServerAuthority.IsServerManagedContainer(val))
						{
							continue;
						}
						num2++;
						if (GetState(val) == null)
						{
							OnContainerAwake(val, fromSweep: true);
							if (GetState(val) != null)
							{
								num3++;
							}
						}
					}
					catch
					{
					}
				}
				bool flag3 = false;
				if (realtimeSinceStartup >= _nextAuthoritySweepHeartbeat)
				{
					_nextAuthoritySweepHeartbeat = realtimeSinceStartup + 300f;
					flag3 = true;
				}
				if (!(num3 > 0 || flag3))
				{
					return;
				}
				try
				{
					Plugin.LogInstance.LogInfo((object)("[ChestTX] server-authority: discovery sweep found=" + num + " managed=" + num2 + " registered=" + num3 + " known=" + States.Count));
				}
				catch
				{
				}
			}
			catch
			{
			}
		}

		internal static bool IsSharedVerbose(Container container, out string why)
		{
			why = "ok";
			if (!ModConfig.AllowConcurrentChestUse.Value)
			{
				why = "AllowConcurrentChestUse disabled";
				return false;
			}
			if ((Object)(object)container == (Object)null)
			{
				why = "null container";
				return false;
			}
			if (!((Behaviour)container).isActiveAndEnabled)
			{
				why = "behaviour disabled";
				return false;
			}
			if (AutoFeedService.IsLocked(container))
			{
				why = "autofeed lease/block active";
				return false;
			}
			if ((Object)(object)container.m_wagon != (Object)null)
			{
				why = "wagon cart";
				return false;
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
			{
				why = "ship";
				return false;
			}
			if ((Object)(object)((Component)container).GetComponent<Player>() != (Object)null)
			{
				why = "player inventory";
				return false;
			}
			if ((Object)(object)((Component)container).GetComponent<TombStone>() != (Object)null)
			{
				why = "tombstone";
				return false;
			}
			ZNetView netView = TxReflect.GetNetView(container);
			if ((Object)(object)netView == (Object)null)
			{
				why = "no netview";
				return false;
			}
			if (!netView.IsValid())
			{
				why = "netview invalid";
				return false;
			}
			return true;
		}

		internal static bool IsShared(Container container)
		{
			string why;
			return IsSharedVerbose(container, out why);
		}

		internal static bool IsManager(Container container)
		{
			try
			{
				if (ServerAuthority.IsAuthorityMode() && ServerAuthority.IsServerManagedContainer(container))
				{
					return ServerAuthority.IsAuthorityManager(container);
				}
			}
			catch
			{
			}
			return container.IsOwner();
		}

		internal static bool IsQuarantined(Container container)
		{
			try
			{
				return GetState(container)?.TxQuarantined ?? false;
			}
			catch
			{
				return false;
			}
		}

		internal static ChestState GetState(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			States.TryGetValue(((Object)container).GetInstanceID(), out var value);
			return value;
		}

		internal static bool HasPendingFor(Container container)
		{
			try
			{
				if ((Object)(object)container == (Object)null)
				{
					return false;
				}
				foreach (PendingTx value in Pending.Values)
				{
					try
					{
						if (value != null && (Object)(object)value.Container == (Object)(object)container)
						{
							return true;
						}
					}
					catch
					{
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static void RequestAdd(Container container, Inventory srcInv, ItemData item, int amount, int wantX, int wantY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			RequestAdd(container, srcInv, item, amount, wantX, wantY, onDone, alreadyRemoved: false);
		}

		internal static void RequestAdd(Container container, Inventory srcInv, ItemData item, int amount, int wantX, int wantY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, bool alreadyRemoved)
		{
			Inventory val = (((Object)(object)container != (Object)null) ? container.GetInventory() : null);
			if (val != null && wantX >= 0 && wantY >= 0 && (wantX >= val.GetWidth() || wantY >= val.GetHeight()))
			{
				TxLog.Info("want-clamped (" + wantX + "," + wantY + ") vs " + val.GetWidth() + "x" + val.GetHeight());
				wantX = -1;
				wantY = -1;
			}
			TxOpItem opItem = SnapshotItem(item, amount, wantX, wantY);
			if (opItem == null)
			{
				TellPlayer("Item cannot be sent (no prefab).");
				return;
			}
			if (wantX < 0 || wantY < 0)
			{
				opItem.X = -1;
				opItem.Y = -1;
			}
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.Add;
			txOpCall.Items.Add(opItem);
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				int accepted = ReadAcceptedAt(pkg, 0);
				CompleteAdd(srcInv, item, opItem, accepted, status, rev, container, onDone, alreadyRemoved, disp);
			}, 0L, null, 1);
		}

		internal static void RequestAddBatch(Container container, Inventory srcInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			if (items == null || items.Count == 0)
			{
				return;
			}
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.AddBatch;
			txOpCall.Items.AddRange(items);
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				switch (disp)
				{
				case TxCompletionKind.Indeterminate:
					TxLog.Warn("addbatch indeterminate: outcome unknown, nothing removed — check the chest before retrying");
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(EmptyCountBody(), status, rev, disp);
					}
					break;
				case TxCompletionKind.CommittedMultiAddDetailsUnavailable:
					TxLog.Warn("addbatch details-unavailable: committed but unattributed, nothing removed");
					TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying.");
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(EmptyCountBody(), status, rev, disp);
					}
					break;
				default:
				{
					List<int> list = ReadAcceptedList(pkg, items.Count);
					for (int i = 0; i < items.Count && i < list.Count; i++)
					{
						CompleteAdd(srcInv, null, items[i], list[i], status, rev, container, null, alreadyRemoved: false, disp);
					}
					RefreshNow(container);
					if (onDone != null)
					{
						onDone(pkg, status, rev, disp);
					}
					break;
				}
				}
			}, 0L, null, items.Count);
		}

		internal static TxOpItem SnapshotAuto(ItemData item, int amount)
		{
			TxOpItem txOpItem = SnapshotItem(item, amount, -1, -1);
			if (txOpItem != null)
			{
				txOpItem.X = -1;
				txOpItem.Y = -1;
			}
			return txOpItem;
		}

		internal static TxOpItem SnapshotItem(ItemData item, int amount, int wantX, int wantY)
		{
			if (item == null || item.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null || amount <= 0)
			{
				return null;
			}
			ItemData val = item.Clone();
			val.m_stack = amount;
			TxOpItem txOpItem = new TxOpItem();
			txOpItem.Snapshot = val;
			txOpItem.SourceRef = item;
			txOpItem.PrefabHash = StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name);
			txOpItem.Amount = amount;
			txOpItem.X = item.m_gridPos.x;
			txOpItem.Y = item.m_gridPos.y;
			txOpItem.MaxStack = item.m_shared.m_maxStackSize;
			if (wantX >= 0 && wantY >= 0)
			{
				txOpItem.X = wantX;
				txOpItem.Y = wantY;
			}
			return txOpItem;
		}

		internal static void RequestTake(Container container, Inventory dstInv, ItemData snapshot, int amount, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, int wantDstX = -1, int wantDstY = -1)
		{
			TxOpItem txOpItem = SnapshotItem(snapshot, amount, -1, -1);
			if (txOpItem != null)
			{
				TxOpCall txOpCall = new TxOpCall();
				txOpCall.Op = TxOp.Take;
				txOpCall.Items.Add(txOpItem);
				Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
				{
					CompleteTake(dstInv, pkg, status, rev, container, onDone, wantDstX, wantDstY, null, disp);
				}, 0L);
			}
		}

		internal static void RequestTakeBatchChunked(Container container, Inventory dstInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			if (items == null || items.Count == 0)
			{
				return;
			}
			if (items.Count <= 4)
			{
				RequestTakeBatch(container, dstInv, items, onDone);
				return;
			}
			for (int i = 0; i < items.Count; i += 4)
			{
				List<TxOpItem> list = new List<TxOpItem>();
				for (int j = i; j < items.Count && j < i + 4; j++)
				{
					list.Add(items[j]);
				}
				RequestTakeBatch(container, dstInv, list, onDone);
			}
		}

		internal static void RequestTakeBatch(Container container, Inventory dstInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			if (items != null && items.Count != 0)
			{
				TxOpCall txOpCall = new TxOpCall();
				txOpCall.Op = TxOp.TakeBatch;
				txOpCall.Items.AddRange(items);
				Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
				{
					CompleteTake(dstInv, pkg, status, rev, container, onDone, -1, -1, null, disp);
				}, 0L);
			}
		}

		internal static void RequestMove(Container container, ItemData snapshot, int amount, int dstX, int dstY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone)
		{
			TxOpItem txOpItem = SnapshotItem(snapshot, amount, -1, -1);
			if (txOpItem == null)
			{
				return;
			}
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.Move;
			txOpCall.Items.Add(txOpItem);
			txOpCall.DstX = dstX;
			txOpCall.DstY = dstY;
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				RefreshNow(container);
				switch (status)
				{
				case TxStatus.UnknownTx:
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					break;
				case TxStatus.Rejected:
					TellPlayer("The shared chest changed. Try that item move again.");
					break;
				}
				if (onDone != null)
				{
					onDone(pkg, status, rev, disp);
				}
			}, 0L);
		}

		internal static void RequestSort(Container container, int mode, bool desc)
		{
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.Sort;
			txOpCall.Mode = mode;
			txOpCall.Desc = desc;
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				RefreshNow(container);
				switch (status)
				{
				case TxStatus.UnknownTx:
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					break;
				default:
					TellPlayer("Chest sort failed. Try again.");
					break;
				case TxStatus.Accepted:
				case TxStatus.Duplicate:
					break;
				}
			}, 0L);
		}

		internal static void RequestSetRule(Container container, string rule)
		{
			TxOpCall txOpCall = new TxOpCall();
			txOpCall.Op = TxOp.SetRule;
			txOpCall.Rule = rule ?? string.Empty;
			Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp)
			{
				RefreshNow(container);
				switch (status)
				{
				case TxStatus.UnknownTx:
					TellPlayer("Chest request outcome unknown. Check the chest before retrying.");
					break;
				default:
					TellPlayer("Chest rule was not saved. Try again.");
					break;
				case TxStatus.Accepted:
				case TxStatus.Duplicate:
					break;
				}
			}, 0L);
		}

		internal static void MutateLocal(Container container, TxOpCall call, Action<StoredResult> onDone, long playerId = 0L, Vector3? actorPos = null)
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			ServerAuthority.EnsureServerOwnership(container, "local-tx");
			ChestState state = GetState(container);
			if (state == null || !IsManager(container))
			{
				if (onDone != null)
				{
					StoredResult storedResult = new StoredResult();
					storedResult.Status = TxStatus.Rejected;
					onDone(storedResult);
				}
				return;
			}
			if (!TryClaimAddItems(call, out var claimed))
			{
				TxLog.Info("local add skipped: all items already in flight");
				if (onDone != null)
				{
					StoredResult storedResult2 = new StoredResult();
					storedResult2.Op = call.Op;
					storedResult2.Status = TxStatus.Accepted;
					storedResult2.Revision = CurrentRevision(container);
					onDone(storedResult2);
				}
				return;
			}
			try
			{
				TxJob txJob = new TxJob();
				txJob.Container = container;
				txJob.IsLocal = true;
				txJob.TxId = NextLocalTxId();
				if (txJob.TxId == 0L)
				{
					TxLog.Warn("local tx refused: counter exhausted (fail closed)");
					if (onDone != null)
					{
						StoredResult storedResult3 = new StoredResult();
						storedResult3.Op = call.Op;
						storedResult3.Status = TxStatus.Rejected;
						storedResult3.Revision = CurrentRevision(container);
						onDone(storedResult3);
					}
					return;
				}
				txJob.Sender = ZNet.GetUID();
				txJob.PlayerId = ((playerId != 0L) ? playerId : LocalPlayerId());
				txJob.ActorPos = (actorPos.HasValue ? actorPos.Value : LocalActorPos());
				txJob.BaseRev = CurrentRevision(container);
				txJob.Call = call;
				txJob.Complete = onDone;
				if (state.RingCorrupt && state.FloorCorrupt)
				{
					TxLog.Warn("tx=" + txJob.TxId + " local refused: ring corrupt (fail closed)");
					if (onDone != null)
					{
						StoredResult storedResult4 = new StoredResult();
						storedResult4.Op = call.Op;
						storedResult4.Status = TxStatus.UnknownTx;
						storedResult4.Revision = CurrentRevision(container);
						onDone(storedResult4);
					}
				}
				else if (state.TxQuarantined)
				{
					bool anyDurable;
					TxStatus txStatus = PersistQuarantineRefusal(state, txJob.TxId, call.Op, txJob.Sender, out anyDurable);
					if (txStatus == TxStatus.Rejected)
					{
						RequestPropagation(state, txJob.Sender);
					}
					TxLog.Warn("tx=" + txJob.TxId + " local refused: quarantined after post-fence failure (status=" + txStatus.ToString() + ", durable=" + anyDurable + ")");
					if (onDone != null)
					{
						StoredResult storedResult5 = new StoredResult();
						storedResult5.Op = call.Op;
						storedResult5.Status = txStatus;
						storedResult5.Revision = CurrentRevision(container);
						onDone(storedResult5);
					}
				}
				else if (IsFloorStale(state, txJob.TxId))
				{
					TxLog.Warn("tx=" + txJob.TxId + " local refused: at/below execution floor (indeterminate)");
					if (onDone != null)
					{
						StoredResult storedResult6 = new StoredResult();
						storedResult6.Op = call.Op;
						storedResult6.Status = TxStatus.UnknownTx;
						storedResult6.Revision = CurrentRevision(container);
						onDone(storedResult6);
					}
				}
				else if (IsFloorCappedFor(state, txJob.TxId))
				{
					TxLog.Warn("tx=" + txJob.TxId + " local refused: execution floor at capacity (fail closed)");
					if (onDone != null)
					{
						StoredResult storedResult7 = new StoredResult();
						storedResult7.Op = call.Op;
						storedResult7.Status = TxStatus.UnknownTx;
						storedResult7.Revision = CurrentRevision(container);
						onDone(storedResult7);
					}
				}
				else
				{
					state.Queue.Enqueue(txJob);
					Drain(state);
				}
			}
			finally
			{
				ReleaseClaimed(claimed);
			}
		}

		internal static void DrainForLocal(Container container)
		{
			ChestState state = GetState(container);
			if (state != null && IsManager(container))
			{
				Drain(state);
			}
		}

		internal static bool AcquireForStructural(Container container)
		{
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			if (!IsShared(container))
			{
				return false;
			}
			ServerAuthority.EnsureServerOwnership(container, "structural-acquire");
			if (ServerAuthority.BlockStructuralForRemote(container, "acquire"))
			{
				return false;
			}
			if (IsManager(container))
			{
				return true;
			}
			ZNetView netView = TxReflect.GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return false;
			}
			try
			{
				long oldOwner = 0L;
				try
				{
					oldOwner = netView.GetZDO().GetOwner();
				}
				catch
				{
				}
				netView.ClaimOwnership();
				if (!container.IsOwner())
				{
					return false;
				}
				byte[] array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null));
				if (!container.IsOwner())
				{
					return false;
				}
				bool flag = false;
				string reason = null;
				if (TxSItemsLoad.TryLoad(container, array, out reason))
				{
					TxReflect.SetLastRevision(container, netView.GetZDO().DataRevision);
					TxReflect.UpdateRows(container);
					flag = true;
				}
				ChestState state = GetState(container);
				if (state != null)
				{
					List<TxJob> list = DequeueAll(state);
					state.Processed.Clear();
					state.ProcOrder.Clear();
					SeedFromRing(state, ReadRing(container), ReadFloor(container));
					TxStatus txStatus = TxStatus.Rejected;
					if (list.Count > 0)
					{
						List<long> list2 = new List<long>();
						foreach (TxJob item in list)
						{
							if (SeedReject(state, item.TxId, (item.Call != null) ? item.Call.Op : TxOp.Query, item.Sender, advanceFloor: true))
							{
								list2.Add(item.TxId);
							}
						}
						bool ringPersisted = WriteRing(state);
						bool floorPersisted = WriteFloor(state);
						txStatus = TxDecision.HandoffDropTerminal(ringPersisted, floorPersisted);
						if (txStatus == TxStatus.UnknownTx)
						{
							TxLog.Warn("structural acquire drops not persisted (indeterminate)");
							foreach (long item2 in list2)
							{
								EvictSeededReject(state, item2);
							}
						}
						RequestPropagation(state);
					}
					foreach (TxJob item3 in list)
					{
						AnswerReject(state, item3, txStatus);
					}
					state.LastOwner = ZNet.GetUID();
					if (flag)
					{
						state.TxQuarantined = false;
						state.QuarantinedSince = 0f;
						state.QuarantineOldOwner = 0L;
						state.QuarantineWarns = 0;
					}
					else
					{
						StampQuarantine(state, oldOwner);
						if (array == null)
						{
							NoteSItemsNull("structural-acquire");
							TxLog.Warn("structural acquire without authoritative reload (null s_items), staying quarantined");
						}
						else
						{
							TxLog.Warn("structural acquire without authoritative reload (invalid s_items: " + reason + "), staying quarantined");
						}
					}
				}
				TxLog.Info("container=" + TxLog.Zid(netView.GetZDO().m_uid) + " structural acquire by " + ZNet.GetUID());
				return true;
			}
			catch (Exception ex)
			{
				TxLog.Warn("structural acquire failed: " + ex.Message);
				return false;
			}
		}

		internal static long IssueTxId()
		{
			if (!ReserveTxCounter())
			{
				return 0L;
			}
			if (!TxIdGen.TryNext(ZNet.GetUID(