Decompiled source of SmartDeposit v0.1.2

BepInEx/plugins/SmartDeposit.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("SmartDeposit")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("SmartDeposit")]
[assembly: AssemblyTitle("SmartDeposit")]
[assembly: AssemblyVersion("0.1.2.0")]
namespace SmartDeposit;

[BepInPlugin("local.valheim.smartdeposit", "Smart Deposit", "0.1.2")]
public sealed class Plugin : BaseUnityPlugin
{
	private sealed class Request
	{
		public Player Player;

		public bool Done;

		public bool Cancelled;

		public float Deadline;
	}

	[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
	private static class ResponsePatch
	{
		private static bool Prefix(Container __instance, bool __1)
		{
			if (!Object.op_Implicit((Object)(object)instance) || !instance.pending.TryGetValue(__instance, out var value))
			{
				return true;
			}
			instance.pending.Remove(__instance);
			instance.Response(__instance, __1, value);
			return false;
		}
	}

	[HarmonyPatch(typeof(Container), "StackAll")]
	private static class StackPatch
	{
		private static bool Prefix(Container __instance)
		{
			if (Object.op_Implicit((Object)(object)instance) && instance.pending.ContainsKey(__instance))
			{
				return (Object)(object)instance.dispatching == (Object)(object)__instance;
			}
			return true;
		}
	}

	private static class CommandPatch
	{
		internal static bool Prefix(Terminal __instance, string __0)
		{
			string[] array = (__0 ?? "").Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0 || !array[0].Equals("smartdeposit", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (!Object.op_Implicit((Object)(object)instance) || !((Behaviour)instance).enabled)
			{
				__instance.AddString("Smart Deposit unavailable.");
				return false;
			}
			if (array.Length == 1)
			{
				instance.Begin();
				return false;
			}
			if (array.Length == 3 && (array[1].Equals("keep", StringComparison.OrdinalIgnoreCase) || array[1].Equals("unkeep", StringComparison.OrdinalIgnoreCase)))
			{
				HashSet<string> hashSet = new HashSet<string>(from s in instance.keep.Value.Split(',')
					select s.Trim() into s
					where s.Length > 0
					select s, StringComparer.OrdinalIgnoreCase);
				if (array[1].Equals("keep", StringComparison.OrdinalIgnoreCase))
				{
					hashSet.Add(array[2]);
				}
				else
				{
					hashSet.Remove(array[2]);
				}
				instance.keep.Value = string.Join(",", hashSet.OrderBy((string s) => s));
				__instance.AddString("Smart Deposit keep list: " + instance.keep.Value);
				return false;
			}
			__instance.AddString("Usage: /smartdeposit; /smartdeposit keep Wood; /smartdeposit unkeep Wood. Keep list: " + instance.keep.Value);
			return false;
		}
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static Func<Container, bool> <>9__20_2;

		public static ConsoleEvent <>9__25_0;

		internal bool <Deposit>b__20_2(Container c)
		{
			return !Object.op_Implicit((Object)(object)c);
		}

		internal void <RegisterCommand>b__25_0(ConsoleEventArgs args)
		{
			CommandPatch.Prefix(args.Context, args.FullLine);
		}
	}

	public const string Id = "local.valheim.smartdeposit";

	private static Plugin instance;

	private Harmony harmony;

	private ConfigEntry<KeyboardShortcut> key;

	private ConfigEntry<float> range;

	private ConfigEntry<string> keep;

	private bool busy;

	private Container dispatching;

	private int moved;

	private int chests;

	private readonly Dictionary<Container, Request> pending = new Dictionary<Container, Request>();

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

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

	private void Awake()
	{
		//IL_0021: 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_006e: Expected O, but got Unknown
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Expected O, but got Unknown
		instance = this;
		key = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "DepositKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Deposit matching items into nearby chests.");
		range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 10f, new ConfigDescription("Search range in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
		keep = ((BaseUnityPlugin)this).Config.Bind<string>("General", "KeepItems", "", "Comma-separated item prefab names to protect. Use /smartdeposit keep Wood, or /smartdeposit unkeep Wood.");
		harmony = new Harmony("local.valheim.smartdeposit");
		try
		{
			harmony.PatchAll();
			RegisterCommand();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Smart Deposit 0.1.2 loaded: F10 or /smartdeposit.");
		}
		catch (Exception ex)
		{
			harmony.UnpatchSelf();
			((Behaviour)this).enabled = false;
			((BaseUnityPlugin)this).Logger.LogError((object)ex);
		}
	}

	private void Update()
	{
		//IL_0039: 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)
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible())
		{
			KeyboardShortcut value = key.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Begin();
			}
		}
	}

	private void Say(string message)
	{
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, "Smart Deposit: " + message, 0, (Sprite)null, false);
		}
	}

	private void Begin()
	{
		if (busy)
		{
			Say("already working.");
		}
		else if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead() || InventoryGui.IsVisible())
		{
			Say("close your inventory first.");
		}
		else
		{
			((MonoBehaviour)this).StartCoroutine(Deposit());
		}
	}

	private bool Allowed(Container c, Player p)
	{
		//IL_0034: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)c) || !Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || ((Character)p).IsDead() || InventoryGui.IsVisible())
		{
			return false;
		}
		if (Vector3.Distance(((Component)c).transform.position, ((Component)p).transform.position) > range.Value || c.IsInUse())
		{
			return false;
		}
		Piece component = ((Component)c).GetComponent<Piece>();
		if (!Object.op_Implicit((Object)(object)component) || component.GetCreator() == 0L || Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent<Vagon>()))
		{
			return false;
		}
		ZNetView component2 = ((Component)c).GetComponent<ZNetView>();
		if (Object.op_Implicit((Object)(object)component2) && component2.IsValid() && (bool)CheckAccess.Invoke(c, new object[1] { p.GetPlayerID() }))
		{
			return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false);
		}
		return false;
	}

	private bool Eligible(ItemData item, Player p)
	{
		if (item.m_gridPos.y == 0 || item.m_equipped || ((Humanoid)p).IsItemEquiped(item) || item.m_stack <= 0)
		{
			return false;
		}
		if (item.m_customData != null && item.m_customData.Count > 0)
		{
			return false;
		}
		string prefab = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : "");
		return !keep.Value.Split(',').Any((string x) => x.Trim().Equals(prefab, StringComparison.OrdinalIgnoreCase));
	}

	private IEnumerator Deposit()
	{
		busy = true;
		moved = 0;
		chests = 0;
		Player p = Player.m_localPlayer;
		try
		{
			Container[] array = pending.Keys.Where((Container val2) => !Object.op_Implicit((Object)(object)val2)).ToArray();
			foreach (Container val in array)
			{
				pending.Remove(val);
			}
			Container[] array2 = (from val2 in Object.FindObjectsByType<Container>((FindObjectsSortMode)0)
				where Allowed(val2, p)
				orderby Vector3.Distance(((Component)val2).transform.position, ((Component)p).transform.position)
				select val2).ToArray();
			Container[] array3 = array2;
			foreach (Container c in array3)
			{
				if (!Allowed(c, p) || pending.ContainsKey(c))
				{
					continue;
				}
				Request request = new Request
				{
					Player = p,
					Deadline = Time.realtimeSinceStartup + 4f
				};
				pending.Add(c, request);
				try
				{
					dispatching = c;
					c.StackAll();
				}
				catch (Exception ex)
				{
					request.Cancelled = true;
					request.Done = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)ex);
				}
				finally
				{
					dispatching = null;
				}
				while (!request.Done && Object.op_Implicit((Object)(object)c) && Time.realtimeSinceStartup < request.Deadline)
				{
					yield return null;
				}
				if (!request.Done)
				{
					request.Cancelled = true;
				}
				yield return null;
			}
			Say((moved > 0) ? $"deposited {moved} items into {chests} chests." : "nothing deposited. No accessible matching space, or all items protected.");
		}
		finally
		{
			busy = false;
		}
	}

	private void Response(Container c, bool granted, Request request)
	{
		try
		{
			if (!granted || request.Cancelled || Time.realtimeSinceStartup > request.Deadline || !Allowed(c, request.Player))
			{
				return;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if (!component.IsOwner())
			{
				return;
			}
			Load.Invoke(c, null);
			if (Allowed(c, request.Player) && component.IsOwner())
			{
				Inventory inventory = ((Humanoid)request.Player).GetInventory();
				Inventory inventory2 = c.GetInventory();
				int num = Transfer(inventory, inventory2, (ItemData item) => Eligible(item, request.Player));
				moved += num;
				if (num > 0)
				{
					chests++;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)ex);
			Say("transfer stopped; check the BepInEx log.");
		}
		finally
		{
			request.Done = true;
		}
	}

	internal static int Transfer(Inventory source, Inventory target, Func<ItemData, bool> eligible)
	{
		int num = 0;
		ItemData[] array = source.GetAllItems().ToArray();
		foreach (ItemData item in array)
		{
			if (!eligible(item) || !target.GetAllItems().Any((ItemData t) => t.IsSameType(item)))
			{
				continue;
			}
			ItemData[] array2 = (from t in target.GetAllItems()
				where t.IsSameType(item)
				select t).ToArray();
			foreach (ItemData val in array2)
			{
				int num3 = Math.Min(item.m_stack, Math.Max(0, val.m_shared.m_maxStackSize - val.m_stack));
				if (num3 > 0)
				{
					int stack = item.m_stack;
					target.MoveItemToThis(source, item, num3, val.m_gridPos.x, val.m_gridPos.y);
					num += stack - item.m_stack;
				}
			}
			for (int num4 = 0; num4 < target.GetHeight(); num4++)
			{
				if (item.m_stack <= 0)
				{
					break;
				}
				for (int num5 = 0; num5 < target.GetWidth(); num5++)
				{
					if (item.m_stack <= 0)
					{
						break;
					}
					if (target.GetItemAt(num5, num4) == null)
					{
						int stack2 = item.m_stack;
						target.MoveItemToThis(source, item, Math.Min(item.m_stack, item.m_shared.m_maxStackSize), num5, num4);
						num += stack2 - item.m_stack;
					}
				}
			}
		}
		return num;
	}

	private static void RegisterCommand()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: 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_0029: Expected O, but got Unknown
		object obj = <>c.<>9__25_0;
		if (obj == null)
		{
			ConsoleEvent val = delegate(ConsoleEventArgs args)
			{
				CommandPatch.Prefix(args.Context, args.FullLine);
			};
			<>c.<>9__25_0 = val;
			obj = (object)val;
		}
		new ConsoleCommand("smartdeposit", "Deposit nearby matching items. Usage: smartdeposit [keep|unkeep ItemPrefab]", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private void OnDestroy()
	{
		((MonoBehaviour)this).StopAllCoroutines();
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
		instance = null;
	}
}