Decompiled source of RunicDisplayStands v1.3.8

RunicDisplayStands.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RunicDisplayStands")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.8.0")]
[assembly: AssemblyInformationalVersion("1.3.8")]
[assembly: AssemblyProduct("RunicDisplayStands")]
[assembly: AssemblyTitle("RunicDisplayStands")]
[assembly: AssemblyVersion("1.3.8.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RunicDisplayStands
{
	public class ContainerBridge : MonoBehaviour
	{
		internal sealed class Transfer
		{
			private static readonly FieldInfo[] EquipmentFields = new string[11]
			{
				"m_leftItem", "m_rightItem", "m_chestItem", "m_legItem", "m_helmetItem", "m_shoulderItem", "m_utilityItem", "m_trinketItem", "m_ammoItem", "m_hiddenLeftItem",
				"m_hiddenRightItem"
			}.Select((string name) => AccessTools.Field(typeof(Humanoid), name)).ToArray();

			private static readonly MethodInfo SetupEquipmentMethod = AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null);

			private readonly ContainerBridge _bridge;

			private readonly InventorySnapshot _playerSnapshot;

			private readonly InventorySnapshot _standSnapshot;

			private readonly ItemData[] _equipped;

			private readonly string _contents;

			private readonly Action _playerChanged;

			private readonly Action _standChanged;

			private bool _finished;

			internal Transfer(ContainerBridge bridge)
			{
				_bridge = bridge;
				Inventory inventory = ((Humanoid)bridge._user).GetInventory();
				_playerSnapshot = new InventorySnapshot(inventory);
				_standSnapshot = new InventorySnapshot(bridge._inventory);
				_equipped = ((IEnumerable<FieldInfo>)EquipmentFields).Select((Func<FieldInfo, ItemData>)((FieldInfo field) => (ItemData)field.GetValue(bridge._user))).ToArray();
				_contents = InventorySnapshot.Contents(inventory.GetAllItems().Concat(bridge._inventory.GetAllItems()));
				_playerChanged = inventory.m_onChanged;
				_standChanged = bridge._inventory.m_onChanged;
				inventory.m_onChanged = null;
				bridge._inventory.m_onChanged = null;
			}

			internal bool Finish(bool actionSucceeded)
			{
				if (_finished)
				{
					return false;
				}
				_finished = true;
				bool flag = false;
				Inventory playerInventory = ((Humanoid)_bridge._user).GetInventory();
				try
				{
					flag = TransferBoundary.Complete(actionSucceeded, () => _bridge.HasAuthority && _bridge._baseline == _bridge.StandContents(_bridge.ReadStand()) && _contents == InventorySnapshot.Contents(playerInventory.GetAllItems().Concat(_bridge._inventory.GetAllItems())), () => _bridge.CommitStand(), delegate
					{
						_playerSnapshot.Restore();
						_standSnapshot.Restore();
						for (int i = 0; i < EquipmentFields.Length; i++)
						{
							EquipmentFields[i].SetValue(_bridge._user, _equipped[i]);
						}
						try
						{
							SetupEquipmentMethod.Invoke(_bridge._user, null);
						}
						catch (Exception ex4)
						{
							ManualLogSource log3 = Plugin.Log;
							if (log3 != null)
							{
								log3.LogWarning((object)("Equipment restored; visual refresh failed: " + ex4.Message));
							}
						}
					});
					if (!flag)
					{
						_bridge.Warn("Transfer canceled; items stayed in their original inventories. Reopen the stand if its ownership changed.");
					}
				}
				catch (Exception ex)
				{
					_bridge.Warn("Transfer canceled: " + ex.Message);
				}
				finally
				{
					playerInventory.m_onChanged = _playerChanged;
					_bridge._inventory.m_onChanged = _standChanged;
					_bridge._transfer = null;
					try
					{
						InventoryChangedMethod.Invoke(playerInventory, new object[2] { flag, false });
						InventoryChangedMethod.Invoke(_bridge._inventory, new object[2] { flag, false });
					}
					catch (Exception ex2)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("Inventory refresh failed: " + ex2.Message));
						}
					}
				}
				if (flag)
				{
					_bridge._baseline = _bridge.StandContents(_bridge._inventory.GetAllItems());
					try
					{
						_bridge.PublishVisuals();
					}
					catch (Exception ex3)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)("Stand transfer saved; visual refresh failed: " + ex3.Message));
						}
					}
				}
				return flag;
			}
		}

		private static ContainerBridge _opened;

		private static StandVirtualContainer _virtualContainer;

		private static bool _stackAllButtonOverridden;

		private static bool _stackAllButtonWasActive;

		private static string _stackAllButtonText;

		private static readonly FieldInfo ContainerInventoryField = AccessTools.Field(typeof(Container), "m_inventory");

		private static readonly MethodInfo InventoryChangedMethod = AccessTools.DeclaredMethod(typeof(Inventory), "Changed", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null);

		private Inventory _inventory;

		private ItemStand _itemStand;

		private ArmorStand _armorStand;

		private Player _user;

		private string _baseline;

		private bool _refreshing;

		private Transfer _transfer;

		internal static ContainerBridge Opened => _opened;

		internal bool InTransfer => _transfer != null;

		internal Inventory StandInventory => _inventory;

		internal bool IsArmorStand => (Object)(object)_armorStand != (Object)null;

		internal bool HasAuthority
		{
			get
			{
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_user != (Object)null && (Object)(object)_user == (Object)(object)Player.m_localPlayer && !((Character)_user).IsTeleporting() && (Object)(object)((Component)this).GetComponent<ZNetView>() != (Object)null && ((Component)this).GetComponent<ZNetView>().IsValid() && ((Component)this).GetComponent<ZNetView>().IsOwner())
				{
					return PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false);
				}
				return false;
			}
		}

		internal static bool IsStandInventory(Inventory inventory)
		{
			if ((Object)(object)_opened != (Object)null)
			{
				return _opened._inventory == inventory;
			}
			return false;
		}

		public static ContainerBridge GetOrCreate(GameObject go)
		{
			ContainerBridge component = go.GetComponent<ContainerBridge>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			component = go.AddComponent<ContainerBridge>();
			component.Initialize();
			return component;
		}

		private void Initialize()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			_itemStand = ((Component)this).GetComponent<ItemStand>();
			_armorStand = ((Component)this).GetComponent<ArmorStand>();
			string text = (((Object)(object)_itemStand != (Object)null) ? _itemStand.GetHoverName() : "$piece_armorstand");
			_inventory = new Inventory(text, (Sprite)null, (!((Object)(object)_armorStand != (Object)null)) ? 1 : 5, (!((Object)(object)_armorStand != (Object)null)) ? 1 : 2);
			Inventory inventory = _inventory;
			inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged));
		}

		private List<ItemData> ReadStand()
		{
			if ((Object)(object)_armorStand != (Object)null)
			{
				return ArmorStandAdapter.GetPlacedItems(_armorStand);
			}
			return new List<ItemData> { ItemStandAdapter.GetPlacedItem(_itemStand) };
		}

		private string StandContents(IEnumerable<ItemData> items)
		{
			if (!((Object)(object)_armorStand == (Object)null))
			{
				return string.Join("\n", from i in items.Where((ItemData i) => i != null).OrderBy(ArmorStandSlots.Slot)
					select ArmorStandSlots.Slot(i) + ":" + InventorySnapshot.Contents((IEnumerable<ItemData>)(object)new ItemData[1] { i }));
			}
			return InventorySnapshot.Contents(items);
		}

		public bool OpenAsContainer(Humanoid user)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)InventoryGui.instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_opened != (Object)null)
			{
				return false;
			}
			_user = val;
			if (!TryClaimOwnership())
			{
				Warn("The stand is synchronizing. Please try again.");
				return false;
			}
			try
			{
				RefreshInventory();
			}
			catch (Exception ex)
			{
				Warn(ex.Message);
				return false;
			}
			_opened = this;
			if ((Object)(object)_virtualContainer == (Object)null)
			{
				GameObject val2 = new GameObject("RunicDisplayStandsVirtualContainer");
				val2.SetActive(false);
				_virtualContainer = val2.AddComponent<StandVirtualContainer>();
			}
			((Container)_virtualContainer).m_name = _inventory.GetName();
			((Container)_virtualContainer).m_width = _inventory.GetWidth();
			((Container)_virtualContainer).m_height = _inventory.GetHeight();
			ContainerInventoryField.SetValue(_virtualContainer, _inventory);
			((Component)_virtualContainer).transform.SetParent(((Component)this).transform, false);
			InventoryGui.instance.Show((Container)(object)_virtualContainer, 1);
			SetUseEquipButton(useEquip: true);
			return true;
		}

		public bool TakeOne(Humanoid user)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)_opened != (Object)null)
			{
				return false;
			}
			_user = val;
			if (!TryClaimOwnership())
			{
				return false;
			}
			try
			{
				RefreshInventory();
				return TakeItem(_inventory.GetItemAt(0, 0), use: false);
			}
			catch (Exception ex)
			{
				Warn(ex.Message);
				return false;
			}
		}

		internal bool TakeItem(ItemData item, bool use)
		{
			if (item == null || !_inventory.ContainsItem(item))
			{
				return false;
			}
			ItemData received = item.Clone();
			received.m_stack = 1;
			received.m_equipped = false;
			bool flag = Execute(delegate
			{
				if (!((Humanoid)_user).GetInventory().AddItem(received))
				{
					throw new InvalidOperationException("There is no available inventory slot for that item.");
				}
				_inventory.RemoveOneItem(item);
			});
			if (flag && use)
			{
				try
				{
					((Humanoid)_user).EquipItem(received, false);
				}
				catch (Exception ex)
				{
					Warn("Item transferred, but could not equip it: " + ex.Message);
				}
			}
			return flag;
		}

		internal void SwapEquip()
		{
			if ((Object)(object)_user == (Object)null)
			{
				return;
			}
			if ((Object)(object)_itemStand != (Object)null)
			{
				TakeItem(_inventory.GetItemAt(0, 0), use: true);
				return;
			}
			ItemData[] outgoing = ArmorStandAdapter.PlayerLoadout(_user, _inventory);
			Execute(delegate
			{
				LoadoutSwap.Apply(((Humanoid)_user).GetInventory(), _inventory, outgoing, delegate(ItemData item)
				{
					((Humanoid)_user).UnequipItem(item, false);
				}, (ItemData item) => ((Humanoid)_user).IsItemEquiped(item) || ((Humanoid)_user).EquipItem(item, false), RunicInventorySlots.Capture(_user));
			});
		}

		private bool Execute(Action action)
		{
			Transfer transfer = null;
			try
			{
				transfer = BeginTransfer();
				action();
				return transfer.Finish(actionSucceeded: true);
			}
			catch (Exception ex)
			{
				transfer?.Finish(actionSucceeded: false);
				Warn(ex.Message);
				return false;
			}
		}

		internal Transfer BeginTransfer()
		{
			if (_transfer != null)
			{
				throw new InvalidOperationException("Another stand action is in progress.");
			}
			if (!HasAuthority || _baseline != StandContents(ReadStand()))
			{
				throw new InvalidOperationException("The stand changed or access was lost. Reopen it and try again.");
			}
			return _transfer = new Transfer(this);
		}

		private bool CommitStand()
		{
			List<ItemData> allItems = _inventory.GetAllItems();
			StandWriteBatch standWriteBatch;
			if ((Object)(object)_itemStand != (Object)null)
			{
				if (allItems.Count > 1)
				{
					throw new InvalidOperationException("An item stand holds one item.");
				}
				standWriteBatch = ItemStandAdapter.PrepareWrite(_itemStand, (allItems.Count == 0) ? null : allItems[0]);
			}
			else
			{
				standWriteBatch = ArmorStandAdapter.PrepareWrite(_armorStand, allItems);
			}
			if (!HasAuthority || _baseline != StandContents(ReadStand()))
			{
				return false;
			}
			return standWriteBatch.Commit();
		}

		private void PublishVisuals()
		{
			List<ItemData> allItems = _inventory.GetAllItems();
			if ((Object)(object)_itemStand != (Object)null)
			{
				ItemStandAdapter.PublishVisual(_itemStand, (allItems.Count == 0) ? null : allItems[0]);
			}
			else
			{
				ArmorStandAdapter.PublishVisuals(_armorStand, allItems);
			}
		}

		internal static void Closed()
		{
			if (!((Object)(object)_opened == (Object)null) && _opened._transfer == null)
			{
				_opened = null;
				SetUseEquipButton(useEquip: false);
				if ((Object)(object)_virtualContainer != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_virtualContainer).gameObject);
					_virtualContainer = null;
				}
			}
		}

		private void RefreshInventory()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			List<ItemData> list = ReadStand();
			_baseline = StandContents(list);
			_refreshing = true;
			try
			{
				_inventory.GetAllItems().Clear();
				for (int i = 0; i < list.Count; i++)
				{
					if (list[i] != null)
					{
						list[i].m_gridPos = StandGridLayout.Position(i, _inventory.GetWidth());
						list[i].m_equipped = false;
						_inventory.GetAllItems().Add(list[i]);
					}
				}
			}
			finally
			{
				_refreshing = false;
			}
		}

		private void OnInventoryChanged()
		{
			if (!_refreshing)
			{
				_ = InTransfer;
			}
		}

		private bool TryClaimOwnership()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return false;
			}
			component.ClaimOwnership();
			return HasAuthority;
		}

		private void LateUpdate()
		{
			if ((Object)(object)_opened == (Object)(object)this && !InTransfer && !HasAuthority)
			{
				Warn("Stand access changed. Reopen it to continue.");
				InventoryGui instance = InventoryGui.instance;
				if (instance != null)
				{
					instance.Hide();
				}
				Closed();
			}
		}

		internal void Warn(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
			Player user = _user;
			if (user != null)
			{
				((Character)user).Message((MessageType)2, "Runic Display Stands: " + message, 0, (Sprite)null, false);
			}
		}

		internal static void SetUseEquipButton(bool useEquip)
		{
			if ((Object)(object)InventoryGui.instance == (Object)null || (Object)(object)InventoryGui.instance.m_stackAllButton == (Object)null)
			{
				return;
			}
			Button stackAllButton = InventoryGui.instance.m_stackAllButton;
			TMP_Text componentInChildren = ((Component)stackAllButton).GetComponentInChildren<TMP_Text>(true);
			if (useEquip)
			{
				if (!_stackAllButtonOverridden)
				{
					_stackAllButtonWasActive = ((Component)stackAllButton).gameObject.activeSelf;
					_stackAllButtonText = ((componentInChildren != null) ? componentInChildren.text : null);
					_stackAllButtonOverridden = true;
				}
				((Component)stackAllButton).gameObject.SetActive(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = "Use/equip";
				}
			}
			else if (_stackAllButtonOverridden)
			{
				((Component)stackAllButton).gameObject.SetActive(_stackAllButtonWasActive);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = _stackAllButtonText;
				}
				_stackAllButtonOverridden = false;
				_stackAllButtonText = null;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateContainer")]
	[HarmonyPriority(0)]
	internal static class InventoryGui_UpdateContainer_Stand_Patch
	{
		private static void Postfix()
		{
			if ((Object)(object)ContainerBridge.Opened != (Object)null)
			{
				ContainerBridge.SetUseEquipButton(useEquip: true);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnStackAll")]
	[HarmonyPriority(800)]
	internal static class InventoryGui_OnStackAll_Stand_Patch
	{
		private static bool Prefix()
		{
			if ((Object)(object)ContainerBridge.Opened == (Object)null)
			{
				return true;
			}
			ContainerBridge.Opened.SwapEquip();
			return false;
		}
	}
	internal sealed class StandVirtualContainer : Container
	{
	}
	[HarmonyPatch(typeof(Container), "IsOwner")]
	internal static class VirtualContainer_IsOwner_Patch
	{
		private static bool Prefix(Container __instance, ref bool __result)
		{
			if (!(__instance is StandVirtualContainer))
			{
				return true;
			}
			__result = (Object)(object)ContainerBridge.Opened != (Object)null && ContainerBridge.Opened.HasAuthority;
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "SetInUse")]
	internal static class VirtualContainer_SetInUse_Patch
	{
		private static bool Prefix(Container __instance)
		{
			return !(__instance is StandVirtualContainer);
		}
	}
	[HarmonyPatch(typeof(Container), "StackAll")]
	internal static class VirtualContainer_StackAll_Patch
	{
		private static bool Prefix(Container __instance)
		{
			if (!(__instance is StandVirtualContainer))
			{
				return true;
			}
			ContainerBridge.Opened?.SwapEquip();
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	internal static class InventoryGui_Hide_Stand_Patch
	{
		private static void Prefix()
		{
			ContainerBridge.Closed();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "CloseContainer")]
	internal static class InventoryGui_CloseContainer_Stand_Patch
	{
		private static void Prefix()
		{
			ContainerBridge.Closed();
		}
	}
	internal static class ItemDataSerializer
	{
		internal static byte[] Serialize(ItemData item)
		{
			//IL_0011: 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)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (item == null)
			{
				return Array.Empty<byte>();
			}
			Inventory val = new Inventory("RunicDisplayStands", (Sprite)null, 1, 1);
			ItemData val2 = item.Clone();
			val2.m_stack = 1;
			val2.m_gridPos = new Vector2i(0, 0);
			val.GetAllItems().Add(val2);
			ZPackage val3 = new ZPackage();
			val.Save(val3);
			return val3.GetArray();
		}

		internal static ItemData Deserialize(byte[] bytes)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if (bytes == null || bytes.Length == 0)
			{
				return null;
			}
			try
			{
				Inventory val = new Inventory("RunicDisplayStands", (Sprite)null, 1, 1);
				val.Load(new ZPackage(bytes));
				List<ItemData> allItems = val.GetAllItems();
				if (allItems.Count != 1 || allItems[0].m_stack != 1)
				{
					throw new InvalidOperationException("Invalid saved stand inventory.");
				}
				ItemData obj = allItems[0];
				obj.m_stack = 1;
				return obj;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not restore complete stand item data; stand left unchanged. " + ex.Message));
				}
				throw;
			}
		}
	}
	internal static class ConfigSync
	{
		private const string RpcName = "RunicDisplayStands_ConfigSync";

		private const int MaximumPayloadBytes = 4096;

		private const int MaximumEntries = 16;

		private const int MaximumKeyBytes = 128;

		private const int MaximumValueBytes = 2048;

		private static readonly List<ConfigEntryBase> _synced = new List<ConfigEntryBase>();

		private static ZRoutedRpc _registeredRpc;

		public static void RegisterSyncedConfig(ConfigEntryBase entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			string text = entry.Definition.Key ?? string.Empty;
			if (!IsBoundedField(text, 128) || text.IndexOfAny(new char[2] { '=', '|' }) >= 0)
			{
				throw new ArgumentException("Synced config keys must be bounded and delimiter-free.", "entry");
			}
			if (!_synced.Exists((ConfigEntryBase candidate) => candidate.Definition == entry.Definition))
			{
				if (_synced.Count >= 16)
				{
					throw new InvalidOperationException("The synced config entry limit has been reached.");
				}
				_synced.Add(entry);
				EnsureRpcRegistered();
			}
		}

		internal static void EnsureRpcRegistered()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && _registeredRpc != instance)
			{
				instance.Register<string>("RunicDisplayStands_ConfigSync", (Action<long, string>)OnConfigReceived);
				_registeredRpc = instance;
			}
		}

		public static void NotifyNewConnection(ZNetPeer peer)
		{
			EnsureRpcRegistered();
			if (_registeredRpc == ZRoutedRpc.instance && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && peer != null && TrySerialize(out var payload))
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "RunicDisplayStands_ConfigSync", new object[1] { payload });
			}
		}

		private static void OnConfigReceived(long sender, string payload)
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null) && !instance.IsServer())
			{
				ZNetPeer serverPeer = instance.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady() && serverPeer.m_uid == sender && TryDeserialize(payload))
				{
					Plugin.RebuildStandPrefabSet();
					Plugin.Log.LogInfo((object)"Received admin config from server; local Stand Prefabs list updated.");
				}
			}
		}

		private static bool TrySerialize(out string payload)
		{
			payload = string.Empty;
			if (_synced.Count == 0 || _synced.Count > 16)
			{
				return false;
			}
			List<string> list = new List<string>(_synced.Count);
			foreach (ConfigEntryBase item in _synced)
			{
				string text = item.Definition.Key ?? string.Empty;
				string text2 = item.BoxedValue?.ToString() ?? string.Empty;
				if (!IsBoundedField(text, 128) || !IsBoundedField(text2, 2048) || text.IndexOfAny(new char[2] { '=', '|' }) >= 0 || text2.IndexOf('|') >= 0)
				{
					return false;
				}
				list.Add(text + "=" + text2);
			}
			payload = string.Join("|", list);
			if (!IsBoundedField(payload, 4096))
			{
				payload = string.Empty;
				return false;
			}
			return true;
		}

		private static bool TryDeserialize(string payload)
		{
			if (!IsBoundedField(payload, 4096))
			{
				return false;
			}
			string[] array = payload.Split(new char[1] { '|' }, StringSplitOptions.None);
			if (array.Length == 0 || array.Length > 16)
			{
				return false;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string[] array2 = array;
			foreach (string text in array2)
			{
				int num = text.IndexOf('=');
				if (num <= 0)
				{
					return false;
				}
				string text2 = text.Substring(0, num);
				string value = text.Substring(num + 1);
				if (!IsBoundedField(text2, 128) || !IsBoundedField(value, 2048) || text2.IndexOf('|') >= 0 || !dictionary.TryAdd(text2, value))
				{
					return false;
				}
			}
			foreach (ConfigEntryBase item in _synced)
			{
				if (dictionary.TryGetValue(item.Definition.Key, out var value2))
				{
					item.BoxedValue = value2;
				}
			}
			return true;
		}

		private static bool IsBoundedField(string value, int maximumBytes)
		{
			if (value != null)
			{
				return Encoding.UTF8.GetByteCount(value) <= maximumBytes;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNet_Awake_ConfigSync_Patch
	{
		private static void Postfix()
		{
			ConfigSync.EnsureRpcRegistered();
		}
	}
	[HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")]
	internal static class ZRoutedRpc_AddPeer_ConfigSync_Patch
	{
		private static void Postfix(ZNetPeer peer)
		{
			ConfigSync.NotifyNewConnection(peer);
		}
	}
	internal static class ArmorStandAdapter
	{
		public static int GetSlotCount(ArmorStand stand)
		{
			return stand.m_slots.Count;
		}

		public static List<ItemData> GetPlacedItems(ArmorStand stand)
		{
			List<KeyValuePair<int, ItemData>> list = new List<KeyValuePair<int, ItemData>>();
			ZNetView component = ((Component)stand).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return ((IEnumerable<ItemData>)(object)new ItemData[9]).ToList();
			}
			ZDO zdo = component.GetZDO();
			for (int i = 0; i < GetSlotCount(stand); i++)
			{
				int itemKey = ItemKey(i);
				int num = AttachedItemIdentity.Resolve(stand.GetAttachedItem(i), component.IsOwner(), () => zdo.GetString(itemKey, string.Empty), (string legacyName) => StringExtensionMethods.GetStableHashCode(legacyName), delegate(int migratedHash)
				{
					zdo.Set(itemKey, migratedHash, false);
					zdo.RemoveString(itemKey);
				});
				if (num == 0)
				{
					continue;
				}
				ItemData val = ItemDataSerializer.Deserialize(zdo.GetByteArray(ItemDataKey(i), (byte[])null));
				if (val != null)
				{
					if ((Object)(object)val.m_dropPrefab == (Object)null || StringExtensionMethods.GetStableHashCode(((Object)val.m_dropPrefab).name) != num)
					{
						throw new InvalidOperationException("Saved stand data does not match the displayed equipment; stand left unchanged.");
					}
				}
				else
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(num);
					if ((Object)(object)itemPrefab == (Object)null)
					{
						throw new InvalidOperationException("A displayed equipment prefab is unavailable.");
					}
					val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
					ItemDrop.LoadFromZDO(val, zdo, i);
					val.m_stack = 1;
				}
				int key = zdo.GetInt(RoleKey(i), 0) - 1;
				list.Add(new KeyValuePair<int, ItemData>(key, val));
			}
			return ArmorStandSlots.ArrangeSaved(list).ToList();
		}

		internal static StandWriteBatch PrepareWrite(ArmorStand stand, IList<ItemData> items)
		{
			ItemData[] array = MapItemsToSlots(stand, items);
			StandWriteBatch standWriteBatch = new StandWriteBatch(((Component)stand).GetComponent<ZNetView>());
			for (int i = 0; i < array.Length; i++)
			{
				standWriteBatch.Item(array[i], i);
				standWriteBatch.Int(RoleKey(i), (array[i] != null) ? (ArmorStandSlots.Slot(array[i]) + 1) : 0);
			}
			return standWriteBatch;
		}

		internal static void PublishVisuals(ArmorStand stand, IList<ItemData> items)
		{
			ItemData[] mapped = MapItemsToSlots(stand, items);
			ZNetView component = ((Component)stand).GetComponent<ZNetView>();
			for (int i = 0; i < mapped.Length; i++)
			{
				component.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { i, 0, 0 });
			}
			foreach (int item in from num in Enumerable.Range(0, mapped.Length)
				where mapped[num] != null
				orderby ArmorStandSlots.IsDisplayHand(ArmorStandSlots.Slot(mapped[num]))
				select num)
			{
				component.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3]
				{
					item,
					(mapped[item] != null) ? StringExtensionMethods.GetStableHashCode(((Object)mapped[item].m_dropPrefab).name) : 0,
					mapped[item]?.m_variant ?? 0
				});
			}
		}

		internal static ItemData[] PlayerLoadout(Player player, Inventory stand)
		{
			List<ItemData> list = ((Humanoid)player).GetInventory().GetEquippedItems().ToList();
			string[] array = new string[2] { "m_hiddenLeftItem", "m_hiddenRightItem" };
			foreach (string text in array)
			{
				object? value = AccessTools.Field(typeof(Humanoid), text).GetValue(player);
				ItemData val = (ItemData)((value is ItemData) ? value : null);
				if (val != null && !list.Contains(val))
				{
					list.Add(val);
				}
			}
			return ArmorStandSlots.SelectPlayerItems(((Humanoid)player).GetInventory(), list, stand);
		}

		private static int ItemKey(int index)
		{
			return StringExtensionMethods.GetStableHashCode($"{index}_item");
		}

		private static int ItemDataKey(int index)
		{
			return StringExtensionMethods.GetStableHashCode($"RunicDisplayStands_itemdata_{index}");
		}

		private static int RoleKey(int index)
		{
			return StringExtensionMethods.GetStableHashCode($"RunicDisplayStands_role_{index}");
		}

		private static bool MatchesVisual(VisSlot native, int role)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			switch (role)
			{
			case 0:
				return (int)native == 6;
			case 1:
				return (int)native == 4;
			case 2:
				return (int)native == 5;
			case 3:
				return (int)native == 7;
			case 4:
				return (int)native == 8;
			case 5:
			case 8:
				if ((int)native != 1)
				{
					return (int)native == 3;
				}
				return true;
			case 6:
			case 7:
				if ((int)native != 0)
				{
					return (int)native == 2;
				}
				return true;
			default:
				return false;
			}
		}

		public static ItemData[] MapItemsToSlots(ArmorStand stand, IEnumerable<ItemData> items)
		{
			return ArmorStandSlots.MapNative(items, GetSlotCount(stand), (int i, int role) => MatchesVisual(stand.m_slots[i].m_slot, role));
		}
	}
	internal static class AttachedItemIdentity
	{
		internal static int Resolve(int currentHash, bool isOwner, Func<string> readLegacyName, Func<string, int> getStableHash, Action<int> migrateOwnedValue)
		{
			if (currentHash != 0)
			{
				return currentHash;
			}
			string text = readLegacyName();
			if (string.IsNullOrEmpty(text))
			{
				return 0;
			}
			int num = getStableHash(text);
			if (isOwner && num != 0)
			{
				migrateOwnedValue?.Invoke(num);
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(ItemStand), "Interact")]
	internal static class ItemStand_Interact_Patch
	{
		private static bool Prefix(ItemStand __instance, Humanoid __0, bool __1, bool __2, ref bool __result)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsManagedStand(((Component)__instance).gameObject))
			{
				return true;
			}
			if (__1)
			{
				return true;
			}
			if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false))
			{
				__result = true;
				return false;
			}
			if (__2 || IsTakeOneHeld())
			{
				ContainerBridge orCreate = ContainerBridge.GetOrCreate(((Component)__instance).gameObject);
				__result = orCreate.TakeOne(__0);
			}
			else
			{
				ContainerBridge orCreate2 = ContainerBridge.GetOrCreate(((Component)__instance).gameObject);
				__result = orCreate2.OpenAsContainer(__0);
			}
			return false;
		}

		private static bool IsTakeOneHeld()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = Plugin.CfgTakeOneKey.Value;
			return ((KeyboardShortcut)(ref value)).IsDown();
		}
	}
	[HarmonyPatch(typeof(Switch), "Interact")]
	internal static class ArmorStand_Switch_Interact_Patch
	{
		[HarmonyBefore(new string[] { "chazman.RunicInteraction" })]
		private static bool Prefix(Switch __instance, Humanoid __0, bool __1, ref bool __result)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			if (!Plugin.IsManagedStand(((Component)componentInParent).gameObject))
			{
				return true;
			}
			if (__1)
			{
				return true;
			}
			if (!PrivateArea.CheckAccess(((Component)componentInParent).transform.position, 0f, false, false))
			{
				__result = true;
				return false;
			}
			ContainerBridge orCreate = ContainerBridge.GetOrCreate(((Component)componentInParent).gameObject);
			__result = orCreate.OpenAsContainer(__0);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "GetElement")]
	[HarmonyPriority(800)]
	internal static class InventoryGrid_GetElement_Bounds_Patch
	{
		private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static bool Prefix(int x, int y, int width, InventoryGrid __instance, ref InventoryElement __result)
		{
			if (!(ElementsField?.GetValue(__instance) is IList list))
			{
				return true;
			}
			long num = (long)y * (long)width + x;
			if (x >= 0 && x < width && y >= 0 && width > 0 && num >= 0 && num < list.Count)
			{
				return true;
			}
			__result = null;
			return false;
		}
	}
	internal static class ItemStandAdapter
	{
		private static readonly MethodInfo GetOrientationMethod = AccessTools.Method(typeof(ItemStand), "GetOrientation", (Type[])null, (Type[])null);

		private static readonly int ItemDataKey = StringExtensionMethods.GetStableHashCode("RunicDisplayStands_itemdata");

		public static ItemData GetPlacedItem(ItemStand stand)
		{
			ZNetView component = ((Component)stand).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return null;
			}
			ZDO zdo = component.GetZDO();
			int num = AttachedItemIdentity.Resolve(stand.GetAttachedItem(), component.IsOwner(), () => zdo.GetString(ZDOVars.s_item, string.Empty), (string legacyName) => StringExtensionMethods.GetStableHashCode(legacyName), delegate(int migratedHash)
			{
				zdo.Set(ZDOVars.s_item, migratedHash, false);
				zdo.RemoveString(ZDOVars.s_item);
			});
			if (num == 0)
			{
				return null;
			}
			ItemData val = ItemDataSerializer.Deserialize(zdo.GetByteArray(ItemDataKey, (byte[])null));
			if (val != null)
			{
				if ((Object)(object)val.m_dropPrefab == (Object)null || StringExtensionMethods.GetStableHashCode(((Object)val.m_dropPrefab).name) != num)
				{
					throw new InvalidOperationException("Saved stand data does not match the displayed item; stand left unchanged.");
				}
				return val;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(num);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				throw new InvalidOperationException("The displayed item prefab is unavailable.");
			}
			ItemData obj = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			ItemDrop.LoadFromZDO(obj, zdo, -1);
			obj.m_stack = 1;
			return obj;
		}

		internal static StandWriteBatch PrepareWrite(ItemStand stand, ItemData item)
		{
			if (item != null && item.m_stack != 1)
			{
				throw new InvalidOperationException("An item stand holds one item, not a stack.");
			}
			StandWriteBatch standWriteBatch = new StandWriteBatch(((Component)stand).GetComponent<ZNetView>());
			standWriteBatch.Item(item);
			return standWriteBatch;
		}

		internal static void PublishVisual(ItemStand stand, ItemData item)
		{
			((Component)stand).GetComponent<ZNetView>().InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4]
			{
				(item != null) ? StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name) : 0,
				item?.m_variant ?? 0,
				item?.m_quality ?? 1,
				GetOrientation(stand)
			});
		}

		private static int GetOrientation(ItemStand stand)
		{
			return (int)GetOrientationMethod.Invoke(stand, null);
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class NamedStandSlotLabels
	{
		private static void Postfix(InventoryGrid __instance, List<InventoryElement> ___m_elements)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (Object)(object)ContainerBridge.Opened != (Object)null && ContainerBridge.Opened.IsArmorStand && ContainerBridge.IsStandInventory(__instance.GetInventory());
			for (int i = 0; i < ___m_elements.Count; i++)
			{
				InventoryElement val = ___m_elements[i];
				Transform val2 = ((Component)val).transform.Find("RunicStandSlotLabel");
				if ((flag || (Object)(object)val2 != (Object)null) && i >= 5 && i < 9)
				{
					RectTransform val3 = (RectTransform)((Component)val).transform;
					RectTransform val4 = (RectTransform)((Component)___m_elements[i % 5]).transform;
					Vector2 anchoredPosition = val3.anchoredPosition;
					anchoredPosition.x = val4.anchoredPosition.x + (flag ? (__instance.m_elementSpace * 0.5f) : 0f);
					val3.anchoredPosition = anchoredPosition;
				}
				if (!flag)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						((Component)val2).gameObject.SetActive(false);
						((Component)val).gameObject.SetActive(true);
					}
					continue;
				}
				if ((Object)(object)val2 == (Object)null)
				{
					Transform val5 = ((Component)val).transform.Find("binding");
					if ((Object)(object)val5 == (Object)null)
					{
						continue;
					}
					val2 = Object.Instantiate<GameObject>(((Component)val5).gameObject, ((Component)val).transform, false).transform;
					((Object)val2).name = "RunicStandSlotLabel";
				}
				((Component)val).gameObject.SetActive(i < 9);
				if (i < 9)
				{
					((Component)val2).gameObject.SetActive(true);
					TMP_Text component = ((Component)val2).GetComponent<TMP_Text>();
					((Behaviour)component).enabled = true;
					component.text = ArmorStandSlots.Labels[i];
					component.fontSize = 11f;
					component.enableAutoSizing = false;
					component.alignment = (TextAlignmentOptions)258;
					((Graphic)component).color = new Color(1f, 0.76f, 0.35f);
					((Graphic)component).raycastTarget = false;
					RectTransform rectTransform = component.rectTransform;
					rectTransform.anchorMin = new Vector2(0f, 1f);
					rectTransform.anchorMax = new Vector2(1f, 1f);
					rectTransform.pivot = new Vector2(0.5f, 1f);
					rectTransform.anchoredPosition = new Vector2(0f, -2f);
					rectTransform.sizeDelta = new Vector2(0f, 15f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
	internal static class NamedStandAutoAdd
	{
		private static bool Prefix(Inventory __instance, ItemData __0, ref bool __result)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			ContainerBridge opened = ContainerBridge.Opened;
			if ((Object)(object)opened == (Object)null || !opened.IsArmorStand || !ContainerBridge.IsStandInventory(__instance))
			{
				return true;
			}
			__result = false;
			if (!opened.InTransfer)
			{
				return false;
			}
			int num = ArmorStandSlots.DefaultSlot(__0);
			int[] array = new int[3] { num, 5, 6 };
			foreach (int slot in array)
			{
				if (ArmorStandSlots.Accepts(slot, __0) && ArmorStandSlots.GetItem(__instance, slot) == null)
				{
					__result = __instance.AddItem(__0, ArmorStandSlots.Position(slot));
					break;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
	internal static class NamedStandDropGuard
	{
		private static bool Prefix(InventoryGrid __instance, ItemData __1, Vector2i __3, ref bool __result)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			ContainerBridge opened = ContainerBridge.Opened;
			if ((Object)(object)opened == (Object)null || !opened.IsArmorStand || !ContainerBridge.IsStandInventory(__instance.GetInventory()))
			{
				return true;
			}
			if (__3.x >= 0 && __3.x < 5 && __3.y >= 0 && __3.y < 2 && ArmorStandSlots.Accepts(__3.y * 5 + __3.x, __1))
			{
				return true;
			}
			opened.Warn("That item does not fit the selected stand slot.");
			__result = false;
			return false;
		}
	}
	internal static class RunicInventorySlots
	{
		internal static Dictionary<int, Vector2i> Capture(Player player)
		{
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<int, Vector2i> dictionary = new Dictionary<int, Vector2i>();
			if (!Chainloader.PluginInfos.TryGetValue("chazman.RunicInventory", out var value))
			{
				return dictionary;
			}
			try
			{
				object obj = AccessTools.Property(((object)value.Instance).GetType(), "Runtime")?.GetValue(value.Instance);
				if (obj == null)
				{
					return dictionary;
				}
				object[] array = new object[3]
				{
					player.GetPlayerID(),
					null,
					null
				};
				MethodInfo methodInfo = AccessTools.Method(obj.GetType(), "TryCapture", (Type[])null, (Type[])null);
				if (methodInfo == null || !(bool)methodInfo.Invoke(obj, array) || array[1] == null)
				{
					return dictionary;
				}
				object obj2 = array[1];
				Type type = obj2.GetType();
				if (Convert.ToInt32(type.GetProperty("AuthorityMode").GetValue(obj2)) != 1 || (int)type.GetProperty("Width").GetValue(obj2) != ((Humanoid)player).GetInventory().GetWidth() || (int)type.GetProperty("Height").GetValue(obj2) != ((Humanoid)player).GetInventory().GetHeight())
				{
					return dictionary;
				}
				foreach (object item in (IEnumerable)type.GetProperty("Roles").GetValue(obj2))
				{
					Type type2 = item.GetType();
					int num = Convert.ToInt32(type2.GetProperty("Role").GetValue(item)) - 1;
					if (num >= 0 && num <= 4 && !(bool)type2.GetProperty("Locked").GetValue(item))
					{
						object value2 = type2.GetProperty("Coordinate").GetValue(item);
						int num2 = (int)value2.GetType().GetProperty("X").GetValue(value2);
						int num3 = (int)value2.GetType().GetProperty("Y").GetValue(value2);
						if (num2 >= 0 && num2 < ((Humanoid)player).GetInventory().GetWidth() && num3 >= 0 && num3 < ((Humanoid)player).GetInventory().GetHeight())
						{
							dictionary[num] = new Vector2i(num2, num3);
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Protected equipment slot lookup unavailable: " + ex.Message));
				}
			}
			return dictionary;
		}
	}
	internal static class StandSlotValidator
	{
		public static bool IsValidForItemStand(ItemData item)
		{
			return item != null;
		}

		public static bool IsValidForArmorSlot(ItemData item, int slotIndex)
		{
			return ArmorStandSlots.Accepts(slotIndex, item);
		}
	}
	[HarmonyPatch]
	internal static class StandGuiTransferPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(InventoryGui), "OnSelectedItem", new Type[4]
			{
				typeof(InventoryGrid),
				typeof(ItemData),
				typeof(Vector2i),
				typeof(Modifier)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "OnTakeAll", Type.EmptyTypes, (Type[])null);
		}

		[HarmonyPriority(800)]
		private static bool Prefix(out ContainerBridge.Transfer __state)
		{
			__state = null;
			ContainerBridge opened = ContainerBridge.Opened;
			if ((Object)(object)opened == (Object)null || opened.InTransfer)
			{
				return true;
			}
			try
			{
				__state = opened.BeginTransfer();
				return true;
			}
			catch (Exception ex)
			{
				opened.Warn(ex.Message);
				return false;
			}
		}

		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception, ContainerBridge.Transfer __state)
		{
			__state?.Finish(__exception == null);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")]
	internal static class StandRightClickPatch
	{
		private static bool Prefix(InventoryGrid __0, ItemData __1)
		{
			ContainerBridge opened = ContainerBridge.Opened;
			if ((Object)(object)opened == (Object)null || !ContainerBridge.IsStandInventory(__0.GetInventory()))
			{
				return true;
			}
			opened.TakeItem(__1, use: true);
			return false;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int)
	})]
	internal static class StandWorldDropPatch
	{
		private static bool Prefix(Humanoid __instance, Inventory __0, ref bool __result)
		{
			ContainerBridge opened = ContainerBridge.Opened;
			if ((Object)(object)opened == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (!opened.InTransfer && !ContainerBridge.IsStandInventory(__0)))
			{
				return true;
			}
			opened.Warn("Move the item into your inventory before dropping it on the ground.");
			__result = false;
			return false;
		}
	}
	[HarmonyPatch]
	internal static class StandUnscopedMutationPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return from method in typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where new string[8] { "AddItem", "RemoveItem", "RemoveOneItem", "RemoveAll", "MoveAll", "MoveItemToThis", "MoveInventoryToGrave", "StackAll" }.Contains(method.Name)
				select method;
		}

		[HarmonyPriority(800)]
		private static bool Prefix(Inventory __instance)
		{
			if (ContainerBridge.IsStandInventory(__instance))
			{
				return ContainerBridge.Opened.InTransfer;
			}
			return true;
		}
	}
	[BepInPlugin("chazman.RunicDisplayStands", "RunicDisplayStands", "1.3.8")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "chazman.RunicDisplayStands";

		public const string PluginName = "RunicDisplayStands";

		public const string PluginVersion = "1.3.8";

		public static Plugin Instance;

		internal static ManualLogSource Log;

		public static ConfigEntry<string> CfgStandPrefabs;

		public static ConfigEntry<bool> CfgGamepadSupport;

		public static ConfigEntry<KeyboardShortcut> CfgTakeOneKey;

		public static HashSet<string> StandPrefabSet = new HashSet<string>();

		private readonly Harmony _harmony = new Harmony("chazman.RunicDisplayStands");

		private void Awake()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			CfgStandPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Admin", "Stand Prefabs", "itemstand,itemstandh,ArmorStand", new ConfigDescription("Comma separated list of prefab names that should be treated as item/armor stands.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdminOnly = true
				}
			}));
			CfgGamepadSupport = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Gamepad Support", true, "Enable controller-friendly interact prompts and alt-take binding.");
			CfgTakeOneKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Take One Item Key", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Hold this while interacting with a stand to take a single item instead of opening it.");
			RebuildStandPrefabSet();
			CfgStandPrefabs.SettingChanged += delegate
			{
				RebuildStandPrefabSet();
			};
			ConfigSync.RegisterSyncedConfig((ConfigEntryBase)(object)CfgStandPrefabs);
			_harmony.PatchAll();
			Log.LogInfo((object)"RunicDisplayStands v1.3.8 loaded.");
		}

		internal static void RebuildStandPrefabSet()
		{
			StandPrefabSet = (from s in CfgStandPrefabs.Value.Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
		}

		internal static bool IsManagedStand(GameObject stand)
		{
			if ((Object)(object)stand == (Object)null)
			{
				return false;
			}
			if ((Object)(object)stand.GetComponent<Piece>() == (Object)null)
			{
				return false;
			}
			string text = ((Object)stand).name.Replace("(Clone)", "");
			if (!StandPrefabSet.Contains(text))
			{
				return text.StartsWith("piece_runic_hero_display", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	public class ConfigurationManagerAttributes
	{
		public bool IsAdminOnly;
	}
	internal static class ArmorStandSlots
	{
		internal const int Count = 9;

		internal const int Width = 5;

		internal const int Height = 2;

		internal const int Helmet = 0;

		internal const int Chest = 1;

		internal const int Legs = 2;

		internal const int Cape = 3;

		internal const int Utility = 4;

		internal const int RightHand = 5;

		internal const int LeftHand = 6;

		internal const int Shield = 7;

		internal const int Weapon = 8;

		internal static readonly string[] Labels = new string[9] { "Helmet", "Chest", "Legs", "Cape", "Utility", "Right hand", "Left hand", "Shield", "Weapon" };

		internal static bool IsWeapon(ItemData item)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			if (item != null)
			{
				if ((int)item.m_shared.m_itemType != 3 && (int)item.m_shared.m_itemType != 14 && (int)item.m_shared.m_itemType != 22)
				{
					return (int)item.m_shared.m_itemType == 4;
				}
				return true;
			}
			return false;
		}

		internal static bool IsHandItem(ItemData item)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			if (!IsWeapon(item))
			{
				if (item != null)
				{
					if ((int)item.m_shared.m_itemType != 5 && (int)item.m_shared.m_itemType != 19)
					{
						return (int)item.m_shared.m_itemType == 15;
					}
					return true;
				}
				return false;
			}
			return true;
		}

		internal static bool Accepts(int slot, ItemData item)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Invalid comparison between Unknown and I4
			if (item == null || item.m_stack != 1)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			switch (slot)
			{
			case 0:
				return (int)itemType == 6;
			case 1:
				return (int)itemType == 7;
			case 2:
				return (int)itemType == 11;
			case 3:
				return (int)itemType == 17;
			case 4:
				return (int)itemType == 18;
			case 5:
			case 6:
				return IsHandItem(item);
			case 7:
				return (int)itemType == 5;
			case 8:
				return IsWeapon(item);
			default:
				return false;
			}
		}

		internal static int DefaultSlot(ItemData item)
		{
			int[] array = new int[9] { 0, 1, 2, 3, 4, 7, 8, 5, 6 };
			foreach (int num in array)
			{
				if (Accepts(num, item))
				{
					return num;
				}
			}
			return -1;
		}

		internal static int Slot(ItemData item)
		{
			if (item.m_gridPos.x < 0 || item.m_gridPos.x >= 5 || item.m_gridPos.y < 0 || item.m_gridPos.y >= 2)
			{
				return -1;
			}
			return item.m_gridPos.y * 5 + item.m_gridPos.x;
		}

		internal static Vector2i Position(int slot)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2i(slot % 5, slot / 5);
		}

		internal static ItemData GetItem(Inventory inventory, int slot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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)
			Vector2i val = Position(slot);
			return inventory.GetItemAt(val.x, val.y);
		}

		internal static bool IsDisplayHand(int slot)
		{
			if (slot != 5)
			{
				return slot == 6;
			}
			return true;
		}

		internal static int LegacySlot(ItemData item, ItemData[] roles)
		{
			int num = DefaultSlot(item);
			if (num >= 0 && roles[num] == null)
			{
				return num;
			}
			int[] array = new int[2] { 5, 6 };
			foreach (int num2 in array)
			{
				if (roles[num2] == null && Accepts(num2, item))
				{
					return num2;
				}
			}
			throw new InvalidOperationException("This older stand has extra equipment that does not fit the named slots. Its saved items were left unchanged.");
		}

		internal static ItemData[] ArrangeSaved(IEnumerable<KeyValuePair<int, ItemData>> attachments)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			ItemData[] array = (ItemData[])(object)new ItemData[9];
			List<ItemData> list = new List<ItemData>();
			foreach (KeyValuePair<int, ItemData> attachment in attachments)
			{
				if (attachment.Key < 0)
				{
					list.Add(attachment.Value);
					continue;
				}
				if (!Accepts(attachment.Key, attachment.Value) || array[attachment.Key] != null)
				{
					throw new InvalidOperationException("Saved stand role is invalid or duplicated; stand left unchanged.");
				}
				array[attachment.Key] = attachment.Value;
			}
			foreach (ItemData item in list)
			{
				array[LegacySlot(item, array)] = item;
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] != null)
				{
					array[i].m_gridPos = Position(i);
					array[i].m_equipped = false;
				}
			}
			return array;
		}

		internal static ItemData[] MapNative(IEnumerable<ItemData> items, int count, Func<int, int, bool> matchesVisual)
		{
			ItemData[] array = (ItemData[])(object)new ItemData[count];
			HashSet<int> hashSet = new HashSet<int>();
			foreach (ItemData item in from i in items
				where i != null
				orderby IsDisplayHand(Slot(i))
				select i)
			{
				int num = Slot(item);
				if (!Accepts(num, item) || !hashSet.Add(num))
				{
					throw new InvalidOperationException("That item does not fit the selected named stand slot.");
				}
				int num2 = -1;
				for (int num3 = 0; num3 < count; num3++)
				{
					if (array[num3] == null && matchesVisual(num3, num))
					{
						num2 = num3;
						break;
					}
				}
				if (num2 < 0)
				{
					throw new InvalidOperationException("This stand has no available attachment point for that slot; items were left unchanged.");
				}
				array[num2] = item;
			}
			return array;
		}

		internal static ItemData[] SelectPlayerItems(Inventory player, IEnumerable<ItemData> selected, Inventory stand)
		{
			ItemData[] source = selected.Concat(from i in player.GetAllItems()
				orderby i.m_gridPos.y, i.m_gridPos.x
				select i).Distinct().ToArray();
			ItemData[] array = (ItemData[])(object)new ItemData[9];
			int[] array2 = new int[7] { 0, 1, 2, 3, 4, 7, 8 };
			foreach (int slot in array2)
			{
				bool combat = slot == 7 || slot == 8;
				if (!combat || GetItem(stand, slot) != null)
				{
					array[slot] = ((IEnumerable<ItemData>)source).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => player.GetAllItems().Contains(i) && Accepts(slot, i) && (combat || slot == 4 || i.m_equipped)));
				}
			}
			return array;
		}
	}
	internal sealed class InventorySnapshot
	{
		private static readonly FieldInfo[] Fields = typeof(ItemData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private readonly Inventory _inventory;

		private readonly ItemData[] _originals;

		private readonly ItemData[] _copies;

		internal InventorySnapshot(Inventory inventory)
		{
			_inventory = inventory;
			_originals = inventory.GetAllItems().ToArray();
			_copies = _originals.Select((ItemData item) => item.Clone()).ToArray();
		}

		internal void Restore()
		{
			for (int i = 0; i < _originals.Length; i++)
			{
				ItemData obj = _copies[i].Clone();
				FieldInfo[] fields = Fields;
				foreach (FieldInfo fieldInfo in fields)
				{
					fieldInfo.SetValue(_originals[i], fieldInfo.GetValue(obj));
				}
			}
			_inventory.GetAllItems().Clear();
			_inventory.GetAllItems().AddRange(_originals);
		}

		internal static string Contents(IEnumerable<ItemData> items)
		{
			//IL_0066: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			SortedDictionary<string, long> sortedDictionary = new SortedDictionary<string, long>(StringComparer.Ordinal);
			foreach (ItemData item in items.Where((ItemData item) => item != null))
			{
				if (item.m_stack <= 0)
				{
					throw new InvalidOperationException("Invalid item stack.");
				}
				ItemData obj = item.Clone();
				obj.m_stack = 1;
				obj.m_gridPos = new Vector2i(0, 0);
				obj.m_equipped = false;
				ZPackage val = new ZPackage();
				obj.Save(val);
				string key = ((Object)item.m_dropPrefab).name + ":" + val.GetBase64();
				sortedDictionary.TryGetValue(key, out var value);
				sortedDictionary[key] = checked(value + item.m_stack);
			}
			return string.Join("\n", sortedDictionary.Select((KeyValuePair<string, long> pair) => pair.Key + ":" + pair.Value));
		}
	}
	internal static class LoadoutSwap
	{
		internal static void Apply(Inventory player, Inventory stand, ItemData[] outgoing, Action<ItemData> unequip, Func<ItemData, bool> equip, IReadOnlyDictionary<int, Vector2i> equipmentSlots = null)
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			if (outgoing.Length != 9)
			{
				throw new ArgumentException("Expected named loadout slots.");
			}
			ItemData[] array = (ItemData[])(object)new ItemData[9];
			Dictionary<int, Vector2i> dictionary = new Dictionary<int, Vector2i>();
			for (int i = 0; i < 9; i++)
			{
				if (ArmorStandSlots.IsDisplayHand(i))
				{
					continue;
				}
				ItemData item = ArmorStandSlots.GetItem(stand, i);
				if (((i == 3 || i == 4) && item == null) || ((i == 8 || i == 7) && (item == null || outgoing[i] == null)))
				{
					continue;
				}
				array[i] = item;
				ItemData val = outgoing[i];
				if (val != null)
				{
					dictionary[i] = val.m_gridPos;
					unequip(val);
					if (!player.RemoveItem(val))
					{
						throw new InvalidOperationException("An equipped item could not be moved.");
					}
				}
				if (item != null)
				{
					stand.GetAllItems().Remove(item);
				}
				if (val != null)
				{
					ItemData val2 = val.Clone();
					val2.m_gridPos = ArmorStandSlots.Position(i);
					val2.m_equipped = false;
					stand.GetAllItems().Add(val2);
				}
			}
			for (int j = 0; j < array.Length; j++)
			{
				ItemData val3 = array[j];
				if (val3 != null)
				{
					val3.m_equipped = false;
					Vector2i value = default(Vector2i);
					bool flag = equipmentSlots?.TryGetValue(j, out value) ?? false;
					if (!flag)
					{
						flag = dictionary.TryGetValue(j, out value);
					}
					if (!((flag && player.GetItemAt(value.x, value.y) == null) ? player.AddItem(val3, value) : player.AddItem(val3)))
					{
						throw new InvalidOperationException("Make room in your inventory before swapping this loadout.");
					}
					if (!equip(val3))
					{
						throw new InvalidOperationException("The replacement equipment could not be equipped; the swap was canceled.");
					}
				}
			}
			ItemData val4 = outgoing[4];
			if (array[4] == null && val4 != null && !val4.m_equipped && !equip(val4))
			{
				throw new InvalidOperationException("Your carried utility item could not be equipped; the swap was canceled.");
			}
		}
	}
	internal static class StandGridLayout
	{
		internal static int Width(int slots)
		{
			return Math.Min(8, Math.Max(1, slots));
		}

		internal static int Height(int slots)
		{
			return Math.Max(1, (slots + Width(slots) - 1) / Width(slots));
		}

		internal static Vector2i Position(int index, int width)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2i(index % width, index / width);
		}
	}
	internal sealed class StandWriteBatch
	{
		private readonly ZNetView _view;

		private readonly ZDO _zdo;

		private readonly List<Action> _writes = new List<Action>();

		private readonly List<Action> _undo = new List<Action>();

		private readonly List<Func<bool>> _checks = new List<Func<bool>>();

		internal StandWriteBatch(ZNetView view)
		{
			_view = view;
			_zdo = view.GetZDO();
		}

		internal void Int(int key, int value)
		{
			int old = _zdo.GetInt(key, 0);
			_writes.Add(delegate
			{
				_zdo.Set(key, value, false);
			});
			_undo.Add(delegate
			{
				_zdo.Set(key, old, false);
			});
			_checks.Add(() => _zdo.GetInt(key, 0) == value);
		}

		internal void ClearLegacy(int key)
		{
			string old = _zdo.GetString(key, string.Empty);
			_writes.Add(delegate
			{
				_zdo.RemoveString(key);
			});
			_undo.Add(delegate
			{
				if (old.Length > 0)
				{
					_zdo.Set(key, old);
				}
				else
				{
					_zdo.RemoveString(key);
				}
			});
			_checks.Add(() => _zdo.GetString(key, string.Empty).Length == 0);
		}

		internal void Bytes(int key, byte[] value)
		{
			byte[] old = (_zdo.GetByteArray(key, (byte[])null) ?? Array.Empty<byte>()).ToArray();
			byte[] prepared = value.ToArray();
			_writes.Add(delegate
			{
				_zdo.Set(key, prepared);
			});
			_undo.Add(delegate
			{
				_zdo.Set(key, old);
			});
			_checks.Add(() => (_zdo.GetByteArray(key, (byte[])null) ?? Array.Empty<byte>()).SequenceEqual(prepared));
		}

		internal void Item(ItemData item, int index = -1)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			string text = ((index < 0) ? "" : (index + "_"));
			int stableHashCode = StringExtensionMethods.GetStableHashCode(text + "item");
			Int(stableHashCode, (item != null) ? StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name) : 0);
			ClearLegacy(stableHashCode);
			Int(StringExtensionMethods.GetStableHashCode(text + "variant"), item?.m_variant ?? 0);
			Int(StringExtensionMethods.GetStableHashCode(text + "quality"), item?.m_quality ?? 1);
			ZPackage val = new ZPackage();
			if (item != null)
			{
				val.Write((byte)109);
				item.Save(val);
			}
			Bytes(StringExtensionMethods.GetStableHashCode(text + "itemData"), (item == null) ? Array.Empty<byte>() : val.GetArray());
			int stableHashCode2 = StringExtensionMethods.GetStableHashCode((index < 0) ? "RunicDisplayStands_itemdata" : ("RunicDisplayStands_itemdata_" + index));
			Bytes(stableHashCode2, (item == null) ? Array.Empty<byte>() : ItemDataSerializer.Serialize(item));
		}

		internal bool Commit()
		{
			if (!_view.IsValid() || !_view.IsOwner() || _view.GetZDO() != _zdo)
			{
				return false;
			}
			try
			{
				foreach (Action write in _writes)
				{
					write();
				}
				if (!_checks.All((Func<bool> check) => check()))
				{
					throw new InvalidOperationException("Stand write verification failed.");
				}
				return true;
			}
			catch
			{
				if (_view.IsValid() && _view.IsOwner() && _view.GetZDO() == _zdo)
				{
					for (int num = _undo.Count - 1; num >= 0; num--)
					{
						_undo[num]();
					}
				}
				throw;
			}
		}
	}
	internal static class TransferBoundary
	{
		internal static bool Complete(bool actionSucceeded, Func<bool> validate, Func<bool> commit, Action rollback)
		{
			bool flag = false;
			try
			{
				if (actionSucceeded && validate())
				{
					flag = commit();
				}
				return flag;
			}
			finally
			{
				if (!flag)
				{
					rollback();
				}
			}
		}
	}
}