Decompiled source of StashLink v1.0.0

StashLink.dll

Decompiled 5 days ago
using System;
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 BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("StashLink")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StashLink")]
[assembly: AssemblyTitle("StashLink")]
[assembly: AssemblyVersion("1.0.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 StashLink
{
	public static class ChestFinder
	{
		private const float CacheSeconds = 0.5f;

		private static List<Container> cache = new List<Container>();

		private static Vector3 cacheCenter;

		private static float cacheRadius;

		private static float cacheTime = -999f;

		private static readonly Dictionary<string, int> totals = new Dictionary<string, int>();

		private static readonly HashSet<string> seenNames = new HashSet<string>();

		private static bool totalsDirty = true;

		private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);

		private static readonly MethodInfo WardCheckMethod = typeof(PrivateArea).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "CheckAccess" && m.GetParameters().Length >= 1 && m.GetParameters()[0].ParameterType == typeof(Vector3));

		public static bool AccessCheckAvailable
		{
			get
			{
				if (CheckAccessMethod != null)
				{
					return WardCheckMethod != null;
				}
				return false;
			}
		}

		private static bool HasAccess(Container container, long playerId)
		{
			if (CheckAccessMethod == null)
			{
				return false;
			}
			return (bool)CheckAccessMethod.Invoke(container, new object[1] { playerId });
		}

		private static bool WardAllows(Vector3 position)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (WardCheckMethod == null)
			{
				return false;
			}
			ParameterInfo[] parameters = WardCheckMethod.GetParameters();
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				ParameterInfo parameterInfo = parameters[i];
				if (i == 0)
				{
					array[i] = position;
				}
				else if (parameterInfo.Name == "flash")
				{
					array[i] = false;
				}
				else if (parameterInfo.HasDefaultValue)
				{
					array[i] = parameterInfo.DefaultValue;
				}
				else
				{
					array[i] = (parameterInfo.ParameterType.IsValueType ? Activator.CreateInstance(parameterInfo.ParameterType) : null);
				}
			}
			return (bool)WardCheckMethod.Invoke(null, array);
		}

		public static bool TryGetContext(Player player, out Vector3 center, out float radius)
		{
			//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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null)
			{
				center = ((Component)currentCraftingStation).transform.position;
				radius = StashLinkPlugin.Radius.Value;
				return true;
			}
			if (((Character)player).InPlaceMode())
			{
				center = ((Component)player).transform.position;
				radius = StashLinkPlugin.BuildRadius.Value;
				return true;
			}
			center = Vector3.zero;
			radius = 0f;
			return false;
		}

		public static List<Container> GetNearbyContainers(Vector3 center, float radius, bool fresh = false)
		{
			//IL_0015: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!fresh && Time.time - cacheTime < 0.5f)
			{
				Vector3 val = center - cacheCenter;
				if (((Vector3)(ref val)).sqrMagnitude < 1f && radius == cacheRadius)
				{
					return cache;
				}
			}
			List<Container> list = new List<Container>();
			Collider[] array = Physics.OverlapSphere(center, radius);
			Player localPlayer = Player.m_localPlayer;
			Collider[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Container componentInParent = ((Component)array2[i]).GetComponentInParent<Container>();
				if (!((Object)(object)componentInParent == (Object)null) && !list.Contains(componentInParent) && componentInParent.GetInventory() != null && !((Object)(object)localPlayer == (Object)null) && HasAccess(componentInParent, localPlayer.GetPlayerID()) && WardAllows(((Component)componentInParent).transform.position))
				{
					list.Add(componentInParent);
				}
			}
			cache = list;
			cacheCenter = center;
			cacheRadius = radius;
			cacheTime = Time.time;
			totalsDirty = true;
			return list;
		}

		public static int CountInChests(string name, Vector3 center, float radius)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			List<Container> nearbyContainers = GetNearbyContainers(center, radius);
			if (totalsDirty)
			{
				totals.Clear();
				foreach (Container item in nearbyContainers)
				{
					if ((Object)(object)item == (Object)null)
					{
						continue;
					}
					Inventory inventory = item.GetInventory();
					seenNames.Clear();
					foreach (ItemData allItem in inventory.GetAllItems())
					{
						string name2 = allItem.m_shared.m_name;
						if (seenNames.Add(name2))
						{
							totals.TryGetValue(name2, out var value);
							totals[name2] = value + inventory.CountItems(name2, -1, true);
						}
					}
				}
				totalsDirty = false;
			}
			if (!totals.TryGetValue(name, out var value2))
			{
				return 0;
			}
			return value2;
		}

		public static void InvalidateCounts()
		{
			totalsDirty = true;
		}
	}
	[HarmonyPatch(typeof(Inventory), "CountItems")]
	public static class CountItemsPatch
	{
		private static void Postfix(Inventory __instance, string name, ref int __result)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (StashLinkPlugin.IgnoreChests)
			{
				return;
			}
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && __instance == ((Humanoid)localPlayer).GetInventory() && ChestFinder.TryGetContext(localPlayer, out var center, out var radius))
				{
					__result += ChestFinder.CountInChests(name, center, radius);
				}
			}
			catch (Exception ex)
			{
				StashLinkPlugin.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch]
	public static class RemoveItemPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.GetDeclaredMethods(typeof(Inventory)).First((MethodInfo m) => m.Name == "RemoveItem" && m.GetParameters().Length >= 2 && m.GetParameters()[0].ParameterType == typeof(string) && m.GetParameters()[1].ParameterType == typeof(int));
		}

		private static void Prefix(Inventory __instance, string name, ref int amount)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory() || !ChestFinder.TryGetContext(localPlayer, out var center, out var radius))
				{
					return;
				}
				StashLinkPlugin.IgnoreChests = true;
				int num;
				try
				{
					num = __instance.CountItems(name, -1, true);
				}
				finally
				{
					StashLinkPlugin.IgnoreChests = false;
				}
				int num2 = amount - num;
				if (num2 <= 0)
				{
					return;
				}
				foreach (Container nearbyContainer in ChestFinder.GetNearbyContainers(center, radius, fresh: true))
				{
					if (num2 <= 0)
					{
						break;
					}
					if (!((Object)(object)nearbyContainer == (Object)null))
					{
						Inventory inventory = nearbyContainer.GetInventory();
						int num3 = Math.Min(num2, inventory.CountItems(name, -1, true));
						if (num3 > 0)
						{
							inventory.RemoveItem(name, num3, -1, true);
							num2 -= num3;
						}
					}
				}
				ChestFinder.InvalidateCounts();
				amount = num + num2;
			}
			catch (Exception ex)
			{
				StashLinkPlugin.Log.LogError((object)ex);
			}
		}
	}
	[BepInPlugin("rhyzt.valheim.stashlink", "StashLink", "1.0.0")]
	public class StashLinkPlugin : BaseUnityPlugin
	{
		public static ConfigEntry<float> Radius;

		public static ConfigEntry<float> BuildRadius;

		public static ManualLogSource Log;

		public static bool IgnoreChests;

		private void Awake()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Radius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Radius", 10f, "Radius (in meters) around the station where chest will be searched for crafting");
			Log = ((BaseUnityPlugin)this).Logger;
			BuildRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BuildRadius", 10f, "Radius (in meters) around the player where chests will be searched for building");
			((BaseUnityPlugin)this).Logger.LogError((object)"Game access checks not found: the mod will not use any chests.");
			new Harmony("rhyzt.valheim.stashlink").PatchAll();
		}
	}
}