using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("KinkoCraft.StackableFood")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.10.0")] [assembly: AssemblyInformationalVersion("1.0.10+662e0fe809020726d34895866fd6ad385a3c9b55")] [assembly: AssemblyProduct("Stackable Food")] [assembly: AssemblyTitle("KinkoCraft.StackableFood")] [assembly: AssemblyVersion("1.0.10.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 KinkoCraft.StackableFood { internal static class PluginMeta { public const string Guid = "KinkoCraft.StackableFood"; public const string Name = "Stackable Food"; public const string Version = "1.0.10"; public const string ItemStackerFixGuid = "com.travv.aleandtale.itemstack99safe"; public const ushort ItemStackerFixCap = 99; } [BepInPlugin("KinkoCraft.StackableFood", "Stackable Food", "1.0.10")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class StackableFoodPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry StandaloneMaxStack; internal static ConfigEntry IncludeDrinks; private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; StandaloneMaxStack = ((BaseUnityPlugin)this).Config.Bind("General", "StandaloneMaxStack", 10, new ConfigDescription("How many food items stack in one inventory slot when ItemStackerFix is NOT installed. With ItemStackerFix installed this is ignored and the cap is 99.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 99), Array.Empty())); IncludeDrinks = ((BaseUnityPlugin)this).Config.Bind("General", "IncludeDrinks", true, "Make drinks (beer, juice, etc.) stackable too. Set to false to leave drinks unstacked."); Harmony val = new Harmony("KinkoCraft.StackableFood"); val.PatchAll(typeof(FoodStackPatch)); val.PatchAll(typeof(ServingStackFix)); val.PatchAll(typeof(WorldSourceStackFix)); Log.LogInfo((object)"Stackable Food v1.0.10 loaded."); } } public class FoodStackPatch { [HarmonyPatch(typeof(ItemManager), "Awake")] [HarmonyPostfix] private static void BumpFoodStacks(ItemManager __instance) { bool flag = Chainloader.PluginInfos.ContainsKey("com.travv.aleandtale.itemstack99safe"); ushort num = (ushort)(flag ? 99 : ((ushort)StackableFoodPlugin.StandaloneMaxStack.Value)); bool value = StackableFoodPlugin.IncludeDrinks.Value; ItemData[] array = __instance.itemDataHub?.itemData; if (array == null) { StackableFoodPlugin.Log.LogWarning((object)"itemDataHub.itemData was null; no food made stackable."); return; } int num2 = 0; ItemData[] array2 = array; foreach (ItemData val in array2) { if (!((Object)(object)val == (Object)null) && IsFood(val, value) && val.maxStack < num && val.maxStack <= 1) { val.maxStack = num; num2++; } } StackableFoodPlugin.Log.LogInfo((object)(string.Format("Stackable Food: cap={0} ({1}), ", num, flag ? "ItemStackerFix detected" : "standalone") + $"includeDrinks={value}, updated {num2} item(s).")); } private static bool IsFood(ItemData data, bool includeDrinks) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 Type type = data.type; if (type - 4 > 1) { if ((int)type == 6) { return includeDrinks; } return false; } return true; } } public class ServingStackFix { [ThreadStatic] private static bool _serveExecutePass; private static readonly FieldInfo RpcStageField = AccessTools.Field(typeof(NetworkBehaviour), "__rpc_exec_stage"); [HarmonyPatch(typeof(ServingTableSlot), "ServeSingleDishServerRpc")] [HarmonyTranspiler] private static IEnumerable Fix_ServeSingleDish(IEnumerable instructions) { return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemById", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneById", (Type[])null, (Type[])null), "ServeSingleDishServerRpc"); } [HarmonyPatch(typeof(TableFeedPlace), "ServeDish")] [HarmonyTranspiler] private static IEnumerable Fix_TableFeedServe(IEnumerable instructions) { return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemByDataId", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneByDataId", (Type[])null, (Type[])null), "TableFeedPlace.ServeDish"); } [HarmonyPatch(typeof(HelperWaiterServeDishState), "TryServeCustomerDish")] [HarmonyTranspiler] private static IEnumerable Fix_WaiterServe(IEnumerable instructions) { return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemByDataId", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneByDataId", (Type[])null, (Type[])null), "HelperWaiterServeDishState.TryServeCustomerDish"); } private static IEnumerable SwapCall(IEnumerable instructions, MethodInfo from, MethodInfo to, string where) { int swapped = 0; foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Call) && instruction.operand as MethodInfo == from) { yield return new CodeInstruction(OpCodes.Call, (object)to); swapped++; } else { yield return instruction; } } if (swapped == 0) { StackableFoodPlugin.Log.LogWarning((object)("ServingStackFix: '" + from.Name + "' call not found in " + where + "; stacked dishes may still be lost there.")); } } [HarmonyPatch(typeof(ServingTable), "ServeDishesServerRpc")] [HarmonyPrefix] private static void CaptureServeStage(ServingTable __instance) { _serveExecutePass = IsServerExecuteStage((NetworkBehaviour)(object)__instance); } [HarmonyPatch(typeof(ServingTable), "ServeDishesServerRpc")] [HarmonyPostfix] private static void FillRemainingSlots(ServingTable __instance, ServerRpcParams serverRpcParams) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) ContainerNet val = default(ContainerNet); if (!_serveExecutePass || !ContainerManager.Instance.GetPlayerContainer(serverRpcParams.Receive.SenderClientId, ref val)) { return; } ServingTableSlot val2 = default(ServingTableSlot); ItemData val4 = default(ItemData); while (__instance.HaveFreeSlot(ref val2)) { Item val3 = default(Item); bool flag = false; foreach (Item item in StringExtension.ToList(val.items)) { if (item.amount > 0 && ItemManager.Instance.GetItemData((uint)item.dataId, ref val4) && IsServable(val4.type)) { val3 = item; flag = true; break; } } if (flag && val.RemoveAmount(val3.dataId, (ushort)1)) { val2.itemDataId.Value = val3.dataId; val2.itemRarity = val3.rarity; continue; } break; } } private static bool IsServerExecuteStage(NetworkBehaviour nb) { object obj = RpcStageField?.GetValue(nb); if (obj != null) { return obj.ToString() == "Execute"; } return false; } internal static bool IsServable(Type type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)type != 5 && (int)type != 4) { return (int)type == 6; } return true; } [HarmonyPatch(typeof(ItemManager), "GetItemSellPrice")] [HarmonyPostfix] private static void FixFoodSellPrice(Item item, ItemData itemData, ref ushort __result) { //IL_000a: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0062: Invalid comparison between Unknown and I4 //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Invalid comparison between Unknown and I4 if ((Object)(object)itemData == (Object)null || !IsServable(itemData.type) || item.amount <= 1) { return; } float num = (int)itemData.price; if (itemData.hasRarity) { if (itemData.shopRarityPrices != null && ((UDictionary)(object)itemData.shopRarityPrices).Count > 0) { num = (int)((UDictionary)(object)itemData.shopRarityPrices)[item.rarity]; } else if ((int)item.rarity == 1) { num *= 1.2f; } else if ((int)item.rarity == 2) { num *= 1.6f; } else if ((int)item.rarity == 3) { num *= 2.2f; } else if ((int)item.rarity == 4) { num *= 3f; } } long num2 = (long)(int)num * (long)item.amount; __result = (ushort)((num2 > 65535) ? 65535 : num2); } public static bool RemoveOneById(ContainerNet cont, uint itemId) { //IL_0017: 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) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)cont == (Object)null) { return false; } Item val = default(Item); if (cont.GetItemById(itemId, ref val, false) && val.amount > 1) { val.amount--; cont.SetItem(val, val.order); Game.Instance.InvokeGameEvent((GameEvent)16, (uint)val.dataId, 1); return true; } return cont.RemoveItemById(itemId); } public static bool RemoveOneByDataId(ContainerNet cont, ushort dataId, out Item removed) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) removed = default(Item); if ((Object)(object)cont == (Object)null) { return false; } foreach (Item item in StringExtension.ToList(cont.items)) { if (item.dataId == dataId && item.amount > 0) { removed = item; removed.amount = 1; if (item.amount > 1) { Item val = item; val.amount--; cont.SetItem(val, val.order); Game.Instance.InvokeGameEvent((GameEvent)16, (uint)item.dataId, 1); } else { cont.RemoveItemById(item.id); } return true; } } return false; } } public class WorldSourceStackFix { [HarmonyPatch(typeof(CollectibleNet), "OnNetworkSpawn")] [HarmonyPrefix] private static void DetectFreshSource(CollectibleNet __instance, out bool __state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) __state = ((NetworkBehaviour)__instance).IsServer && __instance.item != null && __instance.item.Value.dataId == 0; } [HarmonyPatch(typeof(CollectibleNet), "OnNetworkSpawn")] [HarmonyPostfix] private static void ClampFreshSource(CollectibleNet __instance, bool __state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (__state && !((Object)(object)__instance.itemData == (Object)null) && ServingStackFix.IsServable(__instance.itemData.type)) { Item value = __instance.item.Value; if (value.amount > 1) { value.amount = 1; __instance.item.Value = value; } } } [HarmonyPatch(typeof(ItemCharger), "InteractServerRpc")] [HarmonyTranspiler] private static IEnumerable Fix_BarrelDispense(IEnumerable instructions) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown FieldInfo fieldInfo = AccessTools.Field(typeof(ItemData), "maxStack"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(Item), "amount"); MethodInfo methodInfo = AccessTools.Method(typeof(WorldSourceStackFix), "DispenseAmount", (Type[])null, (Type[])null); List list = new List(instructions); int num = SwapItemCtors(list); int num2 = 0; for (int i = 0; i < list.Count; i++) { if (list[i].opcode == OpCodes.Ldfld && list[i].operand as FieldInfo == fieldInfo && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Stfld && list[i + 1].operand as FieldInfo == fieldInfo2) { list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo); num2++; } } if (num == 0 && num2 == 0) { StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no dispense sites found in ItemCharger.InteractServerRpc; barrels may still hand out full stacks."); } return list; } [HarmonyPatch(typeof(ItemDispenser), "TakeServerRpc")] [HarmonyTranspiler] private static IEnumerable Fix_ItemDispenser(IEnumerable instructions) { List list = new List(instructions); if (SwapItemCtors(list) == 0) { StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no Item(itemData) construction found in ItemDispenser.TakeServerRpc; barrel dispensers may still hand out full stacks."); } return list; } [HarmonyPatch(typeof(ItemCombiner), "InteractServerRpc")] [HarmonyTranspiler] private static IEnumerable Fix_ItemCombiner(IEnumerable instructions) { List list = new List(instructions); if (SwapItemCtors(list) == 0) { StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no Item(itemData) construction found in ItemCombiner.InteractServerRpc; combine stations may still hand out full stacks."); } return list; } private static int SwapItemCtors(List code) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown ConstructorInfo constructorInfo = AccessTools.Constructor(typeof(Item), new Type[1] { typeof(ItemData) }, false); MethodInfo methodInfo = AccessTools.Method(typeof(WorldSourceStackFix), "MakeOne", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(WorldSourceStackFix), "InitOne", (Type[])null, (Type[])null); int num = 0; for (int i = 0; i < code.Count; i++) { if (!(code[i].operand as ConstructorInfo != constructorInfo)) { if (code[i].opcode == OpCodes.Newobj) { code[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo); num++; } else if (code[i].opcode == OpCodes.Call) { code[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2); num++; } } } return num; } public static Item MakeOne(ItemData data) { //IL_0026: 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) Item result = default(Item); ((Item)(ref result))..ctor(data); if ((Object)(object)data != (Object)null && ServingStackFix.IsServable(data.type)) { result.amount = 1; } return result; } public static void InitOne(ref Item item, ItemData data) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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) item = new Item(data); if ((Object)(object)data != (Object)null && ServingStackFix.IsServable(data.type)) { item.amount = 1; } } public static ushort DispenseAmount(ItemData data) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)data == (Object)null) { return 1; } if (!ServingStackFix.IsServable(data.type)) { return data.maxStack; } return 1; } } }