Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AutoFeed v1.0.2
Narolith.AutoFeed.dll
Decompiled 3 weeks agousing 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; } } }