Decompiled source of AutoFeed v1.0.2

Narolith.AutoFeed.dll

Decompiled 3 weeks 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 System.Security;
using System.Security.Permissions;
using AutoFeed.Core;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Narolith.AutoFeed")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim mod to auto feed animals from a nearby chest")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+134a89fcff9edb0f1fc7189cd2c11af19bd815c2")]
[assembly: AssemblyProduct("Narolith.AutoFeed")]
[assembly: AssemblyTitle("Narolith.AutoFeed")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 AutoFeed
{
	public static class ContainerExtensions
	{
		public static bool ContainersContainItemFromList(this List<Container> containers, Dictionary<string, ItemData> consumableMap, out Container? targetContainer, out ItemData? targetItem)
		{
			return FindItemInContainers(containers, consumableMap, out targetContainer, out targetItem);
		}

		private static bool FindItemInContainers(List<Container> containers, Dictionary<string, ItemData> consumableMap, out Container? targetContainer, out ItemData? targetItem)
		{
			foreach (Container container in containers)
			{
				if (TryFindMatchingItem(container.GetInventory().GetAllItems(), consumableMap, out targetItem))
				{
					targetContainer = container;
					return true;
				}
			}
			targetContainer = null;
			targetItem = null;
			return false;
		}

		private static bool TryFindMatchingItem(List<ItemData> items, Dictionary<string, ItemData> consumableMap, out ItemData? targetItem)
		{
			foreach (ItemData item in items)
			{
				if (consumableMap.TryGetValue(item.m_shared.m_name, out ItemData value))
				{
					targetItem = value;
					return true;
				}
			}
			targetItem = null;
			return false;
		}
	}
	public static class MonsterAIExtensions
	{
		public static void FeedMonsterWithThrottling(this MonsterAI __instance, Tameable ___m_tamable, Character ___m_character, Container container, ItemData item)
		{
			FeedAnimal(__instance, ___m_tamable, ___m_character, container, item);
			Plugin.LastFeedTimes[((Object)___m_character).GetInstanceID()] = Time.time;
		}

		private static void FeedAnimal(MonsterAI monsterAI, Tameable tamable, Character character, Container container, ItemData item)
		{
			if (tamable != null && monsterAI != null && tamable.IsHungry())
			{
				monsterAI.ConsumeItem(character);
				ZNetView component = ((Component)container).GetComponent<ZNetView>();
				if (component != null && component.IsValid())
				{
					container.GetInventory().RemoveItem(item.m_shared.m_name, 1, -1, true);
					Traverse.Create((object)container.GetInventory()).Method("Changed", Array.Empty<object>()).GetValue();
					Traverse.Create((object)container).Method("Save", Array.Empty<object>()).GetValue();
				}
			}
		}

		public static void ConsumeItem(this MonsterAI monsterAI, Character character)
		{
			//IL_0029: 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)
			monsterAI.m_onConsumedItem?.Invoke(null);
			Character obj = ((character is Humanoid) ? character : null);
			if (obj != null)
			{
				((Humanoid)obj).m_consumeItemEffects.Create(((Component)character).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
			}
			ZSyncAnimation value = Traverse.Create((object)monsterAI).Field("m_animator").GetValue<ZSyncAnimation>();
			if (value != null)
			{
				value.SetTrigger("consume");
			}
		}
	}
	public static class Vector3Extensions
	{
		public static List<Container> GetContainersInRange(this Vector3 center, float radiusRange, int animalId, float currentTime)
		{
			//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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ContainerCache.TryGetValue(animalId, out (float, List<Container>) value) && FeedingLogic.IsCacheValid(value.Item1, currentTime, Plugin.CacheTtl.Value))
			{
				value.Item2.RemoveAll((Container c) => (Object)(object)c == (Object)null);
				return value.Item2;
			}
			float num = radiusRange * radiusRange;
			List<Container> list = new List<Container>();
			foreach (Piece s_allPiece in Piece.s_allPieces)
			{
				if ((Object)(object)s_allPiece == (Object)null)
				{
					continue;
				}
				Container component = ((Component)s_allPiece).GetComponent<Container>();
				if (!((Object)(object)component == (Object)null))
				{
					Vector3 val = ((Component)s_allPiece).transform.position - center;
					if (((Vector3)(ref val)).sqrMagnitude <= num && IsValidZNetView(((Component)component).GetComponent<ZNetView>()) && IsEligibleContainer(component))
					{
						list.Add(component);
					}
				}
			}
			List<Container> list2 = (from x in list.Select(delegate(Container c)
				{
					//IL_0007: 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_0012: Unknown result type (might be due to invalid IL or missing references)
					//IL_0017: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val2 = ((Component)c).transform.position - center;
					return (c, ((Vector3)(ref val2)).sqrMagnitude);
				})
				orderby x.sqrDist
				select x.container).ToList();
			Plugin.ContainerCache[animalId] = (currentTime, list2);
			return list2;
		}

		private static bool IsValidZNetView(ZNetView? zNetView)
		{
			if (zNetView != null)
			{
				return zNetView.IsValid();
			}
			return false;
		}

		private static bool IsEligibleContainer(Container container)
		{
			Inventory inventory = container.GetInventory();
			return FeedingLogic.IsEligibleContainer(((Object)container).name, (inventory != null) ? inventory.NrOfItems() : 0, Plugin.ChestPrefix.Value);
		}
	}
	[BepInPlugin("Narolith.AutoFeed", "Narolith.AutoFeed", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(MonsterAI), "UpdateConsumeItem")]
		private static class UpdateConsumeItemPatch
		{
			private static void Postfix(MonsterAI __instance, Character ___m_character, Tameable ___m_tamable, List<ItemDrop> ___m_consumeItems, bool __result)
			{
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				Character ___m_character2 = ___m_character;
				List<ItemDrop> ___m_consumeItems2 = ___m_consumeItems;
				Tameable ___m_tamable2 = ___m_tamable;
				if (Player.m_localPlayer == null || !ModEnabled() || !HasCharacterData() || !IsTamedAndHungry() || !HasValidFoodTypes() || HasFoundFood())
				{
					return;
				}
				int instanceID = ((Object)___m_character2).GetInstanceID();
				if (!FeedingLogic.ShouldFeed(instanceID, Time.time, LastFeedTimes, 5f))
				{
					return;
				}
				Dictionary<string, ItemData> dictionary = new Dictionary<string, ItemData>();
				foreach (ItemDrop item in ___m_consumeItems2)
				{
					string name = item.m_itemData.m_shared.m_name;
					if (!dictionary.ContainsKey(name))
					{
						dictionary[name] = item.m_itemData;
					}
				}
				if (((Component)___m_character2).gameObject.transform.position.GetContainersInRange(ContainerRange.Value, instanceID, Time.time).ContainersContainItemFromList(dictionary, out Container targetContainer, out ItemData targetItem))
				{
					__instance.FeedMonsterWithThrottling(___m_tamable2, ___m_character2, targetContainer, targetItem);
				}
				bool HasCharacterData()
				{
					return ___m_character2 != null;
				}
				bool HasFoundFood()
				{
					return __result;
				}
				bool HasValidFoodTypes()
				{
					if (___m_consumeItems2 != null)
					{
						return ___m_consumeItems2.Count > 0;
					}
					return false;
				}
				bool IsTamedAndHungry()
				{
					if (___m_tamable2 != null)
					{
						return ___m_tamable2.IsHungry();
					}
					return false;
				}
				static bool ModEnabled()
				{
					return Plugin.ModEnabled.Value;
				}
			}
		}

		internal static ManualLogSource Log = null;

		private static Harmony? _harmony;

		public static readonly Dictionary<int, float> LastFeedTimes = new Dictionary<int, float>();

		internal static readonly Dictionary<int, (float timestamp, List<Container> containers)> ContainerCache = new Dictionary<int, (float, List<Container>)>();

		public static ConfigEntry<float> ContainerRange = null;

		public static ConfigEntry<bool> ModEnabled = null;

		public static ConfigEntry<string> ChestPrefix = null;

		public static ConfigEntry<float> CacheTtl = null;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"AutoFeed v1.0.2 loading...");
			ContainerRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Container Range", 10f, "The radius in which a tamed creature will look for containers to feed from.");
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Whether the mod is enabled.");
			ChestPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Chest Prefix", "piece_chest", "Name prefix for containers eligible for auto-feeding. Leave empty to allow all containers.");
			CacheTtl = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Container Cache TTL", 5f, "Seconds before the nearby-container list is refreshed for each animal.");
			_harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
			Log.LogInfo((object)"AutoFeed loaded.");
		}
	}
	public static class PluginSettings
	{
		public const float FeedInterval = 5f;
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "Narolith.AutoFeed";

		public const string PLUGIN_NAME = "Narolith.AutoFeed";

		public const string PLUGIN_VERSION = "1.0.2";
	}
}
namespace AutoFeed.Core
{
	public static class FeedingLogic
	{
		public static string? FindConsumableInInventory(IEnumerable<string> inventoryItemNames, HashSet<string> consumableNames)
		{
			foreach (string inventoryItemName in inventoryItemNames)
			{
				if (consumableNames.Contains(inventoryItemName))
				{
					return inventoryItemName;
				}
			}
			return null;
		}

		public static bool ShouldFeed(int animalId, float currentTime, IReadOnlyDictionary<int, float> lastFeedTimes, float interval)
		{
			if (!lastFeedTimes.TryGetValue(animalId, out var value))
			{
				return true;
			}
			return currentTime - value >= interval;
		}

		public static bool IsEligibleContainer(string containerName, int itemCount, string prefix)
		{
			if (!string.IsNullOrEmpty(prefix) && !containerName.StartsWith(prefix))
			{
				return false;
			}
			return itemCount > 0;
		}

		public static bool IsCacheValid(float cachedTimestamp, float currentTime, float ttl)
		{
			return currentTime - cachedTimestamp < ttl;
		}
	}
}