Decompiled source of LinkedStorage v1.1.0

LinkedStorage.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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 Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("LinkedStorage")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("LinkedStorage")]
[assembly: AssemblyTitle("LinkedStorage")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LinkedStorage
{
	internal static class ChestEdits
	{
		internal const byte Remove = 1;

		internal const byte Add = 2;

		internal const byte Done = 0;

		internal const byte NotOwner = 1;

		internal const byte Gone = 2;

		private static readonly Func<Container, bool> load = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true);

		internal static ZPackage RemoveEdit(string key, int amount)
		{
			//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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write(key);
			val.Write(amount);
			return val;
		}

		internal static ZPackage AddEdit(ItemData item)
		{
			//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_0013: Expected O, but got Unknown
			//IL_0014: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)2);
			ItemCodec.Write(val, item);
			return val;
		}

		internal static ZPackage Apply(ZDO zdo, ZPackage edit, bool asServer)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			Container val2 = ((zdo != null && zdo.IsValid()) ? HubStorage.PrefabContainer(zdo) : null);
			if ((Object)(object)val2 == (Object)null)
			{
				val.Write((byte)2);
				return val;
			}
			if (!asServer && !zdo.IsOwner())
			{
				val.Write((byte)1);
				return val;
			}
			ZNetView val3 = ((zdo.IsOwner() && (Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(zdo) : null);
			Container val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponentInChildren<Container>() : null);
			if ((Object)(object)val4 != (Object)null && val4.GetInventory() == null)
			{
				val4 = null;
			}
			if ((Object)(object)val4 != (Object)null)
			{
				load(val4);
			}
			Inventory val5 = (((Object)(object)val4 != (Object)null) ? val4.GetInventory() : HubStorage.Load(zdo, val2));
			edit.SetPos(0);
			List<ItemData> list = new List<ItemData>();
			if (edit.ReadByte() == 1)
			{
				string key = edit.ReadString();
				int amount = edit.ReadInt();
				list = ItemCodec.RemoveMatching(val5, key, amount);
			}
			else
			{
				ItemData val6 = ItemCodec.Read(edit);
				if (val6 != null)
				{
					string[] array = HubStorage.DrawerLabels(zdo);
					ItemData val7 = ((array != null && !array.Contains(val6.m_shared.m_name)) ? val6 : ItemCodec.AddWhatFits(val5, val6));
					if (val7 != null)
					{
						list.Add(val7);
					}
				}
			}
			if ((Object)(object)val4 == (Object)null)
			{
				ZPackage val8 = new ZPackage();
				val5.Save(val8);
				zdo.Set(ZDOVars.s_items, val8.GetArray());
			}
			val.Write((byte)0);
			ItemCodec.WriteList(val, list);
			return val;
		}
	}
	internal static class DepositBlock
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class RegisterRpc
		{
			private static void Postfix(Container __instance)
			{
				ZNetView nview = view.Invoke(__instance);
				if ((Object)(object)nview == (Object)null || !nview.IsValid() || (Object)(object)((Component)__instance).GetComponent<LinkMirror>() != (Object)null)
				{
					return;
				}
				nview.Register<bool>("LinkedStorage_NoDeposit", (Action<long, bool>)delegate(long sender, bool blocked)
				{
					if (nview.IsOwner())
					{
						nview.GetZDO().Set(HubStorage.NoDepositKey, blocked);
					}
				});
			}
		}

		[HarmonyPatch(typeof(Container), "Interact")]
		private static class Toggle
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Container __instance, Humanoid character, bool hold, ref bool __result)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0075: Invalid comparison between Unknown and I4
				if (hold || !ZInput.GetKey(Plugin.BlockKey.Value, true) || !Applies(__instance, out var nview))
				{
					return true;
				}
				__result = true;
				ZDO zDO = nview.GetZDO();
				bool flag = zDO.GetLong(ZDOVars.s_creator, 0L) == Game.instance.GetPlayerProfile().GetPlayerID();
				if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false) || ((int)__instance.m_privacy == 0 && !flag) || (int)__instance.m_privacy == 1)
				{
					return false;
				}
				bool flag2 = !zDO.GetBool(HubStorage.NoDepositKey, false);
				nview.InvokeRPC("LinkedStorage_NoDeposit", new object[1] { flag2 });
				((Character)character).Message((MessageType)2, State(flag2), 0, (Sprite)null, false);
				return false;
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
		private static class HoverIcon
		{
			private static void Postfix(Hud __instance, Player player)
			{
				TMP_Text hoverName = (TMP_Text)(object)__instance.m_hoverName;
				GameObject hoverObject = ((Humanoid)player).GetHoverObject();
				if ((Object)(object)hoverObject != (Object)null)
				{
					Hoverable componentInParent = hoverObject.GetComponentInParent<Hoverable>();
					Container val = (Container)(object)((componentInParent is Container) ? componentInParent : null);
					if (val != null && hoverName.text.Length > 0 && Applies(val, out var nview) && Place(hoverName, nview.GetZDO().GetBool(HubStorage.NoDepositKey, false)))
					{
						return;
					}
				}
				if ((Object)(object)icon != (Object)null)
				{
					((Component)icon).gameObject.SetActive(false);
				}
			}
		}

		private const string Rpc = "LinkedStorage_NoDeposit";

		private static readonly FieldRef<Container, ZNetView> view = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static bool? haveMarks;

		private static Image icon;

		private static GameObject cross;

		private static bool Applies(Container container, out ZNetView nview)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			nview = view.Invoke(container);
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || !LinkClient.HubExists || (Object)(object)((Component)container).GetComponent<LinkMirror>() != (Object)null)
			{
				return false;
			}
			ZDO zDO = nview.GetZDO();
			if ((Object)(object)HubStorage.PrefabContainer(zDO) != (Object)null)
			{
				return Vector3.Distance(zDO.GetPosition(), LinkClient.HubPos) <= LinkClient.Range;
			}
			return false;
		}

		private static string State(bool blocked)
		{
			if (!haveMarks.HasValue && (Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_hoverName != (Object)null && (Object)(object)((TMP_Text)Hud.instance.m_hoverName).font != (Object)null)
			{
				haveMarks = ((TMP_Text)Hud.instance.m_hoverName).font.HasCharacter('✅', true, true) && ((TMP_Text)Hud.instance.m_hoverName).font.HasCharacter('❌', true, true);
				if (!haveMarks.Value)
				{
					Plugin.Log.LogInfo((object)("Hover font " + ((Object)((TMP_Text)Hud.instance.m_hoverName).font).name + " has no ✅/❌; the deposit line says on/off."));
				}
			}
			string text = ((haveMarks != true) ? (blocked ? "$linkedstorage_off" : "$linkedstorage_on") : (blocked ? "❌" : "✅"));
			return Localization.instance.Localize("$linkedstorage_deposits <color=" + (blocked ? "#FF5A5A" : "#5FA052") + ">" + text + "</color>");
		}

		private static bool Place(TMP_Text text, bool blocked)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)icon == (Object)null && !BuildIcon(text))
			{
				return false;
			}
			text.ForceMeshUpdate(false, false);
			TMP_TextInfo textInfo = text.textInfo;
			int num = ((textInfo.lineCount > 0) ? textInfo.lineInfo[0].lastVisibleCharacterIndex : (-1));
			if (num < 0)
			{
				return false;
			}
			TMP_LineInfo val = textInfo.lineInfo[0];
			float num2 = val.ascender - val.descender;
			float num3 = num2 * 1.7f;
			RectTransform rectTransform = ((Graphic)icon).rectTransform;
			rectTransform.sizeDelta = new Vector2(num3, num3);
			((Transform)rectTransform).localPosition = new Vector3(textInfo.characterInfo[num].topRight.x + num2 * 0.1f + num3 * 0.35f, (val.ascender + val.descender) / 2f, 0f);
			((Graphic)icon).color = (Color)(blocked ? new Color(0.45f, 0.45f, 0.45f, 0.75f) : Color.white);
			cross.SetActive(blocked);
			((Component)icon).gameObject.SetActive(true);
			return true;
		}

		private static bool BuildIcon(TMP_Text text)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (!((Object)(object)ZNetScene.instance != (Object)null))
			{
				obj = null;
			}
			else
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("piece_linkedstorage_hub");
				obj = ((prefab != null) ? prefab.GetComponent<Piece>() : null);
			}
			Piece val = (Piece)obj;
			if ((Object)(object)val == (Object)null || (Object)(object)val.m_icon == (Object)null)
			{
				return false;
			}
			GameObject val2 = new GameObject("LinkedStorage_hubIcon", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(text.transform, false);
			icon = val2.AddComponent<Image>();
			icon.sprite = val.m_icon;
			icon.preserveAspect = true;
			((Graphic)icon).raycastTarget = false;
			((Graphic)icon).rectTransform.pivot = new Vector2(0.5f, 0.5f);
			cross = new GameObject("cross", new Type[1] { typeof(RectTransform) });
			cross.transform.SetParent(val2.transform, false);
			RectTransform val3 = (RectTransform)cross.transform;
			val3.anchorMin = new Vector2(0.32f, 0.32f);
			val3.anchorMax = new Vector2(0.68f, 0.68f);
			Vector2 offsetMin = (val3.offsetMax = Vector2.zero);
			val3.offsetMin = offsetMin;
			TextMeshProUGUI obj2 = cross.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).font = text.font;
			((TMP_Text)obj2).enableAutoSizing = true;
			((TMP_Text)obj2).fontSizeMin = 4f;
			((TMP_Text)obj2).fontSizeMax = 72f;
			((TMP_Text)obj2).text = "❌";
			((Graphic)obj2).color = new Color(1f, 0.25f, 0.25f, 0.6f);
			((TMP_Text)obj2).alignment = (TextAlignmentOptions)514;
			((Graphic)obj2).raycastTarget = false;
			return true;
		}
	}
	internal static class Crafty
	{
		private const string Guid = "Azumatt.AzuCraftyBoxes";

		internal static readonly int NoCraftyKey = StringExtensionMethods.GetStableHashCode("AzuCraftyBoxes_NoCrafty");

		private static bool resolved;

		private static ConfigFile config;

		private static MethodInfo canPull;

		private static MethodInfo nearby;

		private static MethodInfo getInventory;

		private static MethodInfo itemCount;

		private static MethodInfo getPrefabName;

		private static readonly FieldRef<Container, ZNetView> containerView = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		internal static bool Loaded
		{
			get
			{
				Resolve();
				return config != null;
			}
		}

		internal static float Range
		{
			get
			{
				object obj = Setting("Container Range");
				if (obj is float)
				{
					return (float)obj;
				}
				return 20f;
			}
		}

		private static bool LeaveOne => Setting("Leave One Item")?.ToString() == "On";

		internal static bool ChestToggle => Setting("Container Toggle")?.ToString() != "Off";

		private static void Resolve()
		{
			if (resolved)
			{
				return;
			}
			resolved = true;
			if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuCraftyBoxes", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				return;
			}
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			Type type = assembly.GetType("AzuCraftyBoxes.API");
			Type type2 = assembly.GetType("AzuCraftyBoxes.IContainers.IContainer");
			if (type == null || type2 == null)
			{
				Plugin.Log.LogWarning((object)"AzuCraftyBoxes found, but not its API; Storage Links won't feed crafting.");
				return;
			}
			canPull = type.GetMethod("CanItemBePulled", new Type[2]
			{
				typeof(string),
				typeof(string)
			});
			nearby = type.GetMethod("GetNearbyContainers")?.MakeGenericMethod(typeof(Player));
			getInventory = type2.GetMethod("GetInventory");
			itemCount = type2.GetMethod("ItemCount");
			getPrefabName = type2.GetMethod("GetPrefabName");
			if (canPull == null || nearby == null || getInventory == null || itemCount == null || getPrefabName == null)
			{
				Plugin.Log.LogWarning((object)"AzuCraftyBoxes' API has changed; Storage Links won't feed crafting.");
			}
			else
			{
				config = value.Instance.Config;
			}
		}

		private static object Setting(string key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition("2 - CraftyBoxes", key);
			if (config == null || !config.ContainsKey(val))
			{
				return null;
			}
			return config[val].BoxedValue;
		}

		internal static int PullMode(Player player)
		{
			if (!player.m_customData.TryGetValue("ACB_PreventPulling", out var value) || !int.TryParse(value, out var result) || result < 0 || result > 2)
			{
				return 1;
			}
			return result;
		}

		internal static void Register(Container container)
		{
			if (!Loaded)
			{
				return;
			}
			try
			{
				canPull.DeclaringType.GetMethod("AddContainer", new Type[1] { typeof(Container) })?.Invoke(null, new object[1] { container });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't register a Storage Link with AzuCraftyBoxes: " + ex.Message));
			}
		}

		private static HashSet<Container> Registry()
		{
			return canPull.DeclaringType.Assembly.GetType("AzuCraftyBoxes.Util.Functions.Boxes")?.GetField("Containers", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as HashSet<Container>;
		}

		internal static string Registered(Container container)
		{
			if (!Loaded)
			{
				return "no AzuCraftyBoxes";
			}
			try
			{
				HashSet<Container> hashSet = Registry();
				return (hashSet == null) ? "registry unknown" : (hashSet.Contains(container) ? "registered" : "NOT registered");
			}
			catch (Exception ex)
			{
				return ex.Message;
			}
		}

		internal static HashSet<ZDOID> NearbyIds(Player player)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			if (!Loaded)
			{
				return hashSet;
			}
			try
			{
				HashSet<Inventory> hashSet2 = new HashSet<Inventory>();
				foreach (object item in (IEnumerable)nearby.Invoke(null, new object[2] { player, Range }))
				{
					object? obj = getInventory.Invoke(item, null);
					Inventory val = (Inventory)((obj is Inventory) ? obj : null);
					if (val != null && !LinkMirror.IsMirror(val))
					{
						hashSet2.Add(val);
					}
				}
				HashSet<Container> hashSet3 = Registry();
				if (hashSet3 == null)
				{
					return hashSet;
				}
				foreach (Container item2 in hashSet3)
				{
					if (!((Object)(object)item2 == (Object)null) && hashSet2.Contains(item2.GetInventory()))
					{
						ZNetView val2 = containerView.Invoke(item2);
						if ((Object)(object)val2 != (Object)null && val2.IsValid())
						{
							hashSet.Add(val2.GetZDO().m_uid);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't list AzuCraftyBoxes' chests: " + ex.Message));
			}
			return hashSet;
		}

		internal static bool CanPull(string chestPrefab, string itemPrefab)
		{
			try
			{
				return canPull == null || (bool)canPull.Invoke(null, new object[2] { chestPrefab, itemPrefab });
			}
			catch
			{
				return true;
			}
		}

		internal static int LocalCount(Player player, string sharedName, string itemPrefab)
		{
			if (!Loaded)
			{
				return 0;
			}
			int num = 0;
			try
			{
				foreach (object item in (IEnumerable)nearby.Invoke(null, new object[2] { player, Range }))
				{
					object? obj = getInventory.Invoke(item, null);
					if (!LinkMirror.IsMirror((Inventory)((obj is Inventory) ? obj : null)) && CanPull((string)getPrefabName.Invoke(item, null), itemPrefab))
					{
						int num2 = (int)itemCount.Invoke(item, new object[1] { sharedName });
						if (num2 > 0 && LeaveOne)
						{
							num2--;
						}
						num += Mathf.Max(0, num2);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't count AzuCraftyBoxes' chests: " + ex.Message));
			}
			return num;
		}
	}
	internal static class HubServer
	{
		private sealed class Job
		{
			internal long Sender;

			internal int RequestId;

			internal byte Op;

			internal long PlayerID;

			internal ZPackage Payload;

			internal readonly List<ItemData> Back = new List<ItemData>();

			internal readonly List<ItemData> Unplaced = new List<ItemData>();

			internal byte Status;

			internal byte Mode;

			internal ZPackage Snapshot;
		}

		private sealed class EditCall
		{
			internal byte Status = 2;

			internal List<ItemData> Items = new List<ItemData>();
		}

		private sealed class SentSnapshot
		{
			internal long Version;

			internal readonly Dictionary<ZDOID, uint> Revisions = new Dictionary<ZDOID, uint>();
		}

		internal const string BornKey = "LinkedStorage_born";

		private static ZDOID hubId = ZDOID.None;

		private static readonly Queue<Job> jobs = new Queue<Job>();

		private static int nextEdit;

		private static readonly Dictionary<int, ZPackage> results = new Dictionary<int, ZPackage>();

		private static readonly Dictionary<int, byte> abandoned = new Dictionary<int, byte>();

		internal static double LastBuildMs;

		private static readonly Dictionary<long, SentSnapshot> sent = new Dictionary<long, SentSnapshot>();

		internal static ZDO Hub
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				if (!((ZDOID)(ref hubId)).IsNone())
				{
					return ZDOMan.instance.GetZDO(hubId);
				}
				return null;
			}
		}

		internal static void Start(MonoBehaviour runner)
		{
			//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)
			hubId = ZDOID.None;
			HubStorage.ClearCache();
			jobs.Clear();
			results.Clear();
			abandoned.Clear();
			sent.Clear();
			runner.StartCoroutine(WatchHubs());
			runner.StartCoroutine(RunJobs());
		}

		private static IEnumerator WatchHubs()
		{
			List<ZDO> found = new List<ZDO>();
			while (true)
			{
				found.Clear();
				int index = 0;
				while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("piece_linkedstorage_hub", found, ref index))
				{
					yield return null;
				}
				ZDO val = found.Where((ZDO z) => z.IsValid()).OrderBy(delegate(ZDO z)
				{
					long num = z.GetLong("LinkedStorage_born", 0L);
					return (num <= 0) ? long.MaxValue : num;
				}).ThenBy((ZDO z) => ((ZDOID)(ref z.m_uid)).UserID)
					.ThenBy((ZDO z) => ((ZDOID)(ref z.m_uid)).ID)
					.FirstOrDefault();
				ZDOID val2 = val?.m_uid ?? ZDOID.None;
				if (val2 != hubId)
				{
					hubId = val2;
					HubStorage.ClearCache();
					Plugin.Log.LogInfo((object)((val != null) ? ($"Storage Hub at {val.GetPosition()} ({found.Count} placed: " + string.Join(", ", found.Select((ZDO z) => string.Format("{0} born {1}", z.GetPosition(), z.GetLong("LinkedStorage_born", 0L))).ToArray()) + ").") : "No Storage Hub in this world."));
					SendHub(0L);
				}
				yield return (object)new WaitForSecondsRealtime(5f);
			}
		}

		internal static void SendHub(long target)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			ZDO hub = Hub;
			ZPackage val = new ZPackage();
			val.Write(hub != null);
			val.Write(hub?.m_uid ?? ZDOID.None);
			val.Write((hub != null) ? hub.GetPosition() : Vector3.zero);
			val.Write(Plugin.HubRange);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "LinkedStorage_Hub", new object[1] { val });
		}

		internal static void OnRequest(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				Job job = new Job
				{
					Sender = sender,
					RequestId = pkg.ReadInt(),
					Op = pkg.ReadByte(),
					PlayerID = pkg.ReadLong(),
					Payload = pkg
				};
				if (job.Op == 0)
				{
					SendHub(sender);
				}
				else
				{
					jobs.Enqueue(job);
				}
			}
		}

		internal static void OnEditResult(long sender, ZPackage pkg)
		{
			int key = pkg.ReadInt();
			ZPackage val = pkg.ReadPackage();
			if (abandoned.TryGetValue(key, out var value))
			{
				abandoned.Remove(key);
				val.SetPos(0);
				if (val.ReadByte() == 0 && value == 1)
				{
					List<ItemData> list = ItemCodec.ReadList(val);
					Plugin.Log.LogWarning((object)$"A late chest edit returned {list.Count} stacks; putting them back into the hub.");
					Job job = new Job
					{
						Sender = 0L,
						Op = 3,
						PlayerID = 0L,
						Payload = null
					};
					job.Unplaced.AddRange(list);
					jobs.Enqueue(job);
				}
			}
			else
			{
				results[key] = val;
			}
		}

		private static IEnumerator RunJobs()
		{
			while (true)
			{
				if (jobs.Count == 0)
				{
					yield return null;
					continue;
				}
				Job job = jobs.Dequeue();
				Stack<IEnumerator> stack = new Stack<IEnumerator>();
				stack.Push(Handle(job));
				while (stack.Count > 0)
				{
					IEnumerator enumerator = stack.Peek();
					bool flag;
					try
					{
						flag = enumerator.MoveNext();
					}
					catch (Exception arg)
					{
						Plugin.Log.LogError((object)$"Storage request failed: {arg}");
						job.Status = 3;
						break;
					}
					if (!flag)
					{
						stack.Pop();
					}
					else if (enumerator.Current is IEnumerator item)
					{
						stack.Push(item);
					}
					else
					{
						yield return enumerator.Current;
					}
				}
				Reply(job);
			}
		}

		private static void Reply(Job job)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			job.Back.AddRange(job.Unplaced);
			if (job.Sender == 0L)
			{
				if (job.Back.Count > 0)
				{
					Plugin.Log.LogError((object)string.Format("Lost {0} stacks returning to the hub: {1}.", job.Back.Count, string.Join(", ", job.Back.Select((ItemData i) => i.m_stack + " " + ItemCodec.PrefabName(i)).ToArray())));
				}
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(job.RequestId);
			val.Write(job.Status);
			if (job.Op == 1)
			{
				val.Write((ZPackage)(((object)job.Snapshot) ?? ((object)new ZPackage())));
			}
			else
			{
				ItemCodec.WriteList(val, job.Back);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(job.Sender, "LinkedStorage_Reply", new object[1] { val });
		}

		private static IEnumerator Handle(Job job)
		{
			ZDO hub = Hub;
			if (hub == null)
			{
				job.Status = 1;
				ReadDeposit(job);
				yield break;
			}
			Vector3 position = hub.GetPosition();
			if (job.PlayerID != 0L && !HubStorage.HasWardAccess(position, job.PlayerID))
			{
				job.Status = 2;
				ReadDeposit(job);
				yield break;
			}
			switch (job.Op)
			{
			case 1:
				job.Snapshot = Snapshot(position, job.Sender, job.PlayerID, job.Payload.ReadLong());
				break;
			case 2:
				yield return Take(job, position);
				break;
			case 3:
				ReadDeposit(job);
				yield return Deposit(job, position);
				break;
			}
		}

		private static void ReadDeposit(Job job)
		{
			if (job.Op == 3 && job.Payload != null && job.Unplaced.Count <= 0)
			{
				job.Mode = job.Payload.ReadByte();
				job.Unplaced.AddRange(ItemCodec.ReadList(job.Payload));
			}
		}

		private static ZPackage Snapshot(Vector3 hubPos, long sender, long playerID, long clientVersion)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				return BuildSnapshot(hubPos, sender, playerID, clientVersion);
			}
			finally
			{
				LastBuildMs = stopwatch.Elapsed.TotalMilliseconds;
			}
		}

		private static ZPackage BuildSnapshot(Vector3 hubPos, long sender, long playerID, long clientVersion)
		{
			//IL_0000: 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_001a: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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)
			List<HubStorage.Store> list = HubStorage.Stores(hubPos, Plugin.HubRange, playerID);
			long num = HubStorage.Version(list);
			ZPackage val = new ZPackage();
			val.Write(num != clientVersion);
			if (num == clientVersion)
			{
				return val;
			}
			SentSnapshot value;
			Dictionary<ZDOID, uint> dictionary = ((sent.TryGetValue(sender, out value) && value.Version == clientVersion) ? value.Revisions : null);
			SentSnapshot sentSnapshot = new SentSnapshot
			{
				Version = num
			};
			val.Write(num);
			val.Write(hubPos);
			val.Write(list.Count);
			foreach (HubStorage.Store item in list)
			{
				val.Write(item.PrefabName);
				val.Write(item.Zdo.m_uid);
				val.Write(item.Zdo.GetLong(ZDOVars.s_creator, 0L));
				val.Write(item.Zdo.GetBool(Crafty.NoCraftyKey, false));
				uint dataRevision = item.Zdo.DataRevision;
				sentSnapshot.Revisions[item.Zdo.m_uid] = dataRevision;
				uint value2;
				bool flag = dictionary != null && dictionary.TryGetValue(item.Zdo.m_uid, out value2) && value2 == dataRevision;
				val.Write(flag);
				if (!flag)
				{
					val.Write(HubStorage.PeekBytes(item));
				}
			}
			sent[sender] = sentSnapshot;
			return val;
		}

		private static IEnumerator Take(Job job, Vector3 hubPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>();
			int num = job.Payload.ReadInt();
			for (int i = 0; i < num; i++)
			{
				list.Add(new KeyValuePair<string, int>(job.Payload.ReadString(), job.Payload.ReadInt()));
			}
			HashSet<ZDOID> skip = new HashSet<ZDOID>();
			int num2 = job.Payload.ReadInt();
			for (int j = 0; j < num2; j++)
			{
				skip.Add(job.Payload.ReadZDOID());
			}
			foreach (KeyValuePair<string, int> item in list)
			{
				string key = item.Key;
				int remaining = item.Value;
				foreach (HubStorage.Store item2 in HubStorage.Stores(hubPos, Plugin.HubRange, job.PlayerID))
				{
					if (remaining <= 0)
					{
						break;
					}
					if (!skip.Contains(item2.Zdo.m_uid) && HubStorage.Peek(item2).GetAllItems().Any((ItemData item) => ItemCodec.Matches(item, key)))
					{
						EditCall call = new EditCall();
						yield return RunEdit(item2.Zdo, ChestEdits.RemoveEdit(key, remaining), call);
						job.Back.AddRange(call.Items);
						remaining -= call.Items.Sum((ItemData val) => val.m_stack);
					}
				}
			}
		}

		private static IEnumerator Deposit(Job job, Vector3 hubPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			while (job.Unplaced.Count > 0)
			{
				ItemData left = job.Unplaced[0];
				string name = left.m_shared.m_name;
				List<HubStorage.Store> list = new List<HubStorage.Store>();
				List<HubStorage.Store> list2 = new List<HubStorage.Store>();
				List<HubStorage.Store> list3 = new List<HubStorage.Store>();
				foreach (HubStorage.Store item in HubStorage.Stores(hubPos, Plugin.HubRange, job.PlayerID))
				{
					if (item.Labels != null && !item.Labels.Contains(name))
					{
						continue;
					}
					Inventory val = HubStorage.Peek(item);
					if (ItemCodec.Room(val, left) == 0)
					{
						continue;
					}
					if (item.NoDeposit)
					{
						if (val.ContainsItemByName(name))
						{
							list.Add(item);
						}
						else if (job.PlayerID == 0L)
						{
							list3.Add(item);
						}
					}
					else if (item.Labels != null || val.ContainsItemByName(name))
					{
						list.Add(item);
					}
					else if (job.Mode == 0)
					{
						list2.Add(item);
					}
				}
				foreach (HubStorage.Store item2 in list.Concat(list2).Concat(list3))
				{
					EditCall call = new EditCall();
					yield return RunEdit(item2.Zdo, ChestEdits.AddEdit(left), call);
					if (call.Status == 0)
					{
						left = call.Items.FirstOrDefault();
						if (left == null)
						{
							break;
						}
						job.Unplaced[0] = left;
					}
				}
				job.Unplaced.RemoveAt(0);
				if (left != null)
				{
					job.Back.Add(left);
				}
			}
		}

		private static IEnumerator RunEdit(ZDO zdo, ZPackage edit, EditCall call)
		{
			for (int attempt = 0; attempt < 3; attempt++)
			{
				if (!zdo.IsValid())
				{
					break;
				}
				long owner = zdo.GetOwner();
				ZPackage val;
				if (owner == 0L || owner == ZDOMan.GetSessionID() || ZNet.instance.GetPeer(owner) == null)
				{
					val = ChestEdits.Apply(zdo, edit, asServer: true);
				}
				else
				{
					int id = ++nextEdit;
					ZPackage val2 = new ZPackage();
					val2.Write(id);
					val2.Write(zdo.m_uid);
					val2.Write(edit);
					ZRoutedRpc.instance.InvokeRoutedRPC(owner, "LinkedStorage_Edit", new object[1] { val2 });
					float start = Time.realtimeSinceStartup;
					while (!results.ContainsKey(id))
					{
						if (ZNet.instance.GetPeer(owner) == null || Time.realtimeSinceStartup - start > 30f)
						{
							Plugin.Log.LogWarning((object)$"Chest owner {owner} didn't answer; skipping that chest.");
							edit.SetPos(0);
							abandoned[id] = edit.ReadByte();
							yield break;
						}
						yield return null;
					}
					val = results[id];
					results.Remove(id);
				}
				val.SetPos(0);
				byte b = val.ReadByte();
				if (b == 1)
				{
					yield return null;
					continue;
				}
				call.Status = b;
				if (b == 0)
				{
					call.Items = ItemCodec.ReadList(val);
				}
				break;
			}
		}
	}
	internal static class HubStorage
	{
		internal sealed class Store
		{
			internal ZDO Zdo;

			internal Container Prefab;

			internal string PrefabName;

			internal string[] Labels;

			internal float Distance;

			internal bool NoDeposit;
		}

		private sealed class Sweep
		{
			internal float At;

			internal Vector3 Hub;

			internal float Range;

			internal readonly List<ZDO> Chests = new List<ZDO>();

			internal readonly List<ZDO> Wards = new List<ZDO>();
		}

		private sealed class Cached
		{
			internal uint Revision;

			internal Inventory Inventory;

			internal byte[] Items;
		}

		internal static readonly int NoDepositKey = StringExtensionMethods.GetStableHashCode("LinkedStorage_noDeposit");

		private static readonly int[] DrawerLabelKeys = new int[2]
		{
			StringExtensionMethods.GetStableHashCode("SimpleItemDrawer_type"),
			StringExtensionMethods.GetStableHashCode("SimpleItemDrawer_type2")
		};

		private static readonly HashSet<string> DrawerPrefabs = new HashSet<string> { "piece_itemdrawer", "piece_itemdrawer_double" };

		private static readonly Dictionary<int, KeyValuePair<Container, string>> containers = new Dictionary<int, KeyValuePair<Container, string>>();

		private static Dictionary<int, float> wardRadii;

		private static readonly Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>> findObjects = AccessTools.MethodDelegate<Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>>>(AccessTools.Method(typeof(ZDOMan), "FindObjects", (Type[])null, (Type[])null), (object)null, true);

		private static readonly List<ZDO> nearBuffer = new List<ZDO>();

		private const float SweepSeconds = 3f;

		private static Sweep sweep;

		private static readonly Dictionary<ZDOID, Cached> cache = new Dictionary<ZDOID, Cached>();

		private static List<ZDO> Near(Vector3 point, float range)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_004c: 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)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			nearBuffer.Clear();
			HashSet<SectorIndex> arg = new HashSet<SectorIndex>();
			Vector2s zone = ZoneSystem.GetZone(point - new Vector3(range, 0f, range));
			Vector2s zone2 = ZoneSystem.GetZone(point + new Vector3(range, 0f, range));
			for (int i = zone.x; i <= zone2.x; i++)
			{
				for (int j = zone.y; j <= zone2.y; j++)
				{
					findObjects(ZDOMan.instance, new Vector2s((short)i, (short)j), nearBuffer, arg);
				}
			}
			nearBuffer.RemoveAll((ZDO z) => z == null || !z.IsValid() || Vector3.Distance(z.GetPosition(), point) > range);
			return nearBuffer;
		}

		private static Sweep Swept(Vector3 hub, float range)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (sweep != null && realtimeSinceStartup - sweep.At < 3f && sweep.Hub == hub && sweep.Range == range)
			{
				return sweep;
			}
			if (wardRadii == null)
			{
				FindWards();
			}
			sweep = new Sweep
			{
				At = realtimeSinceStartup,
				Hub = hub,
				Range = range
			};
			foreach (ZDO item in Near(hub, range + ((wardRadii.Count > 0) ? wardRadii.Values.Max() : 0f)))
			{
				int prefab = item.GetPrefab();
				string name;
				if (wardRadii.ContainsKey(prefab))
				{
					sweep.Wards.Add(item);
				}
				else if ((Object)(object)ContainerOf(prefab, out name) != (Object)null)
				{
					sweep.Chests.Add(item);
				}
			}
			return sweep;
		}

		internal static List<Store> Stores(Vector3 hub, float range, long playerID)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Invalid comparison between Unknown and I4
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			List<Store> list = new List<Store>();
			Sweep sweep = Swept(hub, range);
			foreach (ZDO chest in sweep.Chests)
			{
				if (chest.IsValid())
				{
					string name;
					Container val = ContainerOf(chest.GetPrefab(), out name);
					float num = Vector3.Distance(chest.GetPosition(), hub);
					if (!((Object)(object)val == (Object)null) && !(num > range) && (playerID == 0L || (((int)val.m_privacy != 0 || chest.GetLong(ZDOVars.s_creator, 0L) == playerID) && (int)val.m_privacy != 1 && (!val.m_checkGuardStone || HasWardAccess(sweep.Wards, chest.GetPosition(), playerID)))))
					{
						list.Add(new Store
						{
							Zdo = chest,
							Prefab = val,
							PrefabName = name,
							Labels = DrawerLabels(chest),
							Distance = num,
							NoDeposit = chest.GetBool(NoDepositKey, false)
						});
					}
				}
			}
			list.Sort((Store a, Store b) => a.Distance.CompareTo(b.Distance));
			return list;
		}

		private static Container ContainerOf(int prefabHash, out string name)
		{
			if (!containers.TryGetValue(prefabHash, out var value))
			{
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null);
				Container val2 = null;
				if ((Object)(object)val != (Object)null && !Object.op_Implicit((Object)(object)val.GetComponent<PillarFx>()) && !Object.op_Implicit((Object)(object)val.GetComponent<TombStone>()) && (Object.op_Implicit((Object)(object)val.GetComponent<Piece>()) || Object.op_Implicit((Object)(object)val.GetComponent<Vagon>())))
				{
					val2 = val.GetComponentInChildren<Container>(true);
				}
				value = new KeyValuePair<Container, string>(val2, ((Object)(object)val2 != (Object)null) ? ((Object)val).name : null);
				containers[prefabHash] = value;
			}
			name = value.Value;
			return value.Key;
		}

		internal static Inventory Load(ZDO zdo, Container prefab)
		{
			return Read(zdo, prefab, editable: true);
		}

		private static Inventory Read(ZDO zdo, Container prefab, bool editable)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = new Inventory(prefab.m_name, (Sprite)null, prefab.m_width, prefab.m_height);
			byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null);
			if (byteArray == null)
			{
				return val;
			}
			ZPackage val2 = new ZPackage(byteArray);
			Item val3 = (Item)val2.ReadInt();
			if ((int)val3 < 108)
			{
				val2.SetPos(0);
				val.Load(val2);
				return val;
			}
			int num = val2.ReadUShort();
			List<ItemData> allItems = val.GetAllItems();
			for (int i = 0; i < num; i++)
			{
				ItemData val4 = new ItemData();
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ItemData.Load(val2, val4, val3));
				ItemDrop val5 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if (!((Object)(object)val5 == (Object)null))
				{
					if (editable)
					{
						allItems.Add(ItemCodec.Build(itemPrefab, val5, val4));
						continue;
					}
					val4.m_shared = val5.m_itemData.m_shared;
					val4.m_dropPrefab = itemPrefab;
					allItems.Add(val4);
				}
			}
			return val;
		}

		internal static Inventory Peek(Store store)
		{
			return Cache(store).Inventory;
		}

		internal static byte[] PeekBytes(Store store)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			Cached cached = Cache(store);
			if (cached.Items == null)
			{
				ZPackage val = new ZPackage();
				ItemCodec.WriteList(val, cached.Inventory.GetAllItems());
				cached.Items = val.GetArray();
			}
			return cached.Items;
		}

		private static Cached Cache(Store store)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			uint dataRevision = store.Zdo.DataRevision;
			if (!cache.TryGetValue(store.Zdo.m_uid, out var value) || value.Revision != dataRevision)
			{
				value = new Cached
				{
					Revision = dataRevision,
					Inventory = Read(store.Zdo, store.Prefab, editable: false)
				};
				cache[store.Zdo.m_uid] = value;
			}
			return value;
		}

		internal static void ClearCache()
		{
			cache.Clear();
			sweep = null;
		}

		internal static long Version(List<Store> stores)
		{
			long num = -3750763034362895579L;
			foreach (Store store in stores)
			{
				num = (num ^ ((ZDOID)(ref store.Zdo.m_uid)).UserID) * 1099511628211L;
				num = (num ^ ((ZDOID)(ref store.Zdo.m_uid)).ID) * 1099511628211L;
				num = (num ^ store.Zdo.DataRevision) * 1099511628211L;
			}
			if (num != 0L)
			{
				return num;
			}
			return 1L;
		}

		internal static Container PrefabContainer(ZDO zdo)
		{
			string name;
			return ContainerOf(zdo.GetPrefab(), out name);
		}

		internal static string[] DrawerLabels(ZDO zdo)
		{
			ContainerOf(zdo.GetPrefab(), out var name);
			if (name == null || !DrawerPrefabs.Contains(name))
			{
				return null;
			}
			return (from k in DrawerLabelKeys
				select zdo.GetString(k, "") into s
				where s != ""
				select s).ToArray();
		}

		internal static bool HasWardAccess(Vector3 hub, long playerID)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return HasWardAccess(Swept(hub, Plugin.HubRange).Wards, hub, playerID);
		}

		private static bool HasWardAccess(List<ZDO> wards, Vector3 point, long playerID)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			foreach (ZDO ward in wards)
			{
				if (ward.IsValid() && wardRadii.TryGetValue(ward.GetPrefab(), out var value) && ward.GetBool(ZDOVars.s_enabled, false) && !(Utils.DistanceXZ(ward.GetPosition(), point) >= value))
				{
					if (Permits(ward, playerID))
					{
						return true;
					}
					flag = true;
				}
			}
			return !flag;
		}

		private static bool Permits(ZDO ward, long playerID)
		{
			if (ward.GetLong(ZDOVars.s_creator, 0L) == playerID)
			{
				return true;
			}
			int num = ward.GetInt(ZDOVars.s_permitted, 0);
			for (int i = 0; i < num; i++)
			{
				if (ward.GetLong("pu_id" + i, 0L) == playerID)
				{
					return true;
				}
			}
			return false;
		}

		private static void FindWards()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			wardRadii = new Dictionary<int, float>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				PrivateArea val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<PrivateArea>() : null);
				if ((Object)(object)val != (Object)null && (int)val.m_ownerFaction == 0)
				{
					wardRadii[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = val.m_radius;
				}
			}
		}
	}
	internal static class ItemCodec
	{
		private static int itemVersion = -1;

		private static readonly Func<object, object> memberwiseClone = AccessTools.MethodDelegate<Func<object, object>>(AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null), (object)null, true);

		private static int ItemVersion
		{
			get
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Expected O, but got Unknown
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if (itemVersion < 0)
				{
					ZPackage val = new ZPackage();
					new Inventory("", (Sprite)null, 1, 1).Save(val);
					val.SetPos(0);
					itemVersion = val.ReadInt();
				}
				return itemVersion;
			}
		}

		internal static string PrefabName(ItemData item)
		{
			if (!((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return "";
			}
			return ((Object)item.m_dropPrefab).name;
		}

		internal static void Write(ZPackage pkg, ItemData item)
		{
			pkg.Write(PrefabName(item));
			item.Save(pkg);
		}

		internal static ItemData Read(ZPackage pkg)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			string text = pkg.ReadString();
			ItemData val = new ItemData();
			ItemData.Load(pkg, val, (Item)ItemVersion);
			GameObject val2 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
			ItemDrop val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val3 == (Object)null)
			{
				return null;
			}
			ItemData obj = Build(val2, val3, val);
			obj.m_gridPos = new Vector2i(-1, -1);
			return obj;
		}

		internal static ItemData Build(GameObject prefab, ItemDrop drop, ItemData saved)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = drop.m_itemData.Clone();
			val.m_shared = (SharedData)memberwiseClone(val.m_shared);
			val.m_dropPrefab = prefab;
			val.m_stack = saved.m_stack;
			val.m_durability = saved.m_durability;
			val.m_quality = saved.m_quality;
			val.m_variant = saved.m_variant;
			val.m_crafterID = saved.m_crafterID;
			val.m_crafterName = saved.m_crafterName;
			val.m_customData = saved.m_customData;
			val.m_worldLevel = saved.m_worldLevel;
			val.m_pickedUp = saved.m_pickedUp;
			val.m_cheated = saved.m_cheated;
			val.m_gridPos = saved.m_gridPos;
			return val.Clone();
		}

		internal static void WriteList(ZPackage pkg, ICollection<ItemData> items)
		{
			pkg.Write(items.Count);
			foreach (ItemData item in items)
			{
				Write(pkg, item);
			}
		}

		internal static List<ItemData> ReadList(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			List<ItemData> list = new List<ItemData>(num);
			for (int i = 0; i < num; i++)
			{
				ItemData val = Read(pkg);
				if (val != null)
				{
					list.Add(val);
				}
			}
			return list;
		}

		internal static string Key(ItemData item)
		{
			StringBuilder stringBuilder = new StringBuilder(PrefabName(item));
			stringBuilder.Append('|').Append(item.m_quality).Append('|')
				.Append(item.m_variant)
				.Append('|')
				.Append(item.m_worldLevel);
			if (item.m_shared.m_useDurability)
			{
				stringBuilder.Append("|d").Append((int)(item.m_durability * 100f));
			}
			if (item.m_customData != null && item.m_customData.Count > 0)
			{
				foreach (KeyValuePair<string, string> item2 in item.m_customData.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> kv) => kv.Key, StringComparer.Ordinal))
				{
					stringBuilder.Append('|').Append(item2.Key).Append('=')
						.Append(item2.Value);
				}
			}
			return stringBuilder.ToString();
		}

		internal static string NameKey(string sharedName)
		{
			return "@" + Game.m_worldLevel + "@" + sharedName;
		}

		internal static bool Matches(ItemData item, string key)
		{
			if (key.Length == 0 || key[0] != '@')
			{
				return Key(item) == key;
			}
			int num = key.IndexOf('@', 1);
			if (string.CompareOrdinal(item.m_shared.m_name, 0, key, num + 1, key.Length) == 0)
			{
				return item.m_worldLevel >= int.Parse(key.Substring(1, num - 1));
			}
			return false;
		}

		internal static ItemData CloneStack(ItemData item, int stack)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			ItemData obj = item.Clone();
			obj.m_stack = stack;
			obj.m_equipped = false;
			obj.m_gridPos = new Vector2i(-1, -1);
			return obj;
		}

		internal static int Room(Inventory inventory, ItemData item)
		{
			int maxStackSize = item.m_shared.m_maxStackSize;
			int num = inventory.GetWidth() * inventory.GetHeight() - inventory.NrOfItems();
			int num2 = Mathf.Max(0, num) * maxStackSize;
			if (maxStackSize > 1)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_worldLevel == item.m_worldLevel && allItem.m_stack < maxStackSize)
					{
						num2 += maxStackSize - allItem.m_stack;
					}
				}
			}
			return num2;
		}

		internal static ItemData AddWhatFits(Inventory inventory, ItemData item)
		{
			int num = Mathf.Min(item.m_stack, Room(inventory, item));
			if (num > 0)
			{
				inventory.AddItem(CloneStack(item, num));
			}
			if (num >= item.m_stack)
			{
				return null;
			}
			return CloneStack(item, item.m_stack - num);
		}

		internal static List<ItemData> RemoveMatching(Inventory inventory, string key, int amount)
		{
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData item in inventory.GetAllItems().ToList())
			{
				if (amount <= 0)
				{
					break;
				}
				if (Matches(item, key))
				{
					int num = Mathf.Min(item.m_stack, amount);
					list.Add(CloneStack(item, num));
					amount -= num;
					if (num == item.m_stack)
					{
						inventory.RemoveItem(item);
					}
					else
					{
						inventory.RemoveItem(item, num);
					}
				}
			}
			return list;
		}
	}
	internal static class LinkClient
	{
		internal sealed class Snapshot
		{
			internal sealed class Store
			{
				internal string Chest;

				internal ZDOID Id;

				internal long Creator;

				internal bool NoCrafty;

				internal List<ItemData> Items;
			}

			internal Vector3 HubPos;

			internal readonly List<Store> Stores = new List<Store>();

			internal IEnumerable<ItemData> Items => Stores.SelectMany((Store s) => s.Items);
		}

		private sealed class DecodedChest
		{
			internal byte[] Bytes;

			internal List<ItemData> Items;
		}

		[HarmonyPatch(typeof(Game), "Logout")]
		private static class ReturnEscrowOnLogout
		{
			private static void Prefix()
			{
				foreach (List<ItemData> value in escrow.Values)
				{
					Give(value);
				}
				escrow.Clear();
				waiting.Clear();
			}
		}

		internal static bool HubKnown;

		internal static bool HubExists;

		internal static ZDOID HubId = ZDOID.None;

		internal static Vector3 HubPos;

		internal static float Range;

		private static int nextRequest;

		private static float lastHubAsk = -100f;

		private static readonly Dictionary<int, Action<byte, ZPackage>> waiting = new Dictionary<int, Action<byte, ZPackage>>();

		private static readonly Dictionary<int, List<ItemData>> escrow = new Dictionary<int, List<ItemData>>();

		internal static int LastSnapshotBytes;

		internal static int LastSnapshotStacks;

		private static Snapshot held;

		private static long heldVersion;

		private static Dictionary<ZDOID, DecodedChest> decoded = new Dictionary<ZDOID, DecodedChest>();

		internal static bool Busy => waiting.Count > 0;

		internal static void Reset()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			HubKnown = (HubExists = false);
			HubId = ZDOID.None;
			ForgetSnapshot();
			waiting.Clear();
			escrow.Clear();
			lastHubAsk = -100f;
		}

		internal static void OnHub(long sender, ZPackage pkg)
		{
			//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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			HubKnown = true;
			HubExists = pkg.ReadBool();
			HubId = pkg.ReadZDOID();
			HubPos = pkg.ReadVector3();
			Range = pkg.ReadSingle();
		}

		internal static void Tick()
		{
			if (!HubKnown && ZRoutedRpc.instance != null && !((Object)(object)Player.m_localPlayer == (Object)null) && !(Time.realtimeSinceStartup - lastHubAsk < 3f))
			{
				lastHubAsk = Time.realtimeSinceStartup;
				Send(0, null, null);
			}
		}

		private static void Send(byte op, Action<ZPackage> write, Action<byte, ZPackage> onReply)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			int num = ++nextRequest;
			ZPackage val = new ZPackage();
			val.Write(num);
			val.Write(op);
			val.Write(Game.instance.GetPlayerProfile().GetPlayerID());
			write?.Invoke(val);
			if (onReply != null)
			{
				waiting[num] = onReply;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC("LinkedStorage_Request", new object[1] { val });
		}

		internal static void OnReply(long sender, ZPackage pkg)
		{
			int key = pkg.ReadInt();
			byte arg = pkg.ReadByte();
			if (waiting.TryGetValue(key, out var value))
			{
				waiting.Remove(key);
				value(arg, pkg);
			}
		}

		internal static void ForgetSnapshot()
		{
			held = null;
			decoded.Clear();
		}

		private static DecodedChest Decode(ZDOID id, byte[] bytes)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			if (!decoded.TryGetValue(id, out var value) || !Same(value.Bytes, bytes))
			{
				return new DecodedChest
				{
					Bytes = bytes,
					Items = ItemCodec.ReadList(new ZPackage(bytes))
				};
			}
			return value;
		}

		private static bool Same(byte[] a, byte[] 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;
		}

		internal static void RequestSnapshot(Action<byte, Snapshot> done)
		{
			Send(1, delegate(ZPackage pkg)
			{
				pkg.Write((held != null) ? heldVersion : 0);
			}, delegate(byte status, ZPackage pkg)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				ZPackage val = pkg.ReadPackage();
				LastSnapshotBytes = val.Size();
				if (status != 0)
				{
					done(status, new Snapshot());
				}
				else if (!val.ReadBool())
				{
					done(status, held ?? new Snapshot());
				}
				else
				{
					Snapshot snapshot = new Snapshot();
					long num = val.ReadLong();
					snapshot.HubPos = val.ReadVector3();
					int num2 = val.ReadInt();
					Dictionary<ZDOID, DecodedChest> dictionary = new Dictionary<ZDOID, DecodedChest>(num2);
					for (int i = 0; i < num2; i++)
					{
						Snapshot.Store store = new Snapshot.Store
						{
							Chest = val.ReadString(),
							Id = val.ReadZDOID(),
							Creator = val.ReadLong(),
							NoCrafty = val.ReadBool()
						};
						bool num3 = val.ReadBool();
						DecodedChest value = null;
						if (num3 ? (!decoded.TryGetValue(store.Id, out value)) : ((value = Decode(store.Id, val.ReadByteArray())) == null))
						{
							Plugin.Log.LogWarning((object)"Storage Hub snapshot out of step; asking again.");
							ForgetSnapshot();
							RequestSnapshot(done);
							return;
						}
						dictionary[store.Id] = value;
						store.Items = value.Items;
						snapshot.Stores.Add(store);
					}
					decoded = dictionary;
					LastSnapshotStacks = snapshot.Stores.Sum((Snapshot.Store s) => s.Items.Count);
					held = snapshot;
					heldVersion = num;
					done(status, snapshot);
				}
			});
		}

		internal static void Take(IList<KeyValuePair<string, int>> entries, Action<byte, List<ItemData>> done, ICollection<ZDOID> skip = null)
		{
			Send(2, delegate(ZPackage pkg)
			{
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				pkg.Write(entries.Count);
				foreach (KeyValuePair<string, int> entry in entries)
				{
					pkg.Write(entry.Key);
					pkg.Write(entry.Value);
				}
				pkg.Write(skip?.Count ?? 0);
				if (skip != null)
				{
					foreach (ZDOID item in skip)
					{
						pkg.Write(item);
					}
				}
			}, delegate(byte status, ZPackage pkg)
			{
				done(status, ItemCodec.ReadList(pkg));
			});
		}

		internal static void TakeToInventory(ItemData template, int amount, Action<byte, int> done)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			amount = Mathf.Min(amount, ItemCodec.Room(((Humanoid)localPlayer).GetInventory(), template));
			if (amount <= 0)
			{
				done?.Invoke(0, 0);
				return;
			}
			Take(new KeyValuePair<string, int>[1]
			{
				new KeyValuePair<string, int>(ItemCodec.Key(template), amount)
			}, delegate(byte status, List<ItemData> items)
			{
				done?.Invoke(status, Give(items));
			});
		}

		internal static int Give(List<ItemData> items)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			int num = 0;
			foreach (ItemData item in items)
			{
				num += item.m_stack;
				if (!((Object)(object)localPlayer == (Object)null))
				{
					ItemData val = ItemCodec.AddWhatFits(((Humanoid)localPlayer).GetInventory(), item);
					if (val != null)
					{
						ItemDrop.DropItem(val, val.m_stack, ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up, Quaternion.identity);
					}
				}
			}
			return num;
		}

		internal static void Deposit(IList<KeyValuePair<ItemData, int>> picks, byte mode, Action<byte, int> done)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			List<ItemData> list = new List<ItemData>();
			foreach (KeyValuePair<ItemData, int> pick in picks)
			{
				ItemData key = pick.Key;
				int num = Mathf.Min(pick.Value, key.m_stack);
				if (num > 0 && !key.m_equipped && inventory.ContainsItem(key))
				{
					list.Add(ItemCodec.CloneStack(key, num));
					if (num == key.m_stack)
					{
						inventory.RemoveItem(key);
					}
					else
					{
						inventory.RemoveItem(key, num);
					}
				}
			}
			DepositStacks(list, mode, done);
		}

		internal static void DepositStacks(List<ItemData> stacks, byte mode, Action<byte, int> done)
		{
			if (stacks.Count == 0)
			{
				done?.Invoke(0, 0);
				return;
			}
			int sent = stacks.Sum((ItemData s) => s.m_stack);
			int id = nextRequest + 1;
			escrow[id] = stacks;
			Send(3, delegate(ZPackage pkg)
			{
				pkg.Write(mode);
				ItemCodec.WriteList(pkg, stacks);
			}, delegate(byte status, ZPackage pkg)
			{
				escrow.Remove(id);
				List<ItemData> list = ItemCodec.ReadList(pkg);
				Give(list);
				done?.Invoke(status, sent - list.Sum((ItemData i) => i.m_stack));
			});
		}
	}
	internal sealed class LinkMirror : MonoBehaviour
	{
		[HarmonyPatch(typeof(Container), "Save")]
		private static class NeverSave
		{
			private static bool Prefix(Container __instance)
			{
				LinkMirror component = ((Component)__instance).GetComponent<LinkMirror>();
				if ((Object)(object)component == (Object)null)
				{
					return true;
				}
				if (!rebuilding && component.hidden == null)
				{
					component.Reconcile();
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Container), "Load")]
		private static class NeverLoad
		{
			private static bool Prefix(Container __instance, ref bool __result)
			{
				if ((Object)(object)((Component)__instance).GetComponent<LinkMirror>() == (Object)null)
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(Container), "OnDestroyed")]
		private static class NeverDrop
		{
			private static bool Prefix(Container __instance)
			{
				return (Object)(object)((Component)__instance).GetComponent<LinkMirror>() == (Object)null;
			}
		}

		internal const string ChildName = "LinkedStorage_mirror";

		private const float RefreshSeconds = 4f;

		private const float FilterSeconds = 1f;

		private static readonly List<LinkMirror> all = new List<LinkMirror>();

		private static bool rebuilding;

		private Container container;

		private ZNetView nview;

		private Pillar pillar;

		private Dictionary<string, int> baseline = new Dictionary<string, int>();

		private List<ItemData> hidden;

		private LinkClient.Snapshot last;

		private LinkClient.Snapshot filledFrom;

		private string lastSkipped = "";

		private float nextRefresh;

		private float nextFilter;

		private bool refreshing;

		private bool registered;

		internal static double LastFillMs;

		private static readonly FieldRef<Inventory, List<ItemData>> slots = AccessTools.FieldRefAccess<Inventory, List<ItemData>>("m_inventory");

		private static readonly Action<Inventory, bool, bool> changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), (object)null, true);

		private Inventory Inventory
		{
			get
			{
				if (!((Object)(object)container != (Object)null))
				{
					return null;
				}
				return container.GetInventory();
			}
		}

		private bool Useful
		{
			get
			{
				if (LinkClient.HubExists && Crafty.Loaded && (Object)(object)pillar != (Object)null)
				{
					return pillar.IsActive;
				}
				return false;
			}
		}

		private void Awake()
		{
			container = ((Component)this).GetComponent<Container>();
			nview = ((Component)this).GetComponentInParent<ZNetView>();
			pillar = ((Component)this).GetComponentInParent<Pillar>();
			all.Add(this);
		}

		private void OnDestroy()
		{
			all.Remove(this);
		}

		internal static bool IsMirror(Inventory inventory)
		{
			if (inventory != null)
			{
				return all.Any((LinkMirror m) => m.Inventory == inventory);
			}
			return false;
		}

		private bool Carries(ItemData item)
		{
			if (!((Object)(object)pillar == (Object)null))
			{
				return pillar.Carries(item);
			}
			return true;
		}

		private static IEnumerable<LinkMirror> Near(Player player)
		{
			return all.Where((LinkMirror m) => m.Inventory != null && m.hidden == null && Vector3.Distance(((Component)m).transform.position, ((Component)player).transform.position) <= Crafty.Range);
		}

		internal static bool AnyNear(Player player)
		{
			return Near(player).Any((LinkMirror m) => m.Inventory.NrOfItems() > 0);
		}

		internal static int CountNear(Player player, string sharedName)
		{
			return (from m in Near(player)
				select m.Inventory.CountItems(sharedName, -1, true)).DefaultIfEmpty(0).Max();
		}

		internal static string Describe(Player player)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (LinkMirror item in all)
			{
				if (item.Inventory == null)
				{
					continue;
				}
				float num = Vector3.Distance(((Component)item).transform.position, ((Component)player).transform.position);
				if (num > 60f)
				{
					continue;
				}
				string text = string.Join(", ", (from g in item.Inventory.GetAllItems().GroupBy(ItemCodec.PrefabName)
					orderby g.Key
					select $"{g.Key} {g.Sum((ItemData i) => i.m_stack)}").ToArray());
				object[] obj = new object[4]
				{
					num,
					item.Useful,
					Crafty.Registered(item.container),
					null
				};
				ZDO zDO = item.nview.GetZDO();
				obj[3] = ((zDO != null) ? new long?(zDO.GetLong(ZDOVars.s_creator, 0L)) : ((long?)null));
				list.Add(string.Format("mirror at {0:0} m (useful {1}, {2}, creator {3}): ", obj) + ((text == "") ? "empty" : text));
			}
			if (list.Count <= 0)
			{
				return "No Storage Link mirror within 60 m.";
			}
			return string.Join("\n", list.ToArray());
		}

		internal static void RefreshSoon()
		{
			foreach (LinkMirror item in all)
			{
				item.nextRefresh = 0f;
			}
		}

		private void Update()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if (Inventory == null || (Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			if (!registered && nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0L)
			{
				registered = true;
				Crafty.Register(container);
			}
			if (refreshing || hidden != null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < nextRefresh && (last == null || realtimeSinceStartup < nextFilter))
			{
				return;
			}
			Player player = Player.m_localPlayer;
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			float distance = Vector3.Distance(((Component)player).transform.position, ((Component)this).transform.position);
			bool flag = all.All((LinkMirror m) => (Object)(object)m == (Object)(object)this || m.Inventory == null || Vector3.Distance(((Component)player).transform.position, ((Component)m).transform.position) >= distance);
			if (!Useful || !flag || distance > Crafty.Range + 10f)
			{
				last = null;
				if (Inventory.NrOfItems() > 0)
				{
					Fill(null);
				}
				nextRefresh = realtimeSinceStartup + 2f;
				return;
			}
			if (realtimeSinceStartup < nextRefresh)
			{
				nextFilter = realtimeSinceStartup + 1f;
				Fill(last, onlyIfMoved: true);
				return;
			}
			refreshing = true;
			LinkClient.RequestSnapshot(delegate(byte status, LinkClient.Snapshot snapshot)
			{
				refreshing = false;
				nextRefresh = Time.realtimeSinceStartup + 4f;
				nextFilter = Time.realtimeSinceStartup + 1f;
				last = ((status == 0) ? snapshot : null);
				if ((Object)(object)this != (Object)null && hidden == null)
				{
					Fill(last);
				}
			});
		}

		private unsafe void Fill(LinkClient.Snapshot snapshot, bool onlyIfMoved = false)
		{
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = Inventory;
			List<ItemData> list = new List<ItemData>();
			Player localPlayer = Player.m_localPlayer;
			HashSet<ZDOID> hashSet = ((snapshot != null && (Object)(object)localPlayer != (Object)null) ? Crafty.NearbyIds(localPlayer) : new HashSet<ZDOID>());
			string text = string.Join(",", (from id in hashSet
				select ((object)(*(ZDOID*)(&id))/*cast due to .constrained prefix*/).ToString() into s
				orderby s
				select s).ToArray());
			if (text == lastSkipped && (onlyIfMoved || (snapshot != null && snapshot == filledFrom)))
			{
				return;
			}
			lastSkipped = text;
			filledFrom = snapshot;
			if (snapshot != null && (Object)(object)localPlayer != (Object)null)
			{
				int num = Crafty.PullMode(localPlayer);
				long playerID = localPlayer.GetPlayerID();
				Dictionary<string, bool> dictionary = new Dictionary<string, bool>();
				foreach (LinkClient.Snapshot.Store store in snapshot.Stores)
				{
					if (num == 0 || (num == 2 && store.Creator != playerID) || (store.NoCrafty && Crafty.ChestToggle) || hashSet.Contains(store.Id))
					{
						continue;
					}
					foreach (ItemData item in store.Items)
					{
						if (Carries(item))
						{
							string text2 = ItemCodec.PrefabName(item);
							string key = store.Chest + "|" + text2;
							if (!dictionary.TryGetValue(key, out var value))
							{
								value = (dictionary[key] = Crafty.CanPull(store.Chest, text2));
							}
							if (value)
							{
								list.Add(item);
							}
						}
					}
				}
			}
			Rebuild(inventory, list);
		}

		private void Rebuild(Inventory inventory, List<ItemData> items)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			Stopwatch stopwatch = Stopwatch.StartNew();
			Dictionary<string, KeyValuePair<ItemData, int>> dictionary = new Dictionary<string, KeyValuePair<ItemData, int>>();
			List<string> list = new List<string>();
			foreach (ItemData item in items)
			{
				string text = ItemCodec.Key(item);
				if (dictionary.TryGetValue(text, out var value))
				{
					dictionary[text] = new KeyValuePair<ItemData, int>(value.Key, value.Value + item.m_stack);
					continue;
				}
				dictionary[text] = new KeyValuePair<ItemData, int>(item, item.m_stack);
				list.Add(text);
			}
			List<ItemData> list2 = new List<ItemData>();
			int width = inventory.GetWidth();
			foreach (string item2 in list)
			{
				ItemData val = ItemCodec.CloneStack(dictionary[item2].Key, dictionary[item2].Value);
				val.m_gridPos = new Vector2i(list2.Count % width, list2.Count / width);
				list2.Add(val);
			}
			rebuilding = true;
			try
			{
				inventory.SetHeight(Mathf.Max(4, list2.Count / width + 1));
				List<ItemData> obj = slots.Invoke(inventory);
				obj.Clear();
				obj.AddRange(list2);
				changed(inventory, arg2: false, arg3: false);
			}
			finally
			{
				rebuilding = false;
			}
			baseline = Counts(inventory);
			LastFillMs = stopwatch.Elapsed.TotalMilliseconds;
		}

		private static Dictionary<string, int> Counts(Inventory inventory)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				string key = ItemCodec.Key(allItem);
				dictionary[key] = (dictionary.TryGetValue(key, out var value) ? value : 0) + allItem.m_stack;
			}
			return dictionary;
		}

		internal static void HideAll()
		{
			foreach (LinkMirror item in all)
			{
				if (item.Inventory != null && item.hidden == null)
				{
					item.hidden = new List<ItemData>(slots.Invoke(item.Inventory));
					item.SetItems(null);
				}
			}
		}

		internal static void ShowAll()
		{
			foreach (LinkMirror item in all)
			{
				if (item.hidden != null)
				{
					List<ItemData> items = item.hidden;
					item.hidden = null;
					if (item.Inventory != null)
					{
						item.SetItems(items);
					}
				}
			}
		}

		private void SetItems(List<ItemData> items)
		{
			rebuilding = true;
			try
			{
				List<ItemData> list = slots.Invoke(Inventory);
				list.Clear();
				if (items != null)
				{
					list.AddRange(items);
				}
				changed(Inventory, arg2: false, arg3: false);
			}
			finally
			{
				rebuilding = false;
			}
		}

		private void Reconcile()
		{
			Dictionary<string, int> dictionary = Counts(Inventory);
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>();
			foreach (KeyValuePair<string, int> item in baseline)
			{
				int value;
				int num = (dictionary.TryGetValue(item.Key, out value) ? value : 0);
				if (num < item.Value)
				{
					list.Add(new KeyValuePair<string, int>(item.Key, item.Value - num));
				}
			}
			List<ItemData> list2 = new List<ItemData>();
			foreach (ItemData item2 in Inventory.GetAllItems().ToList())
			{
				string key = ItemCodec.Key(item2);
				int value2;
				int num2 = (baseline.TryGetValue(key, out value2) ? value2 : 0);
				int value3;
				int num3 = (dictionary.TryGetValue(key, out value3) ? value3 : 0) - num2;
				if (num3 > 0)
				{
					int num4 = Mathf.Min(num3, item2.m_stack);
					list2.Add(ItemCodec.CloneStack(item2, num4));
					dictionary[key] -= num4;
					rebuilding = true;
					try
					{
						Inventory.RemoveItem(item2, num4);
					}
					finally
					{
						rebuilding = false;
					}
				}
			}
			baseline = Counts(Inventory);
			if (list.Count > 0)
			{
				int wanted = list.Sum((KeyValuePair<string, int> t) => t.Value);
				LinkClient.Take(list, delegate(byte status, List<ItemData> items)
				{
					int num5 = items.Sum((ItemData i) => i.m_stack);
					if (num5 < wanted)
					{
						Plugin.Log.LogWarning((object)$"Another mod used {wanted - num5} items from a Storage Link that the hub no longer had.");
					}
					RefreshSoon();
				}, ((Object)(object)Player.m_localPlayer != (Object)null) ? Crafty.NearbyIds(Player.m_localPlayer) : null);
			}
			List<ItemData> list3 = (((Object)(object)pillar != (Object)null && !pillar.TakesDeposits) ? list2 : new List<ItemData>());
			if (list3.Count > 0)
			{
				LinkClient.Give(list3);
				list2 = list2.Except(list3).ToList();
			}
			if (list2.Count > 0)
			{
				LinkClient.DepositStacks(list2, 0, delegate
				{
					RefreshSoon();
				});
			}
		}
	}
	internal sealed class LsmModel
	{
		internal sealed class Node
		{
			internal string Name;

			internal Vector3 Position;

			internal Quaternion Rotation;

			internal Mesh Mesh;

			internal string[] Materials;
		}

		internal readonly List<Node> Nodes = new List<Node>();

		private readonly Dictionary<string, KeyValuePair<Vector3, float>> markers = new Dictionary<string, KeyValuePair<Vector3, float>>();

		internal Vector3 MarkerPosition(string name)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			return markers[name].Key;
		}

		internal float MarkerValue(string name)
		{
			return markers[name].Value;
		}

		internal static LsmModel Load(string file)
		{
			using Stream stream = typeof(LsmModel).Assembly.GetManifestResourceStream("LinkedStorage.Models." + file);
			if (stream == null)
			{
				throw new FileNotFoundException("embedded model missing", file);
			}
			using BinaryReader r = new BinaryReader(stream);
			return Read(r, file);
		}

		private static LsmModel Read(BinaryReader r, string file)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			if (Encoding.ASCII.GetString(r.ReadBytes(4)) != "LSM1")
			{
				throw new InvalidDataException(file + ": not an LSM1 file");
			}
			LsmModel lsmModel = new LsmModel();
			int num = r.ReadInt32();
			for (int i = 0; i < num; i++)
			{
				Node node = new Node
				{
					Name = ReadString(r),
					Position = ReadVector(r)
				};
				float[] array = new float[9];
				for (int j = 0; j < 9; j++)
				{
					array[j] = r.ReadSingle();
				}
				node.Rotation = Quaternion.LookRotation(new Vector3(array[2], array[5], array[8]), new Vector3(array[1], array[4], array[7]));
				int num2 = r.ReadInt32();
				List<Vector3> list = new List<Vector3>();
				List<Vector3> list2 = new List<Vector3>();
				List<Vector2> list3 = new List<Vector2>();
				List<int[]> list4 = new List<int[]>();
				node.Materials = new string[num2];
				for (int k = 0; k < num2; k++)
				{
					node.Materials[k] = ReadString(r);
					int count = list.Count;
					int num3 = r.ReadInt32();
					for (int l = 0; l < num3; l++)
					{
						list.Add(ReadVector(r));
						list2.Add(ReadVector(r));
						list3.Add(new Vector2(r.ReadSingle(), r.ReadSingle()));
					}
					int num4 = r.ReadInt32();
					int[] array2 = new int[num4];
					for (int m = 0; m < num4; m++)
					{
						array2[m] = count + r.ReadInt32();
					}
					list4.Add(array2);
				}
				Mesh val = new Mesh
				{
					name = "LinkedStorage_" + Path.GetFileNameWithoutExtension(file) + "_" + node.Name
				};
				if (list.Count > 65535)
				{
					val.indexFormat = (IndexFormat)1;
				}
				val.SetVertices(list);
				val.SetNormals(list2);
				val.SetUVs(0, list3);
				val.subMeshCount = num2;
				for (int n = 0; n < num2; n++)
				{
					val.SetTriangles(list4[n], n);
				}
				val.RecalculateBounds();
				node.Mesh = val;
				lsmModel.Nodes.Add(node);
			}
			int num5 = r.ReadInt32();
			for (int num6 = 0; num6 < num5; num6++)
			{
				string key = ReadString(r);
				Vector3 key2 = ReadVector(r);
				lsmModel.markers[key] = new KeyValuePair<Vector3, float>(key2, r.ReadSingle());
			}
			if (r.BaseStream.Position != r.BaseStream.Length)
			{
				throw new InvalidDataException(file + ": trailing bytes");
			}
			return lsmModel;
		}

		private static Vector3 ReadVector(BinaryReader r)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
		}

		private static string ReadString(BinaryReader r)
		{
			return Encoding.UTF8.GetString(r.ReadBytes(r.ReadInt32()));
		}
	}
	internal static class Net
	{
		internal sealed class Runner : MonoBehaviour
		{
		}

		[HarmonyPatch(typeof(Game), "Start")]
		private static class GameStart
		{
			private static void Postfix()
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				instance.Register<ZPackage>("LinkedStorage_Request", (Action<long, ZPackage>)HubServer.OnRequest);
				instance.Register<ZPackage>("LinkedStorage_Reply", (Action<long, ZPackage>)LinkClient.OnReply);
				instance.Register<ZPackage>("LinkedStorage_Edit", (Action<long, ZPackage>)OnEdit);
				instance.Register<ZPackage>("LinkedStorage_EditResult", (Action<long, ZPackage>)HubServer.OnEditResult);
				instance.Register<ZPackage>("LinkedStorage_Hub", (Action<long, ZPackage>)LinkClient.OnHub);
				LinkClient.Reset();
				Runner runner = ((Component)ZNet.instance).gameObject.AddComponent<Runner>();
				if (ZNet.instance.IsServer())
				{
					HubServer.Start((MonoBehaviour)(object)runner);
				}
			}
		}

		internal const string Request = "LinkedStorage_Request";

		internal const string Reply = "LinkedStorage_Reply";

		internal const string Edit = "LinkedStorage_Edit";

		internal const string EditResult = "LinkedStorage_EditResult";

		internal const string Hub = "LinkedStorage_Hub";

		internal const byte OpHub = 0;

		internal const byte OpSnapshot = 1;

		internal const byte OpTake = 2;

		internal const byte OpDeposit = 3;

		internal const byte Ok = 0;

		internal const byte NoHub = 1;

		internal const byte NoAccess = 2;

		internal const byte Failed = 3;

		private static void OnEdit(long sender, ZPackage pkg)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			int num = pkg.ReadInt();
			ZDOID val = pkg.ReadZDOID();
			ZPackage edit = pkg.ReadPackage();
			ZPackage val2 = ChestEdits.Apply(ZDOMan.instance.GetZDO(val), edit, asServer: false);
			ZPackage val3 = new ZPackage();
			val3.Write(num);
			val3.Write(val2);
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "LinkedStorage_EditResult", new object[1] { val3 });
		}
	}
	internal sealed class Pillar : MonoBehaviour, Hoverable, Interactable
	{
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static class OneHubPerWorld
		{
			private static bool Prefix(Player __instance, Piece piece, ref bool __result)
			{
				if ((Object)(object)piece == (Object)null || Utils.GetPrefabName(((Component)piece).gameObject) != "piece_linkedstorage_hub" || !LinkClient.HubExists)
				{
					return true;
				}
				((Character)__instance).Message((MessageType)2, "$msg_linkedstorage_onehub", 0, (Sprite)null, false);
				__result = false;
				return false;
			}
		}

		[SerializeField]
		internal bool isHub;

		[SerializeField]
		internal bool isStone;

		private ZNetView nview;

		internal bool Restricted
		{
			get
			{
				if (isStone)
				{
					if (!((Object)(object)ZoneSystem.instance == (Object)null))
					{
						return !ZoneSystem.instance.GetGlobalKey((GlobalKeys)35);
					}
					return true;
				}
				return false;
			}
		}

		internal bool TakesDeposits => !isStone;

		internal bool IsActive
		{
			get
			{
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				if (isHub)
				{
					if ((Object)(object)nview != (Object)null && nview.IsValid())
					{
						return nview.GetZDO().m_uid == LinkClient.HubId;
					}
					return false;
				}
				return true;
			}
		}

		private string Name
		{
			get
			{
				if (!isHub)
				{
					if (!isStone)
					{
						return "$piece_linkedstorage_link";
					}
					return "$piece_linkedstorage_stonelink";
				}
				return "$piece_linkedstorage_hub";
			}
		}

		internal bool Carries(ItemData item)
		{
			if (Restricted)
			{
				return item.m_shared.m_teleportable;
			}
			return true;
		}

		private void Awake()
		{
			nview = ((Component)this).GetComponent<ZNetView>();
			if (isHub && (Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner() && nview.GetZDO().GetLong("LinkedStorage_born", 0L) == 0L)
			{
				nview.GetZDO().Set("LinkedStorage_born", ZNet.instance.GetTime().Ticks);
			}
		}

		public string GetHoverName()
		{
			return Name;
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			string name = Name;
			name = ((!LinkClient.HubKnown) ? (name + "\n$msg_linkedstorage_connecting") : ((!LinkClient.HubExists) ? (name + "\n$msg_linkedstorage_nohub") : ((!IsActive) ? (name + "\n$msg_linkedstorage_secondhub") : (PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false) ? (name + "\n[<color=yellow><b>$KEY_Use</b></color>] $piece_container_open") : (name + "\n$piece_noaccess")))));
			return Localization.instance.Localize(name);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (hold || !LinkClient.HubExists || !IsActive)
			{
				return false;
			}
			if (!PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return true;
			}
			StorageWindow.Open(this);
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal sealed class PillarFx : MonoBehaviour
	{
		[SerializeField]
		internal Transform orbit;

		[SerializeField]
		internal Transform[] halos;

		[SerializeField]
		internal Transform floater;

		[SerializeField]
		internal Transform heart;

		[SerializeField]
		internal Light heartLight;

		[SerializeField]
		internal ParticleSystem embers;

		[SerializeField]
		internal AudioSource hum;

		[SerializeField]
		internal float emberScale = 1f;

		[SerializeField]
		internal float humScale = 1f;

		private Quaternion[] haloRest;

		private Vector3 floaterRest;

		private Vector3 heartRest;

		private Quaternion heartRestRotation;

		private float phase;

		private bool placed;

		private static bool warned;

		private void Start()
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			if (!warned && ((Object)(object)orbit == (Object)null || halos == null || (Object)(object)floater == (Object)null || (Object)(object)hum == (Object)null))
			{
				warned = true;
				ManualLogSource log = Plugin.Log;
				object[] obj = new object[5]
				{
					((Object)this).name,
					(Object)(object)orbit != (Object)null,
					null,
					null,
					null
				};
				Transform[] array = halos;
				obj[2] = ((array != null) ? array.Length : 0);
				obj[3] = (Object)(object)floater != (Object)null;
				obj[4] = (Object)(object)hum != (Object)null;
				log.LogWarning((object)string.Format("{0}: pillar effect references missing (orbit {1}, halos {2}, tip {3}, hum {4}).", obj));
			}
			Transform[] array2 = halos;
			haloRest = (Quaternion[])(object)new Quaternion[(array2 != null) ? array2.Length : 0];
			for (int i = 0; i < haloRest.Length; i++)
			{
				haloRest[i] = halos[i].localRotation;
			}
			if ((Object)(object)floater != (Object)null)
			{
				floaterRest = floater.localPosition;
				Renderer val = default(Renderer);
				if (((Component)floater).TryGetComponent<Renderer>(ref val))
				{
					val.sharedMaterials = val.sharedMaterials.Select(PillarMaterials.Unnoised).ToArray();
				}
			}
			if ((Object)(object)heart != (Object)null)
			{
				heartRest = heart.localPosition;
				heartRestRotation = heart.localRotation;
			}
			phase = Random.Range(0f, 100f);
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			placed = (Object)(object)component != (Object)null && component.IsValid();
			if ((Object)(object)heartLight != (Object)null)
			{
				((Behaviour)heartLight).enabled = placed;
			}
			if ((Object)(object)embers != (Object)null && !placed)
			{
				embers.Stop(true, (ParticleSystemStopBehavior)0);
			}
			if ((Object)(object)hum != (Object)null)
			{
				if (placed && (Object)(object)hum.clip != (Object)null)
				{
					hum.time = Random.Range(0f, hum.clip.length);
					hum.Play();
				}
				else
				{
					((Behaviour)hum).enabled = false;
				}
			}
		}

		private static float Noise(float t, float seed)
		{
			return 2f * Mathf.PerlinNoise(t * 0.18f, seed) - 1f;
		}

		internal string Describe()
		{
			//IL_0033: 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)
			string[] obj = new string[6]
			{
				string.Format("placed {0}, orbit {1}, ", placed, ((Object)(object)orbit != (Object)null) ? (orbit.localEulerAngles.y.ToString("0") + " deg") : "missing"),
				null,
				null,
				null,
				null,
				null
			};
			Transform[] array = halos;
			obj[1] = string.Format("halos {0}, tip {1}, ", (array != null) ? array.Length : 0, ((Object)(object)floater != (Object)null) ? "ok" : "missing");
			obj[2] = "hum ";
			obj[3] = (((Object)(object)hum == (Object)null) ? "missing" : (hum.isPlaying ? $"playing at {hum.volume:0.00}" : "silent"));
			obj[4] = ", light ";
			obj[5] = (((Object)(object)heartLight != (Object)null && ((Behaviour)heartLight).enabled) ? heartLight.intensity.ToString("0.00") : "off");
			return string.Concat(obj);
		}

		private void Update()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			float num = Time.time + phase;
			if ((Object)(object)orbit != (Object)null)
			{
				orbit.localRotation = Quaternion.Euler(0f, num * 40f, 0f);
			}
			for (int i = 0; i < haloRest.Length; i++)
			{
				float num2 = ((i % 2 == 0) ? (-1f) : 1f);
				halos[i].localRotation = haloRest[i] * Quaternion.Euler(0f, num2 * num * 20f, 0f);
			}
			if ((Object)(object)floater != (Object)null)
			{
				floater.localPosition = floaterRest + Vector3.up * (0.05f * Mathf.Sin((float)Math.PI * 2f * num / 6f));
			}
			if ((Object)(object)heart != (Object)null)
			{
				heart.localPosition = heartRest + Vector3.up * (Noise(num, 7.3f) * 0.42f);
				float t = num * 0.5f;
				heart.localRotation = Quaternion.Euler(Noise(t, 21.7f) * 20f, Noise(t, 31.9f) * 20f * 2f, Noise(t, 43.3f) * 20f) * heartRestRotation;
			}
			if (placed)
			{
				if ((Object)(object)heartLight != (Object)null)
				{
					float num3 = 0.5f - 0.5f * Mathf.Cos((float)Math.PI * 2f * Time.time / 5f);
					heartLight.intensity = 2f * Mathf.Lerp(0.85f, 1f, num3);
				}
				if ((Object)(object)hum != (Object)null)
				{
					hum.volume = Plugin.HumVolume * humScale;
				}
				if ((Object)(object)embers != (Object)null)
				{
					EmissionModule emission = embers.emission;
					((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(20f * emberScale);
				}
			}
		}
	}
	internal static class PillarMaterials
	{
		private static readonly Color LavaColor = new Color(1f, 0.3f, 0.04f);

		private static readonly Color RuneColor = new Color(0.2f, 1f, 0.4f);

		private const float RuneGlow = 1.5f;

		private static Material stone;

		private static Material flametal;

		private static Material lava;

		private static Material runes;

		private static Material halo;

		private static Material core;

		private static Material plainStone;

		private static Material plainStoneDark;

		private static Color coreEmission;

		private const string ValueNoise = "_VALUENOISEVERTEX_ON";

		private static readonly Dictionary<Material, Material> unnoised = new Dictionary<Material, Material>();

		internal static Material Get(string key)
		{
			switch (key)
			{
			case "stone":
				return stone;
			case "flametal":
				return flametal;
			case "lava":
				return lava;
			case "runes":
				return runes;
			case "halo":
				return halo;
			default:
				Plugin.Log.LogWarning((object)("Unknown material key '" + key + "' in a pillar model."));
				return stone;
			}
		}

		internal static Material GetStony(string key)
		{
			if (!(key == "lava"))
			{
				if (key == "flametal")
				{
					return plainStoneDark;
				}
				return plainStone;
			}
			return lava;
		}

		internal static Material Unnoised(Material material)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			if ((Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null || ((Object)material.shader).name != "Custom/Piece" || ((Object)material).name.EndsWith("_still"))
			{
				return material;
			}
			if ((Object)(object)material == (Object)(object)lava || (Object)(object)material == (Object)(object)runes)
			{
				Still(material);
				return material;
			}
			if (!unnoised.TryGetValue(material, out var value))
			{
				value = new Material(material)
				{
					name = ((Object)material).name + "_still"
				};
				Still(value);
				unnoised[material] = value;
			}
			return value;
		}

		private static void Still(Material material)
		{
			material.DisableKeyword("_VALUENOISEVERTEX_ON");
			string[] propertyNames = material.GetPropertyNames((MaterialPropertyType)0);
			foreach (string text in propertyNames)
			{
				if (text.IndexOf("ValueNoise", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					material.SetFloat(text, 0f);
				}
			}
		}

		internal static void Load()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			if (!((Object)(object)stone != (Object)null))
			{
				stone = FindMaterial("Ashlands_Stone_Ashen_Rough_mat") ?? PieceMaterial("Piece_grausten_pillarbase_medium", "new");
				flametal = PieceMaterial("Piece_flametal_pillar", "new") ?? stone;
				plainStone = PieceMaterial("Rock_3", "") ?? stone;
				plainStoneDark = new Material(plainStone)
				{
					name = "LinkedStorage_stone_dark",
					color = plainStone.color * 0.55f
				};
				lava = new Material(stone)
				{
					name = "LinkedStorage_lava",
					color = new Color(0.12f, 0.04f, 0.01f)
				};
				Glow(lava, null);
				Texture2D val = RuneGlyphs.Create();
				runes = new Material(stone)
				{
					name = "LinkedStorage_runes",
					color = stone.color * 0.4f
				};
				Glow(runes, (Texture)(object)val);
				halo = HaloMaterial((Texture)(object)val);
				Animate(0f);
			}
		}

		internal static void Animate(float time)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)stone == (Object)null)
			{
				return;
			}
			float num = 0.5f - 0.5f * Mathf.Cos((float)M