Decompiled source of SmelterLinkMPFix v0.1.0

SmelterLinkFix.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("SmelterLinkFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9eab8a94d563b5e02bc6be49a5ce9c5766bcb90f")]
[assembly: AssemblyProduct("SmelterLinkFix")]
[assembly: AssemblyTitle("SmelterLinkFix")]
[assembly: AssemblyVersion("0.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 SmelterLinkFix
{
	internal static class ActionBlockPatches
	{
		private static bool Blocking(Player player)
		{
			if (Plugin.BlockActions.Value)
			{
				return InputBlockPatches.IsLinking(player);
			}
			return false;
		}

		public static bool TakeInputPrefix(Player __instance, ref bool __result)
		{
			if (!Blocking(__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}

		public static void SetControlsPrefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, bool jump)
		{
			if (Blocking(__instance))
			{
				attack = (attackHold = false);
				secondaryAttack = (secondaryAttackHold = false);
				if (!jump)
				{
					block = (blockHold = false);
				}
			}
		}

		public static bool InventoryShowPrefix()
		{
			return !Blocking(Player.m_localPlayer);
		}

		public static bool SetMapModePrefix(MapMode mode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)mode == 2)
			{
				return !Blocking(Player.m_localPlayer);
			}
			return true;
		}

		public static void ApplyDamagePrefix(Character __instance, out float __state)
		{
			__state = -1f;
			try
			{
				if (Plugin.CancelOnDamage.Value && InputBlockPatches.IsLinking((Player)(object)((__instance is Player) ? __instance : null)))
				{
					__state = __instance.GetHealth();
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("ApplyDamagePrefix", e);
			}
		}

		public static void ApplyDamagePostfix(Character __instance, HitData hit, float __state)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			try
			{
				if (!(__state < 0f) && hit != null && !(__instance.GetHealth() >= __state) && ((int)hit.m_hitType == 1 || (int)hit.m_hitType == 2 || (int)hit.m_hitType == 16) && InputBlockPatches.IsLinking((Player)(object)((__instance is Player) ? __instance : null)))
				{
					LinkModeAccess.Cancel();
					Notify.Center("Linking cancelled: you were hit");
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("ApplyDamagePostfix", e);
			}
		}
	}
	internal static class ChestAccess
	{
		private static Func<Container, bool> s_load;

		public static bool Init()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
			{
				return false;
			}
			s_load = (Func<Container, bool>)Delegate.CreateDelegate(typeof(Func<Container, bool>), methodInfo, throwOnBindFailure: false);
			return s_load != null;
		}

		public static Container Live(ZDO chest)
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			ZNetView val = ZNetScene.instance.FindInstance(chest);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponentInChildren<Container>();
		}

		public static void Refresh(Container live)
		{
			if (!live.IsInUse())
			{
				s_load(live);
			}
		}

		public static bool IsInUse(ZDO chest, Container live)
		{
			if ((Object)(object)live != (Object)null && chest.IsOwner())
			{
				return live.IsInUse();
			}
			return chest.GetInt(ZDOVars.s_inUse, 0) == 1;
		}

		public static bool HasLiveOwner(ZDO chest)
		{
			if (!chest.HasOwner())
			{
				return false;
			}
			long owner = chest.GetOwner();
			if (owner == ZDOMan.GetSessionID() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return true;
			}
			return ZNet.instance.GetPeer(owner) != null;
		}

		public static bool TryClaimUnowned(ZDO chest)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			if (HasLiveOwner(chest) || chest.GetInt(ZDOVars.s_inUse, 0) == 1)
			{
				return false;
			}
			chest.SetOwner(ZDOMan.GetSessionID());
			return true;
		}

		public static int TakeLocal(ZDO chest, string prefabName, int want)
		{
			Container val = Live(chest);
			if (IsInUse(chest, val))
			{
				return 0;
			}
			if ((Object)(object)val != (Object)null)
			{
				Refresh(val);
				return RemoveMatching(val.GetInventory(), prefabName, want);
			}
			Inventory val2 = LoadFromZdo(chest);
			if (val2 == null)
			{
				return 0;
			}
			int num = RemoveMatching(val2, prefabName, want);
			if (num > 0)
			{
				SaveToZdo(chest, val2);
			}
			return num;
		}

		public static bool PutLocal(ZDO chest, string prefabName, int amount)
		{
			Container val = Live(chest);
			if ((Object)(object)val != (Object)null)
			{
				Refresh(val);
				return AddIfFits(val.GetInventory(), prefabName, amount);
			}
			Inventory val2 = LoadFromZdo(chest);
			if (val2 == null || !AddIfFits(val2, prefabName, amount))
			{
				return false;
			}
			SaveToZdo(chest, val2);
			return true;
		}

		public static bool Fits(ZDO chest, string prefabName, int amount)
		{
			Inventory val = LoadFromZdo(chest);
			if (val != null)
			{
				return CanFit(val, prefabName, amount);
			}
			return false;
		}

		public static void Drop(string prefabName, int amount, Vector3 position)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				Plugin.Log.LogError((object)$"Cannot drop {amount}x {prefabName}: item prefab or scene not available, items lost");
				return;
			}
			int num = Mathf.Max(1, val2.m_itemData.m_shared.m_maxStackSize);
			int num2 = amount;
			while (num2 > 0)
			{
				int num3 = Mathf.Min(num2, num);
				num2 -= num3;
				ItemDrop component = Object.Instantiate<GameObject>(val, position + Vector3.up + Random.insideUnitSphere * 0.3f, Quaternion.identity).GetComponent<ItemDrop>();
				component.m_itemData.m_stack = num3;
				ItemDrop.OnCreateNew(component, false);
				component.SetStack(num3);
			}
			Plugin.Log.LogWarning((object)$"Dropped {amount}x {prefabName} at {position:0} (chest full or unreachable)");
		}

		private static int RemoveMatching(Inventory inventory, string prefabName, int want)
		{
			int num = 0;
			foreach (ItemData item in new List<ItemData>(inventory.GetAllItems()))
			{
				if (num >= want)
				{
					break;
				}
				if (!((Object)(object)item?.m_dropPrefab == (Object)null) && !(((Object)item.m_dropPrefab).name != prefabName))
				{
					int num2 = Mathf.Min(item.m_stack, want - num);
					if (inventory.RemoveItem(item, num2))
					{
						num += num2;
					}
				}
			}
			return num;
		}

		private static bool AddIfFits(Inventory inventory, string prefabName, int amount)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!CanFit(inventory, prefabName, amount))
			{
				return false;
			}
			ItemData obj = inventory.AddItem(prefabName, amount, 1, 0, 0L, "", new Vector2i(-1, -1), false, false, false);
			if (obj == null)
			{
				Plugin.Log.LogError((object)$"AddItem {amount}x {prefabName} failed after CanAddItem said it fits");
			}
			return obj != null;
		}

		private static bool CanFit(Inventory inventory, string prefabName, int amount)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			ItemData val3 = val2.m_itemData.Clone();
			val3.m_quality = 1;
			val3.m_worldLevel = (byte)Game.m_worldLevel;
			return inventory.CanAddItem(val3, amount);
		}

		private static Inventory LoadFromZdo(ZDO chest)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(chest.GetPrefab()) : null);
			Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren<Container>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			Inventory val3 = new Inventory(val2.m_name, val2.m_bkg, val2.m_width, val2.m_height);
			byte[] byteArray = chest.GetByteArray(ZDOVars.s_items, (byte[])null);
			if (byteArray != null)
			{
				val3.Load(new ZPackage(byteArray));
			}
			return val3;
		}

		private static void SaveToZdo(ZDO chest, Inventory inventory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			inventory.Save(val);
			chest.Set(ZDOVars.s_items, val.GetArray());
		}
	}
	internal static class ChestFinder
	{
		private delegate void FindObjectsFn(ZDOMan self, Vector2s sector, List<ZDO> objects, HashSet<SectorIndex> visited);

		private struct Miss
		{
			public float RetryAt;

			public float Delay;
		}

		private const float MinRetrySeconds = 1f;

		private const float MaxRetrySeconds = 10f;

		private static FindObjectsFn s_findObjects;

		private static ZDOMan s_zdoMan;

		private static readonly Dictionary<(long, Vector2s), ZDO> s_found = new Dictionary<(long, Vector2s), ZDO>();

		private static readonly Dictionary<(long, Vector2s), Miss> s_misses = new Dictionary<(long, Vector2s), Miss>();

		private static readonly Dictionary<int, bool> s_containerPrefabs = new Dictionary<int, bool>();

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

		private static readonly HashSet<SectorIndex> s_visited = new HashSet<SectorIndex>();

		public static bool Init()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ZDOMan), "FindObjects", new Type[3]
			{
				typeof(Vector2s),
				typeof(List<ZDO>),
				typeof(HashSet<SectorIndex>)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			s_findObjects = (FindObjectsFn)Delegate.CreateDelegate(typeof(FindObjectsFn), methodInfo, throwOnBindFailure: false);
			return s_findObjects != null;
		}

		public static ZDO Find(long uid, Vector3 near)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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)
			ZDOMan instance = ZDOMan.instance;
			if (uid == 0L || instance == null)
			{
				return null;
			}
			if (instance != s_zdoMan)
			{
				s_zdoMan = instance;
				s_found.Clear();
				s_misses.Clear();
			}
			float value = Plugin.SearchRadius.Value;
			(long, Vector2s) key = (uid, ZoneSystem.GetZone(near));
			if (s_found.TryGetValue(key, out var value2))
			{
				if (Matches(value2, uid) && InRange(value2, near, value))
				{
					return value2;
				}
				s_found.Remove(key);
			}
			s_misses.TryGetValue(key, out var value3);
			if (Time.time < value3.RetryAt)
			{
				return null;
			}
			ZDO val = Scan(instance, uid, near, value);
			if (val != null)
			{
				s_found[key] = val;
				s_misses.Remove(key);
			}
			else
			{
				float num = ((value3.Delay <= 0f) ? 1f : Mathf.Min(value3.Delay * 2f, 10f));
				s_misses[key] = new Miss
				{
					RetryAt = Time.time + num,
					Delay = num
				};
			}
			return val;
		}

		public static bool IsContainer(ZDO zdo)
		{
			int prefab = zdo.GetPrefab();
			if (s_containerPrefabs.TryGetValue(prefab, out var value))
			{
				return value;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			if ((Object)(object)prefab2 == (Object)null)
			{
				return false;
			}
			value = (Object)(object)prefab2.GetComponent<Container>() != (Object)null;
			s_containerPrefabs[prefab] = value;
			return value;
		}

		private static bool Matches(ZDO zdo, long uid)
		{
			if (zdo.IsValid() && IsContainer(zdo))
			{
				return zdo.GetLong(ChestUid.UidKey, 0L) == uid;
			}
			return false;
		}

		private static bool InRange(ZDO zdo, Vector3 near, float radius)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return HorizontalDistanceSqr(zdo.GetPosition(), near) <= radius * radius;
		}

		public static float HorizontalDistanceSqr(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return num * num + num2 * num2;
		}

		public static float HorizontalDistance(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Sqrt(HorizontalDistanceSqr(a, b));
		}

		private static ZDO Scan(ZDOMan zdoMan, long uid, Vector3 near, float radius)
		{
			//IL_0013: 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_0019: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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)
			int num = Mathf.CeilToInt(radius / 64f + 0.5f);
			Vector2s zone = ZoneSystem.GetZone(near);
			s_buffer.Clear();
			s_visited.Clear();
			for (int i = -num; i <= num; i++)
			{
				for (int j = -num; j <= num; j++)
				{
					s_findObjects(zdoMan, new Vector2s(zone.x + i, zone.y + j), s_buffer, s_visited);
				}
			}
			ZDO result = null;
			float num2 = radius * radius;
			foreach (ZDO item in s_buffer)
			{
				if (item != null && Matches(item, uid))
				{
					float num3 = HorizontalDistanceSqr(item.GetPosition(), near);
					if (num3 <= num2)
					{
						result = item;
						num2 = num3;
					}
				}
			}
			s_buffer.Clear();
			return result;
		}
	}
	internal static class ChestPatches
	{
		private delegate bool PeerAreaFn(ZDOMan self, Vector3 point, long uid);

		private static PeerAreaFn s_isInPeerActiveArea;

		public static bool Init()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ZDOMan), "IsInPeerActiveArea", new Type[2]
			{
				typeof(Vector3),
				typeof(long)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			s_isInPeerActiveArea = (PeerAreaFn)Delegate.CreateDelegate(typeof(PeerAreaFn), methodInfo, throwOnBindFailure: false);
			return s_isInPeerActiveArea != null;
		}

		public static bool TakePrefix(ZDO zdo, string prefabName, int want, ref int __result)
		{
			__result = 0;
			try
			{
				if (want <= 0 || zdo == null || !zdo.IsValid() || !ChestFinder.IsContainer(zdo))
				{
					return false;
				}
				if (zdo.IsOwner() || ChestAccess.TryClaimUnowned(zdo))
				{
					__result = ChestAccess.TakeLocal(zdo, prefabName, want);
				}
				else if (ChestAccess.HasLiveOwner(zdo))
				{
					ChestRpc.RequestOwnership(zdo);
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("TakePrefix", e);
				__result = 0;
			}
			return false;
		}

		public static bool PutPrefix(ZDO zdo, string prefabName, int amount, ref bool __result)
		{
			__result = false;
			try
			{
				if (amount <= 0)
				{
					__result = true;
					return false;
				}
				if (zdo == null || !zdo.IsValid() || !ChestFinder.IsContainer(zdo))
				{
					return false;
				}
				if (zdo.IsOwner() || ChestAccess.TryClaimUnowned(zdo))
				{
					__result = ChestAccess.PutLocal(zdo, prefabName, amount);
				}
				else if (ChestAccess.HasLiveOwner(zdo) && ChestAccess.Fits(zdo, prefabName, amount))
				{
					ChestRpc.SendPut(zdo, prefabName, amount);
					__result = true;
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("PutPrefix", e);
				__result = false;
			}
			return false;
		}

		public static void SetInUsePrefix(Container __instance, bool inUse)
		{
			try
			{
				if (inUse && !__instance.IsInUse() && __instance.IsOwner())
				{
					ChestAccess.Refresh(__instance);
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("SetInUsePrefix", e);
			}
		}

		public static void StackResponsePrefix(Container __instance, bool granted)
		{
			try
			{
				if (granted)
				{
					ChestAccess.Refresh(__instance);
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("StackResponsePrefix", e);
			}
		}

		public static bool GuardIntPrefix(ZDO zdo, ref int __result)
		{
			if (!HandledByOtherPeer(zdo))
			{
				return true;
			}
			__result = 0;
			return false;
		}

		public static bool GuardBoolPrefix(ZDO zdo, ref bool __result)
		{
			if (!HandledByOtherPeer(zdo))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool HandledByOtherPeer(ZDO smelter)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return smelter != null && smelter.HasOwner() && !smelter.IsOwner() && s_isInPeerActiveArea(ZDOMan.instance, smelter.GetPosition(), smelter.GetOwner());
			}
			catch (Exception e)
			{
				ErrorLog.Once("HandledByOtherPeer", e);
				return false;
			}
		}
	}
	internal static class ChestRpc
	{
		private struct PendingPut
		{
			public string Prefab;

			public int Amount;

			public Vector3 DropPosition;

			public long Owner;

			public float Deadline;
		}

		private const string PutRpc = "SLFix_Put";

		private const string PutAckRpc = "SLFix_PutAck";

		private const string OwnerRpc = "SLFix_RequestChestOwner";

		private const float PutTimeoutSeconds = 30f;

		private const float OwnerRequestCooldown = 3f;

		private const int MaxPutAmount = 100000;

		private static readonly Dictionary<long, PendingPut> s_pendingPuts = new Dictionary<long, PendingPut>();

		private static readonly Dictionary<ZDOID, float> s_ownerRequestAt = new Dictionary<ZDOID, float>();

		private static long s_nextRequestId = 1L;

		public static void Register()
		{
			FlushPending("world closed");
			s_ownerRequestAt.Clear();
			if (ZRoutedRpc.instance == null)
			{
				return;
			}
			try
			{
				ZRoutedRpc.instance.Register<ZDOID, string, int, Vector3, long>("SLFix_Put", (Method<ZDOID, string, int, Vector3, long>)RPC_Put);
				ZRoutedRpc.instance.Register<long>("SLFix_PutAck", (Action<long, long>)RPC_PutAck);
				ZRoutedRpc.instance.Register<ZDOID>("SLFix_RequestChestOwner", (Action<long, ZDOID>)RPC_RequestOwner);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Chest RPC registration failed: {arg}");
			}
		}

		public static void SendPut(ZDO chest, string prefab, int amount)
		{
			//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)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			long owner = chest.GetOwner();
			long num = s_nextRequestId++;
			Vector3 position = chest.GetPosition();
			s_pendingPuts[num] = new PendingPut
			{
				Prefab = prefab,
				Amount = amount,
				DropPosition = position,
				Owner = owner,
				Deadline = Time.time + 30f
			};
			ZRoutedRpc.instance.InvokeRoutedRPC(owner, "SLFix_Put", new object[5] { chest.m_uid, prefab, amount, position, num });
		}

		public static void RequestOwnership(ZDO chest)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (!s_ownerRequestAt.TryGetValue(chest.m_uid, out var value) || !(Time.time < value))
			{
				s_ownerRequestAt[chest.m_uid] = Time.time + 3f;
				ZRoutedRpc.instance.InvokeRoutedRPC(chest.GetOwner(), "SLFix_RequestChestOwner", new object[1] { chest.m_uid });
			}
		}

		public static void CheckTimeouts()
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if (s_pendingPuts.Count == 0 || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			List<long> list = null;
			foreach (KeyValuePair<long, PendingPut> s_pendingPut in s_pendingPuts)
			{
				if (Time.time > s_pendingPut.Value.Deadline)
				{
					(list ?? (list = new List<long>())).Add(s_pendingPut.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (long item in list)
			{
				PendingPut pendingPut = s_pendingPuts[item];
				s_pendingPuts.Remove(item);
				Plugin.Log.LogWarning((object)string.Format("{0} #{1} not confirmed by {2}, dropping items instead", "SLFix_Put", item, pendingPut.Owner));
				ChestAccess.Drop(pendingPut.Prefab, pendingPut.Amount, pendingPut.DropPosition);
			}
		}

		public static void ShutdownPrefix()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					foreach (PendingPut value in s_pendingPuts.Values)
					{
						ChestAccess.Drop(value.Prefab, value.Amount, value.DropPosition);
					}
					s_pendingPuts.Clear();
				}
				FlushPending("world closed");
			}
			catch (Exception e)
			{
				ErrorLog.Once("ShutdownPrefix", e);
			}
		}

		private static void FlushPending(string reason)
		{
			foreach (KeyValuePair<long, PendingPut> s_pendingPut in s_pendingPuts)
			{
				Plugin.Log.LogWarning((object)string.Format("{0} #{1} ({2}x {3}) unconfirmed ({4})", "SLFix_Put", s_pendingPut.Key, s_pendingPut.Value.Amount, s_pendingPut.Value.Prefab, reason));
			}
			s_pendingPuts.Clear();
		}

		private static void RPC_Put(long sender, ZDOID chestId, string prefab, int amount, Vector3 dropPosition, long id)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				if (amount <= 0 || amount > 100000)
				{
					Plugin.Log.LogWarning((object)string.Format("{0} #{1} from {2}: invalid amount {3}, ignored", "SLFix_Put", id, sender, amount));
					flag = true;
				}
				else
				{
					ZDO zDO = ZDOMan.instance.GetZDO(chestId);
					if (zDO != null && zDO.IsOwner() && ChestFinder.IsContainer(zDO) && ChestAccess.PutLocal(zDO, prefab, amount))
					{
						flag = true;
					}
					else
					{
						flag = true;
						ChestAccess.Drop(prefab, amount, (zDO != null) ? zDO.GetPosition() : dropPosition);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)string.Format("{0} #{1} failed: {2}", "SLFix_Put", id, arg));
				if (!flag)
				{
					try
					{
						ChestAccess.Drop(prefab, amount, dropPosition);
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)string.Format("{0} #{1} drop failed, {2}x {3} lost: {4}", "SLFix_Put", id, amount, prefab, ex));
					}
				}
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "SLFix_PutAck", new object[1] { id });
		}

		private static void RPC_PutAck(long sender, long id)
		{
			if (s_pendingPuts.TryGetValue(id, out var value) && value.Owner == sender)
			{
				s_pendingPuts.Remove(id);
			}
		}

		private static void RPC_RequestOwner(long sender, ZDOID chestId)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ZDO zDO = ZDOMan.instance.GetZDO(chestId);
				if (zDO != null && zDO.IsOwner() && !ChestAccess.IsInUse(zDO, ChestAccess.Live(zDO)))
				{
					ZDOMan.instance.ForceSendZDO(sender, chestId);
					zDO.SetOwner(sender);
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("SLFix_RequestChestOwner", e);
			}
		}
	}
	internal static class ChestUid
	{
		public static readonly int UidKey = StringExtensionMethods.GetStableHashCode("slfix_uid");

		private static readonly int[] s_slotKeys = new int[3]
		{
			StringExtensionMethods.GetStableHashCode("slfix_in"),
			StringExtensionMethods.GetStableHashCode("slfix_fuel"),
			StringExtensionMethods.GetStableHashCode("slfix_out")
		};

		public static int SlotKey(int slot)
		{
			return s_slotKeys[slot];
		}

		public static int SlotIndex(object slot)
		{
			int num = Convert.ToInt32(slot);
			if (num < 0 || num >= s_slotKeys.Length)
			{
				throw new ArgumentOutOfRangeException("slot", num, "Unknown SmelterLink slot");
			}
			return num;
		}

		public static long NewUid()
		{
			long num;
			do
			{
				num = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0);
			}
			while (num == 0L);
			return num;
		}
	}
	internal static class ErrorLog
	{
		private static readonly HashSet<string> s_logged = new HashSet<string>();

		public static void Once(string where, Exception e)
		{
			if (s_logged.Add(where))
			{
				Plugin.Log.LogError((object)$"{where} failed: {e}");
			}
		}
	}
	internal static class InputBlockPatches
	{
		public static bool UseHotbarItemPrefix(Player __instance)
		{
			return !IsLinking(__instance);
		}

		public static bool StartEmotePrefix(Player __instance, string emote, ref bool __result)
		{
			if (emote != "sit" || !IsLinking(__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}

		public static bool MenuShowPrefix()
		{
			try
			{
				return !ZInput.GetKeyDown((KeyCode)27, true) || !LinkModeAccess.SelectingNowOrLastFrame;
			}
			catch (Exception e)
			{
				ErrorLog.Once("MenuShowPrefix", e);
				return true;
			}
		}

		public static void TrackSelecting()
		{
			try
			{
				LinkModeAccess.LateUpdate();
			}
			catch (Exception e)
			{
				ErrorLog.Once("TrackSelecting", e);
			}
		}

		public static bool IsLinking(Player player)
		{
			try
			{
				return (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && LinkModeAccess.Selecting;
			}
			catch (Exception e)
			{
				ErrorLog.Once("IsLinking", e);
				return false;
			}
		}
	}
	internal static class LinkModeAccess
	{
		private static Func<bool> s_selecting;

		private static Action s_cancel;

		private static FieldInfo s_smelterField;

		private static int s_lastSelectingFrame = -1;

		public static bool CanCancel => s_cancel != null;

		public static bool CanGetSmelter => s_smelterField != null;

		public static bool Selecting => s_selecting();

		public static ZDO Smelter
		{
			get
			{
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (s_smelterField == null || !Selecting || ZDOMan.instance == null)
				{
					return null;
				}
				ZDO zDO = ZDOMan.instance.GetZDO((ZDOID)s_smelterField.GetValue(null));
				if (zDO == null || !zDO.IsValid())
				{
					return null;
				}
				return zDO;
			}
		}

		public static bool SelectingNowOrLastFrame
		{
			get
			{
				if (!Selecting)
				{
					return s_lastSelectingFrame == Time.frameCount - 1;
				}
				return true;
			}
		}

		public static bool Init()
		{
			Type type = AccessTools.TypeByName("SmelterLink.LinkMode");
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.PropertyGetter(type, "Selecting");
			if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
			{
				return false;
			}
			s_selecting = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo);
			MethodInfo methodInfo2 = AccessTools.Method(type, "Cancel", Type.EmptyTypes, (Type[])null);
			if (methodInfo2 != null && methodInfo2.IsStatic && methodInfo2.ReturnType == typeof(void))
			{
				s_cancel = (Action)Delegate.CreateDelegate(typeof(Action), methodInfo2);
			}
			FieldInfo fieldInfo = AccessTools.Field(type, "_smelter");
			if (fieldInfo != null && fieldInfo.IsStatic && fieldInfo.FieldType == typeof(ZDOID))
			{
				s_smelterField = fieldInfo;
			}
			return true;
		}

		public static void Cancel()
		{
			s_cancel();
		}

		public static void LateUpdate()
		{
			if (Selecting)
			{
				s_lastSelectingFrame = Time.frameCount;
			}
		}
	}
	internal static class LinkPatches
	{
		private static readonly KeyValuePair<int, int>[] s_legacyKeys = new KeyValuePair<int, int>[3]
		{
			ZDO.GetHashZDOID("slink_in"),
			ZDO.GetHashZDOID("slink_fuel"),
			ZDO.GetHashZDOID("slink_out")
		};

		private static Action<ZDO> s_claimSmelter;

		private static Action<ZDO> s_trackSmelter;

		public static bool Init(Type links, Type registry)
		{
			s_claimSmelter = StaticAction(links, "Claim");
			s_trackSmelter = StaticAction(registry, "Track");
			if (s_claimSmelter != null)
			{
				return s_trackSmelter != null;
			}
			return false;
		}

		private static Action<ZDO> StaticAction(Type type, string name)
		{
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, name, new Type[1] { typeof(ZDO) }, (Type[])null));
			if (!(methodInfo == null))
			{
				return (Action<ZDO>)Delegate.CreateDelegate(typeof(Action<ZDO>), methodInfo, throwOnBindFailure: false);
			}
			return null;
		}

		public static bool GetPrefix(ZDO zdo, object slot, ref ZDOID __result)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				__result = FindTarget(zdo, slot)?.m_uid ?? ZDOID.None;
			}
			catch (Exception e)
			{
				ErrorLog.Once("GetPrefix", e);
				__result = ZDOID.None;
			}
			return false;
		}

		public static bool ResolvePrefix(ZDO smelter, object slot, ref ZDO __result)
		{
			try
			{
				__result = FindTarget(smelter, slot);
			}
			catch (Exception e)
			{
				ErrorLog.Once("ResolvePrefix", e);
				__result = null;
			}
			return false;
		}

		public static bool HasAnyPrefix(ZDO zdo, ref bool __result)
		{
			try
			{
				__result = zdo != null && (zdo.GetLong(ChestUid.SlotKey(0), 0L) != 0L || zdo.GetLong(ChestUid.SlotKey(1), 0L) != 0L || zdo.GetLong(ChestUid.SlotKey(2), 0L) != 0);
			}
			catch (Exception e)
			{
				ErrorLog.Once("HasAnyPrefix", e);
				__result = false;
			}
			return false;
		}

		public static bool SetPrefix(ZDO zdo, object slot, ZDOID target)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (zdo == null)
				{
					return false;
				}
				int slot2 = ChestUid.SlotIndex(slot);
				UidRpc.CancelPending(zdo.m_uid, slot2);
				if (((ZDOID)(ref target)).IsNone())
				{
					WriteLink(zdo, slot2, 0L);
					return false;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(target);
				if (zDO == null || !ChestFinder.IsContainer(zDO))
				{
					Notify.Center("Link failed: chest not found");
					return false;
				}
				float num = ChestFinder.HorizontalDistance(zdo.GetPosition(), zDO.GetPosition());
				if (num > Plugin.SearchRadius.Value)
				{
					Notify.Center($"Chest is {num:0}m away, more than SearchRadius ({Plugin.SearchRadius.Value:0}m): link saved but not used");
				}
				long num2 = zDO.GetLong(ChestUid.UidKey, 0L);
				if (num2 != 0L)
				{
					WriteLink(zdo, slot2, num2);
				}
				else if (zDO.IsOwner())
				{
					num2 = ChestUid.NewUid();
					zDO.Set(ChestUid.UidKey, num2);
					WriteLink(zdo, slot2, num2);
				}
				else
				{
					UidRpc.Request(zdo.m_uid, slot2, zDO);
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("SetPrefix", e);
			}
			return false;
		}

		public static void WriteLink(ZDO smelter, int slot, long uid)
		{
			s_claimSmelter(smelter);
			smelter.Set(ChestUid.SlotKey(slot), uid);
			ClearLegacyLinks(smelter);
			if (uid != 0L)
			{
				s_trackSmelter(smelter);
			}
		}

		private static void ClearLegacyLinks(ZDO smelter)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			KeyValuePair<int, int>[] array = s_legacyKeys;
			foreach (KeyValuePair<int, int> keyValuePair in array)
			{
				ZDOID zDOID = smelter.GetZDOID(keyValuePair);
				if (!((ZDOID)(ref zDOID)).IsNone())
				{
					smelter.Set(keyValuePair, ZDOID.None);
				}
			}
		}

		private static ZDO FindTarget(ZDO smelter, object slot)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (smelter == null)
			{
				return null;
			}
			return ChestFinder.Find(smelter.GetLong(ChestUid.SlotKey(ChestUid.SlotIndex(slot)), 0L), smelter.GetPosition());
		}
	}
	internal static class Notify
	{
		private static string s_pending;

		private static int s_showFrame;

		public static void Center(string text)
		{
			Plugin.Log.LogInfo((object)text);
			s_pending = text;
			s_showFrame = Time.frameCount + 1;
		}

		public static void Update()
		{
			if (s_pending != null && Time.frameCount >= s_showFrame)
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, s_pending, 0, (Sprite)null, false, true);
				}
				s_pending = null;
			}
		}
	}
	[BepInPlugin("radek.smelterlinkfix", "SmelterLinkFix", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "radek.smelterlinkfix";

		public const string PluginName = "SmelterLinkFix";

		public const string PluginVersion = "0.1.0";

		private const string TargetGuid = "nishinku.smelterlink";

		private const string TargetVersion = "0.2.0";

		private Harmony _harmony;

		private bool _active;

		private bool _updateErrorLogged;

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<float> SearchRadius { get; private set; }

		internal static ConfigEntry<bool> BlockActions { get; private set; }

		internal static ConfigEntry<bool> CancelOnDamage { get; private set; }

		internal static ConfigEntry<bool> ShowRange { get; private set; }

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Links", "SearchRadius", 64f, new ConfigDescription("How far from a smelter (horizontal distance in meters) linked chests are searched for. Chests further away stay linked but are not used", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 256f), Array.Empty<object>()));
			BlockActions = ((BaseUnityPlugin)this).Config.Bind<bool>("LinkMode", "BlockActionsWhileLinking", true, "While linking you can only move (walk, run, jump, crouch, dodge, look). Off: only SmelterLink keys (1/2/3, X, Esc) are blocked from triggering game actions");
			CancelOnDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("LinkMode", "CancelOnDamage", true, "Getting hit by a creature, player or turret cancels linking, so you can fight back. Poison, fire, frost and drowning do not");
			ShowRange = ((BaseUnityPlugin)this).Config.Bind<bool>("LinkMode", "ShowRange", true, "While linking, draw the link range (Links.SearchRadius) around the smelter and show chest distance in its hover text");
			if (!IsTargetSupported(out var version))
			{
				return;
			}
			Type type = AccessTools.TypeByName("SmelterLink.Links");
			Type registry = AccessTools.TypeByName("SmelterLink.Registry");
			Type type2 = AccessTools.TypeByName("SmelterLink.ContainerIO");
			Type type3 = AccessTools.TypeByName("SmelterLink.SmelterOps");
			if (!LinkModeAccess.Init() || type == null || !LinkPatches.Init(type, registry) || !ChestFinder.Init() || type2 == null || type3 == null || !ChestPatches.Init() || !ChestAccess.Init())
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"SmelterLink or Valheim internals not found, patches disabled");
				return;
			}
			_harmony = new Harmony("radek.smelterlinkfix");
			try
			{
				Prefix(AccessTools.Method(typeof(Player), "UseHotbarItem", (Type[])null, (Type[])null), typeof(InputBlockPatches), "UseHotbarItemPrefix");
				Prefix(AccessTools.Method(typeof(Player), "StartEmote", (Type[])null, (Type[])null), typeof(InputBlockPatches), "StartEmotePrefix");
				Prefix(AccessTools.Method(typeof(Menu), "Show", (Type[])null, (Type[])null), typeof(InputBlockPatches), "MenuShowPrefix");
				Prefix(AccessTools.Method(type, "Get", (Type[])null, (Type[])null), typeof(LinkPatches), "GetPrefix");
				Prefix(AccessTools.Method(type, "Set", (Type[])null, (Type[])null), typeof(LinkPatches), "SetPrefix");
				Prefix(AccessTools.Method(type, "Resolve", (Type[])null, (Type[])null), typeof(LinkPatches), "ResolvePrefix");
				Prefix(AccessTools.Method(type, "HasAny", (Type[])null, (Type[])null), typeof(LinkPatches), "HasAnyPrefix");
				Postfix(AccessTools.Method(typeof(ZNet), "Awake", (Type[])null, (Type[])null), typeof(UidRpc), "Register");
				Prefix(AccessTools.Method(type2, "Take", (Type[])null, (Type[])null), typeof(ChestPatches), "TakePrefix");
				Prefix(AccessTools.Method(type2, "Put", (Type[])null, (Type[])null), typeof(ChestPatches), "PutPrefix");
				Prefix(AccessTools.Method(type3, "FeedFuel", (Type[])null, (Type[])null), typeof(ChestPatches), "GuardIntPrefix");
				Prefix(AccessTools.Method(type3, "FeedOre", (Type[])null, (Type[])null), typeof(ChestPatches), "GuardIntPrefix");
				Prefix(AccessTools.Method(type3, "Simulate", (Type[])null, (Type[])null), typeof(ChestPatches), "GuardIntPrefix");
				Prefix(AccessTools.Method(type3, "FlushPendingOutput", (Type[])null, (Type[])null), typeof(ChestPatches), "GuardBoolPrefix");
				Postfix(AccessTools.Method(typeof(ZNet), "Awake", (Type[])null, (Type[])null), typeof(ChestRpc), "Register");
				Prefix(AccessTools.Method(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), typeof(ChestRpc), "ShutdownPrefix");
				Prefix(AccessTools.Method(typeof(Container), "SetInUse", (Type[])null, (Type[])null), typeof(ChestPatches), "SetInUsePrefix");
				Prefix(AccessTools.Method(typeof(Container), "RPC_StackResponse", (Type[])null, (Type[])null), typeof(ChestPatches), "StackResponsePrefix");
				Prefix(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), typeof(ActionBlockPatches), "TakeInputPrefix");
				Prefix(AccessTools.Method(typeof(Player), "SetControls", (Type[])null, (Type[])null), typeof(ActionBlockPatches), "SetControlsPrefix");
				Prefix(AccessTools.Method(typeof(InventoryGui), "Show", (Type[])null, (Type[])null), typeof(ActionBlockPatches), "InventoryShowPrefix");
				Prefix(AccessTools.Method(typeof(Minimap), "SetMapMode", (Type[])null, (Type[])null), typeof(ActionBlockPatches), "SetMapModePrefix");
				if (LinkModeAccess.CanCancel)
				{
					MethodInfo original = AccessTools.Method(typeof(Character), "ApplyDamage", (Type[])null, (Type[])null);
					Prefix(original, typeof(ActionBlockPatches), "ApplyDamagePrefix");
					Postfix(original, typeof(ActionBlockPatches), "ApplyDamagePostfix");
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"SmelterLink.LinkMode.Cancel not found, CancelOnDamage disabled");
				}
				if (!LinkModeAccess.CanGetSmelter)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"SmelterLink.LinkMode._smelter not found, link range display disabled");
				}
				Postfix(AccessTools.Method(typeof(Container), "GetHoverText", (Type[])null, (Type[])null), typeof(RangeDisplay), "ContainerHoverPostfix", 200);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Patching failed, patches disabled: {arg}");
				_harmony.UnpatchSelf();
				return;
			}
			_active = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("SmelterLinkFix 0.1.0 loaded (SmelterLink " + version + ", Valheim " + Version.GetVersionString(false) + ")"));
		}

		private void Update()
		{
			if (!_active)
			{
				return;
			}
			try
			{
				UidRpc.CheckTimeouts();
				ChestRpc.CheckTimeouts();
				Notify.Update();
			}
			catch (Exception arg)
			{
				if (!_updateErrorLogged)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Update failed: {arg}");
				}
				_updateErrorLogged = true;
			}
		}

		private void LateUpdate()
		{
			if (!_active)
			{
				return;
			}
			InputBlockPatches.TrackSelecting();
			try
			{
				RangeDisplay.LateUpdate();
			}
			catch (Exception e)
			{
				ErrorLog.Once("RangeDisplay", e);
			}
		}

		private void Prefix(MethodInfo original, Type patchClass, string patch)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			if (original == null)
			{
				throw new MissingMethodException("Target method for " + patch + " not found");
			}
			_harmony.Patch((MethodBase)original, new HarmonyMethod(patchClass, patch, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private void Postfix(MethodInfo original, Type patchClass, string patch, int priority = 400)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if (original == null)
			{
				throw new MissingMethodException("Target method for " + patch + " not found");
			}
			_harmony.Patch((MethodBase)original, (HarmonyMethod)null, new HarmonyMethod(patchClass, patch, (Type[])null)
			{
				priority = priority
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private bool IsTargetSupported(out string version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("nishinku.smelterlink", out var value))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"SmelterLink (nishinku.smelterlink) not found, patches disabled");
				return false;
			}
			version = value.Metadata.Version.ToString();
			if (version != "0.2.0")
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("SmelterLink " + version + " is not supported (expected 0.2.0), patches disabled"));
				return false;
			}
			return true;
		}
	}
	internal static class RangeDisplay
	{
		private const int Segments = 96;

		private const float HeightOffset = 0.3f;

		private const float Width = 0.15f;

		private const float IncompleteRetrySeconds = 2f;

		private static readonly Color RingColor = new Color(1f, 0.85f, 0.3f, 0.8f);

		private static LineRenderer s_ring;

		private static Material s_material;

		private static bool s_shaderMissing;

		private static ZDOID s_builtFor = ZDOID.None;

		private static Vector3 s_builtCenter;

		private static float s_builtRadius;

		private static bool s_incomplete;

		private static float s_retryAt;

		public static void LateUpdate()
		{
			//IL_0058: 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_006a: 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_007b: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = (Plugin.ShowRange.Value ? LinkModeAccess.Smelter : null);
			if (val == null)
			{
				if ((Object)(object)s_ring != (Object)null && ((Component)s_ring).gameObject.activeSelf)
				{
					((Component)s_ring).gameObject.SetActive(false);
				}
				return;
			}
			LineRenderer ring = GetRing();
			if (!((Object)(object)ring == (Object)null))
			{
				Vector3 position = val.GetPosition();
				float value = Plugin.SearchRadius.Value;
				if (val.m_uid != s_builtFor || position != s_builtCenter || value != s_builtRadius || (s_incomplete && Time.time >= s_retryAt))
				{
					s_incomplete = !Build(ring, position, value);
					s_retryAt = Time.time + 2f;
					s_builtFor = val.m_uid;
					s_builtCenter = position;
					s_builtRadius = value;
				}
				if (!((Component)ring).gameObject.activeSelf)
				{
					((Component)ring).gameObject.SetActive(true);
				}
			}
		}

		public static void ContainerHoverPostfix(Container __instance, ref string __result)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Plugin.ShowRange.Value && !((Object)(object)__instance == (Object)null))
				{
					ZDO smelter = LinkModeAccess.Smelter;
					if (smelter != null)
					{
						float num = ChestFinder.HorizontalDistance(smelter.GetPosition(), ((Component)__instance).transform.position);
						float value = Plugin.SearchRadius.Value;
						__result += ((num <= value) ? $"\n<color=#9BE39B>{num:0}m / {value:0}m</color>" : $"\n<color=#FF6B6B>{num:0}m / {value:0}m, out of range</color>");
					}
				}
			}
			catch (Exception e)
			{
				ErrorLog.Once("ContainerHoverPostfix", e);
			}
		}

		private static LineRenderer GetRing()
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//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_0067: Expected O, but got Unknown
			if ((Object)(object)s_ring != (Object)null)
			{
				return s_ring;
			}
			if (s_shaderMissing)
			{
				return null;
			}
			if ((Object)(object)s_material == (Object)null)
			{
				Shader val = Shader.Find("Sprites/Default");
				if ((Object)(object)val == (Object)null)
				{
					s_shaderMissing = true;
					Plugin.Log.LogWarning((object)"Shader Sprites/Default not found, link range ring disabled");
					return null;
				}
				s_material = new Material(val)
				{
					hideFlags = (HideFlags)32
				};
			}
			s_ring = new GameObject("SmelterLinkFix_Range").AddComponent<LineRenderer>();
			((Renderer)s_ring).sharedMaterial = s_material;
			s_ring.useWorldSpace = true;
			s_ring.loop = true;
			s_ring.widthMultiplier = 0.15f;
			LineRenderer obj = s_ring;
			Color startColor = (s_ring.endColor = RingColor);
			obj.startColor = startColor;
			((Renderer)s_ring).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)s_ring).receiveShadows = false;
			s_builtFor = ZDOID.None;
			return s_ring;
		}

		private static bool Build(LineRenderer ring, Vector3 center, float radius)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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)
			bool result = true;
			Vector3[] array = (Vector3[])(object)new Vector3[96];
			Vector3 val = default(Vector3);
			float y = default(float);
			for (int i = 0; i < 96; i++)
			{
				float num = (float)i * (float)Math.PI * 2f / 96f;
				((Vector3)(ref val))..ctor(center.x + Mathf.Cos(num) * radius, center.y, center.z + Mathf.Sin(num) * radius);
				if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val, ref y))
				{
					val.y = y;
				}
				else
				{
					result = false;
				}
				val.y += 0.3f;
				array[i] = val;
			}
			ring.positionCount = 96;
			ring.SetPositions(array);
			return result;
		}
	}
	internal static class UidRpc
	{
		private struct Pending
		{
			public ZDOID Smelter;

			public int Slot;

			public ZDOID Chest;

			public long Owner;

			public float Deadline;
		}

		private const string AssignRpc = "SLFix_AssignUid";

		private const string AckRpc = "SLFix_AssignUidAck";

		private const float TimeoutSeconds = 5f;

		private static readonly List<Pending> s_pending = new List<Pending>();

		public static void Register()
		{
			s_pending.Clear();
			if (ZRoutedRpc.instance == null)
			{
				return;
			}
			try
			{
				ZRoutedRpc.instance.Register<ZDOID>("SLFix_AssignUid", (Action<long, ZDOID>)RPC_Assign);
				ZRoutedRpc.instance.Register<ZDOID, long>("SLFix_AssignUidAck", (Action<long, ZDOID, long>)RPC_Ack);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"RPC registration failed, linking chests owned by other players will not work: {arg}");
			}
		}

		public static void Request(ZDOID smelter, int slot, ZDO chest)
		{
			//IL_0024: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			long owner = chest.GetOwner();
			if (owner == 0L)
			{
				Notify.Center("Link failed: chest has no owner, try again");
				return;
			}
			s_pending.Add(new Pending
			{
				Smelter = smelter,
				Slot = slot,
				Chest = chest.m_uid,
				Owner = owner,
				Deadline = Time.time + 5f
			});
			ZRoutedRpc.instance.InvokeRoutedRPC(owner, "SLFix_AssignUid", new object[1] { chest.m_uid });
		}

		public static void CancelPending(ZDOID smelter, int slot)
		{
			//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)
			s_pending.RemoveAll((Pending p) => p.Smelter == smelter && p.Slot == slot);
		}

		public static void CheckTimeouts()
		{
			if (s_pending.Count != 0 && s_pending.RemoveAll((Pending p) => Time.time > p.Deadline) > 0)
			{
				Notify.Center("Link failed: chest owner did not respond (does everyone have SmelterLinkFix?)");
			}
		}

		private static void RPC_Assign(long sender, ZDOID chestId)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			long num = 0L;
			try
			{
				ZDO zDO = ZDOMan.instance.GetZDO(chestId);
				if (zDO != null && zDO.IsOwner() && ChestFinder.IsContainer(zDO))
				{
					num = zDO.GetLong(ChestUid.UidKey, 0L);
					if (num == 0L)
					{
						num = ChestUid.NewUid();
						zDO.Set(ChestUid.UidKey, num);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)string.Format("{0} failed: {1}", "SLFix_AssignUid", arg));
				num = 0L;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "SLFix_AssignUidAck", new object[2] { chestId, num });
		}

		private static void RPC_Ack(long sender, ZDOID chestId, long uid)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				for (int num = s_pending.Count - 1; num >= 0; num--)
				{
					Pending pending = s_pending[num];
					if (!(pending.Chest != chestId) && pending.Owner == sender)
					{
						s_pending.RemoveAt(num);
						ZDO zDO = ZDOMan.instance.GetZDO(pending.Smelter);
						if (uid == 0L || zDO == null || !zDO.IsValid())
						{
							Notify.Center("Link failed, try again");
						}
						else
						{
							LinkPatches.WriteLink(zDO, pending.Slot, uid);
						}
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)string.Format("{0} failed: {1}", "SLFix_AssignUidAck", arg));
			}
		}
	}
}