Decompiled source of DetectStorage v0.1.0

BepInEx\plugins\Detect\Detect.dll

Decompiled 5 days 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.Configuration;
using Detect.Net;
using Detect.Pieces;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
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: AssemblyVersion("0.0.0.0")]
namespace Detect
{
	[BepInPlugin("saad.detect", "Detect Storage", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "saad.detect";

		public const string ModName = "Detect Storage";

		public const string ModVersion = "0.1.0";

		public const string DetectCategoryName = "Detect";

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ConfigEntry<float> HubScanRadius { get; private set; }

		public static ConfigEntry<float> SortButtonCooldownSeconds { get; private set; }

		public static ConfigEntry<bool> HubShowRadiusRing { get; private set; }

		private void Awake()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			Instance = this;
			HubScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Hub", "ScanRadiusMeters", 20f, "Radius in meters used by Detect Hub to find Detect Chests.");
			SortButtonCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Hub", "SortButtonCooldownSeconds", 0.5f, "Client-side cooldown for Sort button clicks.");
			HubShowRadiusRing = ((BaseUnityPlugin)this).Config.Bind<bool>("Hub", "ShowRadiusRing", true, "Show a visible radius ring around Detect Hubs.");
			_harmony = new Harmony("saad.detect");
			_harmony.PatchAll();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterPieces;
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterPieces;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void RegisterPieces()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterPieces;
			PieceManager.Instance.AddPieceCategory("Detect");
			DetectHub.RegisterPiece();
			DetectChest.RegisterPiece();
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)"Detect Storage: pieces and category registered.");
		}
	}
}
namespace Detect.UI
{
	[HarmonyPatch]
	public static class ChestFilterPatch
	{
		private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer") ?? AccessTools.Field(typeof(InventoryGui), "m_container");

		private static bool _filterSelectionMode;

		private static DetectChest _activeChest;

		[HarmonyPatch(typeof(InventoryGui), "Update")]
		[HarmonyPostfix]
		private static void InventoryGui_Update_Postfix(InventoryGui __instance)
		{
			DetectChest detectChest = DetectChest.FromContainer(GetOpenContainer(__instance));
			if ((Object)(object)detectChest == (Object)null && _filterSelectionMode)
			{
				DisableFilterMode();
			}
			if (IsFilterKeyPressed() && !((Object)(object)detectChest == (Object)null))
			{
				if (_filterSelectionMode && _activeChest == detectChest)
				{
					DisableFilterMode();
					Logger.LogInfo((object)"Detect Chest: filter selection mode disabled.");
					ShowCenterMessage("Detect filter mode OFF");
				}
				else
				{
					_filterSelectionMode = true;
					_activeChest = detectChest;
					Logger.LogInfo((object)"Detect Chest: filter selection mode enabled. Click an item to set filter, click empty slot to clear.");
					ShowCenterMessage("Detect filter mode ON - click item/empty");
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "Hide")]
		[HarmonyPostfix]
		private static void InventoryGui_Hide_Postfix()
		{
			DisableFilterMode();
		}

		[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
		[HarmonyPostfix]
		private static void InventoryGui_OnSelectedItem_Postfix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod)
		{
			ApplyFilterFromPickedItem(__instance, item);
		}

		[HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")]
		[HarmonyPostfix]
		private static void InventoryGui_OnRightClickItem_Postfix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos)
		{
			ApplyFilterFromPickedItem(__instance, item);
		}

		private static void ApplyFilterFromPickedItem(InventoryGui gui, ItemData item)
		{
			if (_filterSelectionMode && !((Object)(object)_activeChest == (Object)null) && _activeChest.IsUsable())
			{
				if (DetectChest.FromContainer(GetOpenContainer(gui)) != _activeChest)
				{
					DisableFilterMode();
					return;
				}
				string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : (item?.m_shared?.m_name ?? string.Empty));
				_activeChest.RequestSetFilterFromClient(text);
				ShowCenterMessage(string.IsNullOrEmpty(text) ? "Detect filter cleared" : ("Detect filter set: " + item.m_shared.m_name));
				DisableFilterMode();
			}
		}

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

		private static void DisableFilterMode()
		{
			_filterSelectionMode = false;
			_activeChest = null;
		}

		private static bool IsFilterKeyPressed()
		{
			if (Input.GetKeyDown((KeyCode)102))
			{
				return true;
			}
			try
			{
				return ZInput.GetKeyDown((KeyCode)102, false);
			}
			catch
			{
				return false;
			}
		}

		private static void ShowCenterMessage(string message)
		{
			if (!string.IsNullOrEmpty(message) && !((Object)(object)MessageHud.instance == (Object)null))
			{
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch]
	public static class HubSortButtonPatch
	{
		private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer") ?? AccessTools.Field(typeof(InventoryGui), "m_container");

		private const KeyCode SortHotkey = 103;

		private static DetectHub _boundHub;

		private static float _nextAllowedSortTriggerTime;

		[HarmonyPatch(typeof(InventoryGui), "Hide")]
		[HarmonyPostfix]
		private static void InventoryGui_Hide_Postfix()
		{
			_boundHub = null;
		}

		[HarmonyPatch(typeof(InventoryGui), "Update")]
		[HarmonyPriority(0)]
		[HarmonyPostfix]
		private static void InventoryGui_Update_Postfix(InventoryGui __instance)
		{
			Container openContainer = GetOpenContainer(__instance);
			_boundHub = (((Object)(object)openContainer == (Object)null) ? null : (((Component)openContainer).GetComponent<DetectHub>() ?? ((Component)openContainer).GetComponentInParent<DetectHub>()));
			if (!((Object)(object)_boundHub == (Object)null) && IsSortHotkeyPressed() && !(Time.time < _nextAllowedSortTriggerTime))
			{
				_nextAllowedSortTriggerTime = Time.time + Mathf.Max(0.1f, Plugin.SortButtonCooldownSeconds?.Value ?? 0.5f);
				Logger.LogInfo((object)"Detect: hotkey sort triggered.");
				_boundHub.RequestSortFromClient();
				ShowCenterMessage("Detect: sorting...");
			}
		}

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

		private static bool IsSortHotkeyPressed()
		{
			if (Input.GetKeyDown((KeyCode)103))
			{
				return true;
			}
			try
			{
				return ZInput.GetKeyDown((KeyCode)103, false);
			}
			catch
			{
				return false;
			}
		}

		private static void ShowCenterMessage(string message)
		{
			if (!string.IsNullOrEmpty(message) && !((Object)(object)MessageHud.instance == (Object)null))
			{
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
	}
}
namespace Detect.Pieces
{
	public class DetectChest : MonoBehaviour
	{
		public const string PiecePrefabName = "piece_detect_chest";

		public const string FilterZdoKey = "detect_filter_item";

		private const string VanillaSourcePiece = "piece_chest_wood";

		private const string VanillaLargestChestPiece = "piece_chest_blackmetal";

		private static readonly HashSet<DetectChest> ActiveChests = new HashSet<DetectChest>();

		private static readonly MethodInfo InventoryChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);

		private static readonly MethodInfo ContainerSaveMethod = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);

		private static readonly MethodInfo AddItemByNameMethod = AccessTools.Method(typeof(Inventory), "AddItem", new Type[7]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(long),
			typeof(string),
			typeof(bool)
		}, (Type[])null);

		private static readonly MethodInfo AddItemWithPositionMethod = AccessTools.Method(typeof(Inventory), "AddItem", new Type[4]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int)
		}, (Type[])null);

		private static bool _registered;

		private Container _container;

		private ZNetView _nview;

		public Container Container => _container;

		public ZNetView NetView => _nview;

		private void Awake()
		{
			_container = ((Component)this).GetComponent<Container>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)_nview != (Object)null)
			{
				_nview.Register<string>("Detect_RequestFilterSet", (Action<long, string>)RPC_RequestSetFilter);
			}
		}

		private void OnEnable()
		{
			ActiveChests.Add(this);
		}

		private void OnDisable()
		{
			ActiveChests.Remove(this);
		}

		public static void RegisterPiece()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			if (!_registered)
			{
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("piece_detect_chest", "piece_chest_wood");
				if ((Object)(object)val == (Object)null)
				{
					Logger.LogError((object)"Detect: failed to clone prefab piece_chest_wood for Detect Chest.");
					return;
				}
				ConfigurePrefab(val);
				PieceConfig val2 = new PieceConfig();
				val2.Name = "Detect Chest";
				val2.Description = "Filtered storage for one item type.";
				val2.PieceTable = "_HammerPieceTable";
				val2.Category = "Detect";
				val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
				{
					new RequirementConfig("Wood", 5, 0, true)
				};
				PieceConfig val3 = val2;
				PieceManager.Instance.AddPiece(new CustomPiece(val, true, val3));
				_registered = true;
				Logger.LogInfo((object)"Detect Chest registered.");
			}
		}

		private static void ConfigurePrefab(GameObject prefab)
		{
			Piece component = prefab.GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_name = "Detect Chest";
				component.m_description = "Accepts one configured item type.";
				component.m_craftingStation = null;
			}
			Container component2 = prefab.GetComponent<Container>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_name = "Detect Chest";
				ApplyLargestVanillaChestSize(component2);
			}
			DetectChest detectChest = default(DetectChest);
			if (!prefab.TryGetComponent<DetectChest>(ref detectChest))
			{
				prefab.AddComponent<DetectChest>();
			}
			StationExtension[] componentsInChildren = prefab.GetComponentsInChildren<StationExtension>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			CraftingStation[] componentsInChildren2 = prefab.GetComponentsInChildren<CraftingStation>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
		}

		private static void ApplyLargestVanillaChestSize(Container detectContainer)
		{
			int width = 8;
			int height = 4;
			GameObject prefab = PrefabManager.Instance.GetPrefab("piece_chest_blackmetal");
			Container val = default(Container);
			if ((Object)(object)prefab != (Object)null && prefab.TryGetComponent<Container>(ref val))
			{
				width = val.m_width;
				height = val.m_height;
			}
			detectContainer.m_width = width;
			detectContainer.m_height = height;
		}

		public bool IsUsable()
		{
			if ((Object)(object)_container != (Object)null && (Object)(object)_nview != (Object)null)
			{
				return _nview.IsValid();
			}
			return false;
		}

		public string GetFilterPrefabName()
		{
			ZNetView nview = _nview;
			ZDO obj = ((nview != null) ? nview.GetZDO() : null);
			return ((obj != null) ? obj.GetString("detect_filter_item", string.Empty) : null) ?? string.Empty;
		}

		public bool HasFilter()
		{
			return !string.IsNullOrEmpty(GetFilterPrefabName());
		}

		public bool AcceptsItem(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			string filterPrefabName = GetFilterPrefabName();
			if (string.IsNullOrEmpty(filterPrefabName))
			{
				return false;
			}
			string itemKey = GetItemKey(item);
			if (!string.IsNullOrEmpty(itemKey))
			{
				return string.Equals(itemKey, filterPrefabName, StringComparison.Ordinal);
			}
			return false;
		}

		public void RequestSetFilterFromClient(string itemPrefabName)
		{
			if (IsUsable())
			{
				Logger.LogInfo((object)("Detect Chest: sending filter set request '" + (itemPrefabName ?? string.Empty) + "'."));
				DetectRPC.RequestChestFilterSet(_nview, itemPrefabName);
			}
		}

		public void SetFilterServer(string itemPrefabName)
		{
			ZNetView nview = _nview;
			ZDO val = ((nview != null) ? nview.GetZDO() : null);
			if (val != null)
			{
				val.Set("detect_filter_item", itemPrefabName ?? string.Empty);
				ContainerSaveMethod?.Invoke(_container, null);
				MethodInfo inventoryChangedMethod = InventoryChangedMethod;
				if ((object)inventoryChangedMethod != null)
				{
					Container container = _container;
					inventoryChangedMethod.Invoke((container != null) ? container.GetInventory() : null, null);
				}
				Logger.LogInfo((object)("Detect Chest: server filter set to '" + (itemPrefabName ?? string.Empty) + "'."));
			}
		}

		public static DetectChest FromContainer(Container container)
		{
			object obj;
			if (!((Object)(object)container == (Object)null))
			{
				obj = ((Component)container).GetComponent<DetectChest>();
				if (obj == null)
				{
					return ((Component)container).GetComponentInParent<DetectChest>();
				}
			}
			else
			{
				obj = null;
			}
			return (DetectChest)obj;
		}

		public static List<DetectChest> GetInRange(Vector3 center, float radius)
		{
			//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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			List<DetectChest> list = new List<DetectChest>();
			float num = radius * radius;
			foreach (DetectChest activeChest in ActiveChests)
			{
				if (!((Object)(object)activeChest == (Object)null) && activeChest.IsUsable())
				{
					Vector3 val = ((Component)activeChest).transform.position - center;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						list.Add(activeChest);
					}
				}
			}
			list.Sort(delegate(DetectChest left, DetectChest right)
			{
				//IL_0006: 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_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: 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_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)left).transform.position - center;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)right).transform.position - center;
				float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude;
				return sqrMagnitude.CompareTo(sqrMagnitude2);
			});
			return list;
		}

		public bool CanAcceptStack(ItemData item, int stackAmount)
		{
			if (item == null || stackAmount <= 0 || !AcceptsItem(item))
			{
				return false;
			}
			Container container = _container;
			Inventory val = ((container != null) ? container.GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			return CanInventoryFitStack(val, item, stackAmount);
		}

		public bool TryStoreStackServer(ItemData item, int stackAmount)
		{
			if (!DetectRPC.IsServer() || !CanAcceptStack(item, stackAmount))
			{
				return false;
			}
			Container container = _container;
			Inventory val = ((container != null) ? container.GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			string itemKey = GetItemKey(item);
			if (string.IsNullOrEmpty(itemKey))
			{
				return false;
			}
			if (!TryAddStack(val, item, stackAmount, itemKey))
			{
				Logger.LogWarning((object)$"Detect Chest: failed to add stack '{itemKey}' x{stackAmount}.");
				return false;
			}
			InventoryChangedMethod?.Invoke(val, null);
			ContainerSaveMethod?.Invoke(_container, null);
			return true;
		}

		private void RPC_RequestSetFilter(long sender, string itemPrefabName)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (DetectRPC.IsServer() && IsUsable())
			{
				if (!DetectRPC.IsSenderCloseEnough(sender, ((Component)this).transform.position, 8f))
				{
					Logger.LogWarning((object)$"Detect Chest filter denied: sender {sender} is too far or unresolved.");
					return;
				}
				Logger.LogInfo((object)$"Detect Chest: filter RPC accepted from sender {sender} with value '{itemPrefabName ?? string.Empty}'.");
				SetFilterServer(itemPrefabName);
			}
		}

		private static bool CanInventoryFitStack(Inventory inventory, ItemData item, int stackAmount)
		{
			int num = stackAmount;
			string itemKey = GetItemKey(item);
			if (string.IsNullOrEmpty(itemKey))
			{
				return false;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem == null || (Object)(object)allItem.m_dropPrefab == (Object)null)
				{
					continue;
				}
				string itemKey2 = GetItemKey(allItem);
				if (string.IsNullOrEmpty(itemKey2) || !string.Equals(itemKey2, itemKey, StringComparison.Ordinal))
				{
					continue;
				}
				int num2 = Mathf.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack);
				if (num2 > 0)
				{
					num -= num2;
					if (num <= 0)
					{
						return true;
					}
				}
			}
			int num3 = Mathf.Max(1, item.m_shared.m_maxStackSize);
			num -= inventory.GetEmptySlots() * num3;
			return num <= 0;
		}

		private static bool TryAddStack(Inventory inventory, ItemData sourceItem, int stackAmount, string prefabName)
		{
			if (sourceItem == null || stackAmount <= 0 || string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			if (AddItemByNameMethod != null && AddItemByNameMethod.Invoke(inventory, new object[7]
			{
				prefabName,
				stackAmount,
				sourceItem.m_quality,
				sourceItem.m_variant,
				sourceItem.m_crafterID,
				sourceItem.m_crafterName ?? string.Empty,
				false
			}) is ItemData)
			{
				return true;
			}
			if (AddItemWithPositionMethod != null)
			{
				ItemData val = sourceItem.Clone();
				val.m_stack = stackAmount;
				object obj = AddItemWithPositionMethod.Invoke(inventory, new object[4] { val, stackAmount, -1, -1 });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			return false;
		}

		private static string GetItemKey(ItemData item)
		{
			if (item == null)
			{
				return string.Empty;
			}
			if (!((Object)(object)item.m_dropPrefab != (Object)null) || string.IsNullOrEmpty(((Object)item.m_dropPrefab).name))
			{
				return item.m_shared?.m_name ?? string.Empty;
			}
			return ((Object)item.m_dropPrefab).name;
		}
	}
	public class DetectHub : MonoBehaviour
	{
		public const string PiecePrefabName = "piece_detect_hub";

		private const string VanillaSourcePiece = "piece_chest_wood";

		private const int RadiusRingSegments = 96;

		private const float RadiusRingHeight = 0.05f;

		private static bool _registered;

		private static readonly MethodInfo InventoryChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);

		private static readonly MethodInfo ContainerSaveMethod = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);

		private static readonly MethodInfo RemoveItemMethod = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[2]
		{
			typeof(ItemData),
			typeof(int)
		}, (Type[])null);

		private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private Container _container;

		private ZNetView _nview;

		private LineRenderer _radiusRenderer;

		private float _radiusRingLastValue = -1f;

		public Container Container => _container;

		public ZNetView NetView => _nview;

		private void Awake()
		{
			_container = ((Component)this).GetComponent<Container>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)_nview != (Object)null)
			{
				_nview.Register("Detect_RequestSort", (Action<long>)RPC_RequestSort);
			}
			SetupRadiusRing();
		}

		private void Update()
		{
			UpdateRadiusRing();
		}

		public static void RegisterPiece()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			if (!_registered)
			{
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("piece_detect_hub", "piece_chest_wood");
				if ((Object)(object)val == (Object)null)
				{
					Logger.LogError((object)"Detect: failed to clone prefab piece_chest_wood for Detect Hub.");
					return;
				}
				ConfigurePrefab(val);
				PieceConfig val2 = new PieceConfig();
				val2.Name = "Detect Hub";
				val2.Description = "Stores loot and sorts it into nearby Detect Chests.";
				val2.PieceTable = "_HammerPieceTable";
				val2.Category = "Detect";
				val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[2]
				{
					new RequirementConfig("Wood", 20, 0, true),
					new RequirementConfig("Stone", 20, 0, true)
				};
				PieceConfig val3 = val2;
				PieceManager.Instance.AddPiece(new CustomPiece(val, true, val3));
				_registered = true;
				Logger.LogInfo((object)"Detect Hub registered.");
			}
		}

		private static void ConfigurePrefab(GameObject prefab)
		{
			Piece component = prefab.GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_name = "Detect Hub";
				component.m_description = "Drop loot here, then click Sort.";
				component.m_craftingStation = null;
			}
			Container component2 = prefab.GetComponent<Container>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_name = "Detect Hub";
			}
			DetectHub detectHub = default(DetectHub);
			if (!prefab.TryGetComponent<DetectHub>(ref detectHub))
			{
				prefab.AddComponent<DetectHub>();
			}
			StationExtension[] componentsInChildren = prefab.GetComponentsInChildren<StationExtension>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			CraftingStation[] componentsInChildren2 = prefab.GetComponentsInChildren<CraftingStation>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
		}

		public float GetScanRadius()
		{
			return Mathf.Max(0.5f, Plugin.HubScanRadius?.Value ?? 10f);
		}

		public bool IsUsable()
		{
			if ((Object)(object)_container != (Object)null && (Object)(object)_nview != (Object)null)
			{
				return _nview.IsValid();
			}
			return false;
		}

		public void RequestSortFromClient()
		{
			if (!IsUsable())
			{
				Logger.LogWarning((object)"Detect Hub: sort request ignored, hub is not usable.");
				return;
			}
			Logger.LogInfo((object)"Detect Hub: sending sort RPC request.");
			DetectRPC.RequestHubSort(_nview);
		}

		private void RPC_RequestSort(long sender)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (!DetectRPC.IsServer() || !IsUsable())
			{
				Logger.LogWarning((object)"Detect Hub: server RPC sort ignored (not server or unusable).");
				return;
			}
			if (!DetectRPC.IsSenderCloseEnough(sender, ((Component)this).transform.position, 8f))
			{
				Logger.LogWarning((object)$"Detect Hub sort denied: sender {sender} is too far or unresolved.");
				return;
			}
			Logger.LogInfo((object)$"Detect Hub: sort RPC accepted from sender {sender}.");
			ExecuteSortServer();
		}

		private void ExecuteSortServer()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				Logger.LogWarning((object)"Detect Hub: sort aborted, hub inventory is null.");
				return;
			}
			List<DetectChest> inRange = DetectChest.GetInRange(((Component)this).transform.position, GetScanRadius());
			if (inRange.Count == 0)
			{
				Logger.LogInfo((object)"Detect Hub: no Detect Chest found in range.");
				return;
			}
			List<ItemData> list = new List<ItemData>(inventory.GetAllItems());
			bool flag = false;
			foreach (ItemData item in list)
			{
				if (item == null || item.m_stack <= 0)
				{
					continue;
				}
				int stack = item.m_stack;
				foreach (DetectChest item2 in inRange)
				{
					if (!((Object)(object)item2 == (Object)null) && item2.IsUsable() && item2.CanAcceptStack(item, stack) && item2.TryStoreStackServer(item, stack))
					{
						if (RemoveItemMethod != null)
						{
							RemoveItemMethod.Invoke(inventory, new object[2] { item, stack });
						}
						else
						{
							inventory.RemoveItem(item, stack);
						}
						flag = true;
						break;
					}
				}
			}
			if (flag)
			{
				InventoryChangedMethod?.Invoke(inventory, null);
				ContainerSaveMethod?.Invoke(_container, null);
				Logger.LogInfo((object)"Detect Hub: sorting moved items successfully.");
			}
			else
			{
				Logger.LogInfo((object)"Detect Hub: sorting completed but no stack could be moved.");
			}
		}

		private void SetupRadiusRing()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("DetectHubRadiusRing");
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.localPosition = new Vector3(0f, 0.05f, 0f);
			val.transform.localRotation = Quaternion.identity;
			_radiusRenderer = val.AddComponent<LineRenderer>();
			_radiusRenderer.loop = true;
			_radiusRenderer.useWorldSpace = false;
			_radiusRenderer.positionCount = 96;
			_radiusRenderer.widthMultiplier = 0.03f;
			_radiusRenderer.textureMode = (LineTextureMode)0;
			((Renderer)_radiusRenderer).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)_radiusRenderer).receiveShadows = false;
			((Renderer)_radiusRenderer).enabled = false;
			Material val2 = new Material(Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default"));
			val2.color = new Color(0.35f, 1f, 0.35f, 0.75f);
			((Renderer)_radiusRenderer).material = val2;
		}

		private void UpdateRadiusRing()
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_radiusRenderer == (Object)null)
			{
				return;
			}
			ConfigEntry<bool> hubShowRadiusRing = Plugin.HubShowRadiusRing;
			if ((hubShowRadiusRing != null && !hubShowRadiusRing.Value) || !ShouldShowBuildRadius())
			{
				((Renderer)_radiusRenderer).enabled = false;
				return;
			}
			((Renderer)_radiusRenderer).enabled = true;
			float num = Mathf.Max(0.5f, GetScanRadius());
			if (!Mathf.Approximately(num, _radiusRingLastValue))
			{
				_radiusRingLastValue = num;
				for (int i = 0; i < 96; i++)
				{
					float num2 = (float)i / 96f * (float)Math.PI * 2f;
					float num3 = Mathf.Cos(num2) * num;
					float num4 = Mathf.Sin(num2) * num;
					_radiusRenderer.SetPosition(i, new Vector3(num3, 0f, num4));
				}
			}
		}

		private static bool ShouldShowBuildRadius()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			object? obj = PlacementGhostField?.GetValue(localPlayer);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			string text = ((Object)val).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				text = text.Substring(0, num).Trim();
			}
			if (!string.Equals(text, "piece_detect_hub", StringComparison.Ordinal))
			{
				return string.Equals(text, "piece_detect_chest", StringComparison.Ordinal);
			}
			return true;
		}
	}
}
namespace Detect.Net
{
	public static class DetectRPC
	{
		public const string HubSortRequest = "Detect_RequestSort";

		public const string ChestFilterRequest = "Detect_RequestFilterSet";

		private static readonly MethodInfo GetPeerMethod = AccessTools.Method(typeof(ZNet), "GetPeer", new Type[1] { typeof(long) }, (Type[])null);

		private static readonly FieldInfo PeerRefPosField = AccessTools.Field(typeof(ZNetPeer), "m_refPos");

		public static void RequestHubSort(ZNetView nview)
		{
			if (!((Object)(object)nview == (Object)null) && nview.IsValid())
			{
				nview.InvokeRPC("Detect_RequestSort", Array.Empty<object>());
			}
		}

		public static void RequestChestFilterSet(ZNetView nview, string filterPrefabName)
		{
			if (!((Object)(object)nview == (Object)null) && nview.IsValid())
			{
				nview.InvokeRPC("Detect_RequestFilterSet", new object[1] { filterPrefabName ?? string.Empty });
			}
		}

		public static bool IsServer()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}

		public static bool IsSenderCloseEnough(long sender, Vector3 point, float maxDistance)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			float num = maxDistance * maxDistance;
			object obj = GetPeerMethod?.Invoke(ZNet.instance, new object[1] { sender });
			if (obj != null && PeerRefPosField != null)
			{
				Vector3 val = (Vector3)PeerRefPosField.GetValue(obj) - point;
				return ((Vector3)(ref val)).sqrMagnitude <= num;
			}
			if (ZNet.instance.IsServer() && !ZNet.instance.IsDedicated())
			{
				return true;
			}
			return false;
		}
	}
}