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 TroughFoodCompatibility v0.1.2
plugins/TroughFoodCompatibility/TroughFoodCompatibility.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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: AssemblyCompany("TroughFoodCompatibility")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyProduct("TroughFoodCompatibility")] [assembly: AssemblyTitle("TroughFoodCompatibility")] [assembly: AssemblyVersion("0.1.2.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 TroughFoodCompatibility { internal static class FoodRules { internal static readonly string[] OriginalFoods = new string[5] { "Carrot", "Turnip", "Onion", "Raspberry", "Blueberries" }; internal static HashSet<string> Parse(string text) { return new HashSet<string>(from x in (text ?? "").Split(new char[1] { ',' }) select x.Trim() into x where x.Length > 0 select x, StringComparer.Ordinal); } internal static string Prefab(string name) { string text; if (name == null || !name.EndsWith("(Clone)", StringComparison.Ordinal)) { text = name; if (text == null) { return ""; } } else { text = name.Substring(0, name.Length - 7); } return text; } internal static IEnumerable<string> MeatEquivalents(IEnumerable<string> diet) { HashSet<string> foods = new HashSet<string>(diet, StringComparer.Ordinal); if (foods.Contains("RawMeat")) { yield return "rk_pork"; } if (foods.Contains("HareMeat")) { yield return "BOH_RawHare_Meat"; } } internal static IEnumerable<string> AdditionalDiets(string creature, IEnumerable<string> diet, bool wolfHare, bool seafood, bool oceanFish, bool henSeeds) { HashSet<string> foods = new HashSet<string>(diet, StringComparer.Ordinal); if (wolfHare && creature == "Wolf") { yield return "HareMeat"; yield return "BOH_RawHare_Meat"; } if (seafood && foods.Contains("FishRaw")) { yield return "Meat_Cod_RtD"; yield return "Meat_Tuna_RtD"; yield return "Meat_Marlin_RtD"; } if (oceanFish && (creature == "Monster_Orca_RtD" || creature == "Mirmaid_RtD")) { yield return "FishRaw"; } if (henSeeds && creature == "Hen") { yield return "Seed_Rice_RtD"; yield return "Seed_Wheat_RtD"; yield return "Seed_Corn_RtD"; } } internal static string Signature(IEnumerable<string> diet) { return string.Join(",", diet.Distinct().OrderBy<string, string>((string x) => x, StringComparer.Ordinal)); } internal static bool RejectMixed<T>(IEnumerable<T> items, Func<T, bool> invalidFood, bool enabled) { if (!enabled) { return items.Any(invalidFood); } return false; } } [BepInPlugin("vercadi.TroughFoodCompatibility", "Trough Food Compatibility", "0.1.2")] [BepInDependency("MagicMike.CoreWoodExtras", "2.2.7")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")] public sealed class Plugin : BaseUnityPlugin { private sealed class DietState { internal int Revision = -1; internal List<ItemDrop> List; internal string Signature; internal readonly List<ItemDrop> Added = new List<ItemDrop>(); } public const string Id = "vercadi.TroughFoodCompatibility"; internal const string SupportedHash227 = "EBEE48CF9F4675FCEE70FDBE6CC3768D4E9CFC564613BDE4CFFC2EF3A47EB62F"; internal const string SupportedHash228 = "D8017BEA62F3201C693B5D6B5BFB5E219DA1E0E637CC0399BBC362A3C94540D2"; private static Plugin instance; private Harmony harmony; private ConfigSync sync; private ConfigEntry<bool> enabledFeature; private ConfigEntry<bool> allowCollect; private ConfigEntry<bool> meatEquivalents; private ConfigEntry<bool> wolfHare; private ConfigEntry<bool> seafood; private ConfigEntry<bool> oceanFish; private ConfigEntry<bool> henSeeds; private ConfigEntry<string> herbivores; private ConfigEntry<string> extraFoods; private HashSet<string> herbivoreNames; private HashSet<string> extraFoodNames; private HashSet<string> troughFoods; private readonly HashSet<string> additions = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> observedFoods = new HashSet<string>(StringComparer.Ordinal); private readonly ConditionalWeakTable<MonsterAI, DietState> dietStates = new ConditionalWeakTable<MonsterAI, DietState>(); private int revision; private bool ready; private static bool Active { get { if (Object.op_Implicit((Object)(object)instance) && instance.ready) { return instance.enabledFeature.Value; } return false; } } private void Awake() { //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Expected O, but got Unknown //IL_041f: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Expected O, but got Unknown //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Expected O, but got Unknown //IL_0463: Expected O, but got Unknown //IL_047c: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Expected O, but got Unknown instance = this; enabledFeature = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Accept registered creature foods in CoreWoodExtras troughs and allow mixed-food troughs."); allowCollect = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Allow trough auto collect", false, "Allow CoreWoodExtras automatic ground pickup. Its own option must also be enabled. Expanded diets can include valuable meals and materials; disabled by default."); meatEquivalents = ((BaseUnityPlugin)this).Config.Bind<bool>("Optional diets", "Raw meat equivalents", true, "Animals that eat vanilla boar meat also accept BoneAppetit Raw Pork (rk_pork). Animals that eat vanilla hare meat also accept OdinsHares raw hare meat (BOH_RawHare_Meat). Does not give meat to herbivores. Missing mods are ignored."); herbivores = ((BaseUnityPlugin)this).Config.Bind<string>("Optional diets", "Creature prefabs", "Deer,Boar,Sheep_TW", "Exact creatures whose diets receive Extra food prefabs. Empty extra-food list makes no diet changes."); extraFoods = ((BaseUnityPlugin)this).Config.Bind<string>("Optional diets", "Extra food prefabs", "", "Optional comma-separated installed item prefab names. Adds to these creatures' diets without replacing existing food. Empty by default. Missing items are ignored."); wolfHare = ((BaseUnityPlugin)this).Config.Bind<bool>("Optional diets", "Wolves eat hare meat", false, "Let ordinary wolves eat vanilla and OdinsHares raw hare meat."); seafood = ((BaseUnityPlugin)this).Config.Bind<bool>("Optional diets", "Raw fish equivalents", false, "Creatures already eating FishRaw also accept installed RtD cod, tuna and marlin meats."); oceanFish = ((BaseUnityPlugin)this).Config.Bind<bool>("Optional diets", "Ocean companion fish fallback", false, "RtD orcas and mermaids also accept vanilla FishRaw. Dolphins retain their shrimp diet."); henSeeds = ((BaseUnityPlugin)this).Config.Bind<bool>("Optional diets", "Hens eat RtD seeds", false, "Adult hens accept installed RtD rice, wheat and corn seeds. Does not change baby chickens."); sync = new ConfigSync("vercadi.TroughFoodCompatibility") { DisplayName = "Trough Food Compatibility", CurrentVersion = "0.1.2", MinimumRequiredVersion = "0.1.2", ModRequired = true }; ((ConditionalConfigSync)sync).AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock server settings", true, "Only server administrators may change shared settings.")); ((ConditionalConfigSync)sync).AddConfigEntry<bool>(enabledFeature, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<bool>(allowCollect, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<string>(herbivores, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<string>(extraFoods, (ConfigSyncMode)0); ((ConditionalConfigSync)sync).AddConfigEntry<bool>(meatEquivalents, (ConfigSyncMode)0); ConfigEntry<bool>[] array = new ConfigEntry<bool>[4] { wolfHare, seafood, oceanFish, henSeeds }; foreach (ConfigEntry<bool> val in array) { ((ConditionalConfigSync)sync).AddConfigEntry<bool>(val, (ConfigSyncMode)0); val.SettingChanged += Changed; } enabledFeature.SettingChanged += Changed; herbivores.SettingChanged += Changed; extraFoods.SettingChanged += Changed; meatEquivalents.SettingChanged += Changed; ReadSettings(); try { Assembly assembly = ((object)Chainloader.PluginInfos["MagicMike.CoreWoodExtras"].Instance).GetType().Assembly; using (FileStream inputStream = File.OpenRead(assembly.Location)) { using SHA256 sHA = SHA256.Create(); string text = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", ""); if (text != "EBEE48CF9F4675FCEE70FDBE6CC3768D4E9CFC564613BDE4CFFC2EF3A47EB62F" && text != "D8017BEA62F3201C693B5D6B5BFB5E219DA1E0E637CC0399BBC362A3C94540D2") { throw new NotSupportedException("CoreWoodExtras binary differs from the reviewed 2.2.7 / 2.2.8 releases."); } } Type type = assembly.GetType("CoreWoodExtras.AddDecor", throwOnError: true); troughFoods = (HashSet<string>)AccessTools.Field(type, "troughItems").GetValue(null); MethodInfo methodInfo = AccessTools.Method(assembly.GetType("CoreWoodExtras.Containers.UtilityMethods", throwOnError: true), "TryFeedAnimal", new Type[3] { typeof(MonsterAI), typeof(Tameable), typeof(Character) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(assembly.GetType("CoreWoodExtras.Containers.TroughAutoCollect", throwOnError: true), "CollectNearbyItems", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null || troughFoods == null || !FoodRules.OriginalFoods.All(troughFoods.Contains)) { throw new NotSupportedException("Reviewed trough contracts were not found."); } harmony = new Harmony("vercadi.TroughFoodCompatibility"); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "BeforeFeed", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MixedFoodTranspiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "AllowCollect", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, Last("SceneReady"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(MonsterAI), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, Last("AnimalReady"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("ACTIVE: reviewed CoreWoodExtras 2.2.7 / 2.2.8 trough food discovery and mixed-food filter registered. Auto collect=" + allowCollect.Value + ".")); } catch (Exception ex) { ready = false; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("INACTIVE: trough compatibility was not applied. " + ex)); } } private static HarmonyMethod Last(string name) { //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_0019: Expected O, but got Unknown return new HarmonyMethod(typeof(Plugin), name, (Type[])null) { priority = 0 }; } private void ReadSettings() { herbivoreNames = FoodRules.Parse(herbivores.Value); extraFoodNames = FoodRules.Parse(extraFoods.Value); revision++; } private void Changed(object sender, EventArgs args) { ReadSettings(); if (ready && Object.op_Implicit((Object)(object)ZNetScene.instance)) { RefreshScene(ZNetScene.instance); } if (!enabledFeature.Value) { RemoveWhitelistAdditions(); } } private void RemoveWhitelistAdditions() { if (troughFoods != null) { foreach (string addition in additions) { troughFoods.Remove(addition); } } additions.Clear(); } private static void SceneReady(ZNetScene __instance) { if (Object.op_Implicit((Object)(object)instance) && instance.ready) { instance.RemoveWhitelistAdditions(); instance.observedFoods.Clear(); instance.revision++; instance.RefreshScene(__instance); ((BaseUnityPlugin)instance).Logger.LogInfo((object)("Trough food discovery: " + instance.troughFoods.Count + " allowed item prefabs. Optional herbivore additions=" + instance.extraFoodNames.Count + ".")); } } private void RefreshScene(ZNetScene scene) { foreach (GameObject prefab in scene.m_prefabs) { if (Object.op_Implicit((Object)(object)prefab) && Object.op_Implicit((Object)(object)prefab.GetComponent<Tameable>())) { MonsterAI component = prefab.GetComponent<MonsterAI>(); if (Object.op_Implicit((Object)(object)component)) { DiscoverFoods(component); } } } if (enabledFeature.Value && Object.op_Implicit((Object)(object)ObjectDB.instance)) { foreach (string extraFoodName in extraFoodNames) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(extraFoodName); if (Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>())) { observedFoods.Add(extraFoodName); } } } if (!enabledFeature.Value) { return; } foreach (string observedFood in observedFoods) { AddAllowed(observedFood); } } private static void AnimalReady(MonsterAI __instance) { if (Object.op_Implicit((Object)(object)instance) && instance.ready) { instance.Observe(__instance); } } private static void BeforeFeed(MonsterAI __0) { if (Object.op_Implicit((Object)(object)instance) && instance.ready) { instance.Observe(__0); } } private void Observe(MonsterAI ai) { if (!Object.op_Implicit((Object)(object)ai) || !Object.op_Implicit((Object)(object)((Component)ai).GetComponent<Tameable>()) || ai.m_consumeItems == null) { return; } DietState state = dietStates.GetValue(ai, (MonsterAI key) => new DietState()); List<ItemDrop> list = ai.m_consumeItems.Where((ItemDrop x) => Object.op_Implicit((Object)(object)x) && (state.List != ai.m_consumeItems || !state.Added.Contains(x))).ToList(); string[] diet = list.Select((ItemDrop x) => FoodRules.Prefab(((Object)((Component)x).gameObject).name)).ToArray(); string text = FoodRules.Signature(diet); if (state.Revision != revision || state.List != ai.m_consumeItems || state.Signature != text) { state.Added.Clear(); state.Revision = revision; state.Signature = text; ai.m_consumeItems = list; if (enabledFeature.Value && Object.op_Implicit((Object)(object)ObjectDB.instance)) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (herbivoreNames.Contains(FoodRules.Prefab(((Object)((Component)ai).gameObject).name))) { hashSet.UnionWith(extraFoodNames); } if (meatEquivalents.Value) { hashSet.UnionWith(FoodRules.MeatEquivalents(diet)); } hashSet.UnionWith(FoodRules.AdditionalDiets(FoodRules.Prefab(((Object)((Component)ai).gameObject).name), diet, wolfHare.Value, seafood.Value, oceanFish.Value, henSeeds.Value)); foreach (string name in hashSet) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); ItemDrop val = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); if (Object.op_Implicit((Object)(object)val) && !ai.m_consumeItems.Any((ItemDrop x) => Object.op_Implicit((Object)(object)x) && FoodRules.Prefab(((Object)((Component)x).gameObject).name) == name)) { ai.m_consumeItems.Add(val); state.Added.Add(val); } } } state.List = ai.m_consumeItems; } DiscoverFoods(ai); } private void DiscoverFoods(MonsterAI ai) { if (ai.m_consumeItems == null) { return; } foreach (ItemDrop consumeItem in ai.m_consumeItems) { if (!Object.op_Implicit((Object)(object)consumeItem) || consumeItem.m_itemData?.m_shared == null) { continue; } string text = FoodRules.Prefab(((Object)((Component)consumeItem).gameObject).name); if (text.Length != 0) { observedFoods.Add(text); if (enabledFeature.Value) { AddAllowed(text); } } } if (enabledFeature.Value && meatEquivalents.Value && Object.op_Implicit((Object)(object)ObjectDB.instance)) { foreach (string item in FoodRules.MeatEquivalents(from x in ai.m_consumeItems where Object.op_Implicit((Object)(object)x) select FoodRules.Prefab(((Object)((Component)x).gameObject).name))) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item); if (Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>())) { AddAllowed(item); } } } if (!enabledFeature.Value || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return; } foreach (string item2 in FoodRules.AdditionalDiets(FoodRules.Prefab(((Object)((Component)ai).gameObject).name), from x in ai.m_consumeItems where Object.op_Implicit((Object)(object)x) select FoodRules.Prefab(((Object)((Component)x).gameObject).name), wolfHare.Value, seafood.Value, oceanFish.Value, henSeeds.Value)) { GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab(item2); if (Object.op_Implicit((Object)(object)itemPrefab2) && Object.op_Implicit((Object)(object)itemPrefab2.GetComponent<ItemDrop>())) { AddAllowed(item2); } } } private void AddAllowed(string name) { if (troughFoods.Add(name)) { additions.Add(name); } } private static bool AllowCollect() { if (Active) { return instance.allowCollect.Value; } return true; } internal static IEnumerable<CodeInstruction> MixedFoodTranspiler(IEnumerable<CodeInstruction> source) { List<CodeInstruction> list = source.ToList(); CodeInstruction[] array = list.Where((CodeInstruction i) => i.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(Enumerable) && methodInfo.Name == "Any" && methodInfo.GetParameters().Length == 2 && methodInfo.GetGenericArguments().SequenceEqual(new Type[1] { typeof(ItemData) })).ToArray(); if (array.Length != 1) { throw new NotSupportedException("Expected one mixed-inventory rejection query."); } array[0].opcode = OpCodes.Call; array[0].operand = AccessTools.Method(typeof(Plugin), "RejectMixedInventory", (Type[])null, (Type[])null); return list; } private static bool RejectMixedInventory(IEnumerable<ItemData> items, Func<ItemData, bool> predicate) { return FoodRules.RejectMixed(items, predicate, Active); } private void OnDestroy() { ready = false; RemoveWhitelistAdditions(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } } }