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 ReliableCooking v0.1.2
BepInEx/plugins/ReliableCooking/ReliableCooking.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using ReliableStationSafety; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyMetadata("AI_Assisted_Creation", "Coded with the assistance of ChatGPT 6.0; packaged with OpenAI Codex.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "OpenAI")] [assembly: AssemblyVersion("0.0.0.0")] namespace ReliableCooking { [BepInIncompatibility("aedenthorn.CookingStationTweaks")] [BepInPlugin("au.tyvalheim.reliablecooking", "Reliable Cooking", "0.1.2")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(CookingStation), "CookItem")] internal static class InputPatch { [HarmonyPriority(0)] private static bool Prefix(CookingStation __instance, Humanoid __0, ItemData __1, ref bool __result, bool __runOriginal, out FoodAttempt __state) { __state = null; if (!__runOriginal) { return false; } Plugin instance = Instance; Player val = (Player)(object)((__0 is Player) ? __0 : null); if ((Object)(object)instance == (Object)null || !instance.Enabled.Value || (Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (!Rules.Food(((Object)__instance).name) && !(((Object)__instance).name.Replace("(Clone)", "").Trim() == "piece_FrostFoundry"))) { return true; } __result = false; ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (instance.inputFaulted) { instance.InputMessage(val, "Inputs paused after an error; restart before retrying. No new input consumed."); return false; } try { if (Rules.Food(((Object)__instance).name) && (Object)(object)component != (Object)null && component.IsValid() && component.HasOwner() && !component.IsOwner()) { string message; bool result = RemoteFoodInput.Begin(__instance, val, __1, out __state, out message); if (message != null) { instance.InputMessage(val, message); } return result; } if (!CanWrite(__instance, component, val)) { instance.InputMessage(val, "Station is remote-owned, loading or protected. Food kept in your inventory; cooking settings apply when this client owns it."); return false; } if (__1 == null || !Object.op_Implicit((Object)(object)__1.m_dropPrefab)) { return false; } foreach (ItemMessage incompatibleItem in __instance.m_incompatibleItems) { if (Object.op_Implicit((Object)(object)incompatibleItem.m_item) && incompatibleItem.m_item.m_itemData.m_shared.m_name == __1.m_shared.m_name) { return true; } } if (!(bool)AllowedInput.Invoke(__instance, new object[1] { __1 })) { return true; } int num = (int)FreeSlot.Invoke(__instance, null); Inventory inventory = ((Humanoid)val).GetInventory(); if (num < 0 || !inventory.ContainsItem(__1) || __1.m_stack <= 0) { return false; } ZDO zDO = component.GetZDO(); string text = "slot" + num; if (zDO.GetString(text, "") != "" || !CanWrite(__instance, component, val)) { return false; } List<ItemData> allItems = inventory.GetAllItems(); int val2 = allItems.IndexOf(__1); int stack = __1.m_stack; __1.m_stack--; if (__1.m_stack == 0) { allItems.Remove(__1); } try { if (!CanWrite(__instance, component, val)) { throw new InvalidOperationException("Ownership changed before station input."); } AddInput.Invoke(__instance, new object[3] { ZDOMan.GetSessionID(), ((Object)__1.m_dropPrefab).name, __1.m_cheated }); if (zDO.GetString(text, "") == "") { throw new InvalidOperationException("Station did not accept its input."); } } catch { if (zDO.GetString(text, "") == "") { __1.m_stack = stack; if (!allItems.Contains(__1)) { allItems.Insert(Math.Min(val2, allItems.Count), __1); } } InventoryChanged.Invoke(inventory, new object[2] { false, false }); throw; } InventoryChanged.Invoke(inventory, new object[2] { false, false }); __result = true; } catch (Exception ex) { instance.inputFaulted = true; ((BaseUnityPlugin)instance).Logger.LogError((object)("Station inputs paused until restart after error; inspect inventory and station before retrying. " + ex)); } return false; } private static Exception Finalizer(Exception __exception, FoodAttempt __state) { RemoteFoodInput.Finish(__state); return __exception; } } [HarmonyPatch(typeof(CookingStation), "Awake")] internal static class SetupPatch { [HarmonyPriority(800)] private static void Prefix(CookingStation __instance) { Plugin instance = Instance; if ((Object)(object)instance == (Object)null || !instance.Enabled.Value) { return; } try { Configure(__instance, instance.SlotMultiplier.Value, instance.CookTimeMultiplier.Value, instance.CastTimeMultiplier.Value, instance.PreventBurning.Value); } catch (Exception ex) { ((Component)__instance).gameObject.AddComponent<ConfigurationFailed>(); ((BaseUnityPlugin)instance).Logger.LogError((object)("Station customization skipped; original station arrays retained and native initialization continues. Station=" + ((Object)__instance).name + "; " + ex)); } } } [HarmonyPatch(typeof(CookingStation), "UpdateCooking")] internal static class CompletePatch { private static void Postfix(CookingStation __instance, ZNetView ___m_nview) { //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) Plugin instance = Instance; if ((Object)(object)instance == (Object)null || instance.outputFaulted || !instance.Enabled.Value || (Object)(object)((Component)__instance).GetComponent<ConfigurationFailed>() != (Object)null || !Object.op_Implicit((Object)(object)___m_nview) || !___m_nview.IsValid() || !___m_nview.IsOwner()) { return; } bool flag = Rules.Food(((Object)__instance).name); bool flag2 = ((Object)__instance).name.Replace("(Clone)", "").Trim() == "piece_FrostFoundry"; if (((!flag || !instance.AutoPop.Value) && (!flag2 || !instance.AutoPopCasts.Value)) || (__instance.m_recordCrafter && (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead()))) { return; } try { for (int i = 0; i < __instance.m_slots.Length; i++) { if (!(bool)Done.Invoke(__instance, null)) { break; } if (!___m_nview.IsValid()) { break; } if (!___m_nview.IsOwner()) { break; } ZDO zDO = ___m_nview.GetZDO(); int num = -1; for (int j = 0; j < __instance.m_slots.Length; j++) { string text = zDO.GetString("slot" + j, ""); if (text != "" && (bool)ItemDone.Invoke(__instance, new object[1] { text })) { num = j; break; } } if (num >= 0 && ___m_nview.IsValid() && ___m_nview.IsOwner()) { Remove.Invoke(__instance, new object[3] { 0L, ((Component)__instance).transform.position + ((Component)__instance).transform.forward, 1 }); if (zDO.GetString("slot" + num, "") != "") { throw new InvalidOperationException("Native output did not clear the completed slot; automatic output will not retry."); } continue; } break; } } catch (Exception ex) { instance.outputFaulted = true; ((BaseUnityPlugin)instance).Logger.LogError((object)("Automatic output paused until restart after exception; inspect station before retrying. Configuration was not changed. " + ex)); } } } public const string Id = "au.tyvalheim.reliablecooking"; public const string Version = "0.1.2"; private bool inputFaulted; private float nextInputMessage; private static readonly MethodInfo AddInput = AccessTools.Method(typeof(CookingStation), "RPC_AddItem", (Type[])null, (Type[])null); private static readonly MethodInfo FreeSlot = AccessTools.Method(typeof(CookingStation), "GetFreeSlot", (Type[])null, (Type[])null); private static readonly MethodInfo AllowedInput = AccessTools.Method(typeof(CookingStation), "IsItemAllowed", new Type[1] { typeof(ItemData) }, (Type[])null); private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); internal static Plugin Instance; internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> PreventBurning; internal ConfigEntry<bool> AutoPop; internal ConfigEntry<bool> AutoPopCasts; internal ConfigEntry<float> SlotMultiplier; internal ConfigEntry<float> CookTimeMultiplier; internal ConfigEntry<float> CastTimeMultiplier; private Harmony harmony; private bool outputFaulted; private static readonly MethodInfo Done = AccessTools.Method(typeof(CookingStation), "HaveDoneItem", (Type[])null, (Type[])null); private static readonly MethodInfo ItemDone = AccessTools.Method(typeof(CookingStation), "IsItemDone", (Type[])null, (Type[])null); private static readonly MethodInfo Remove = AccessTools.Method(typeof(CookingStation), "RPC_RemoveDoneItem", (Type[])null, (Type[])null); private void InputMessage(Player player, string message) { if (!(Time.unscaledTime < nextInputMessage)) { nextInputMessage = Time.unscaledTime + 1.5f; ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Input] " + message)); ((Character)player).Message((MessageType)1, "Reliable Cooking: " + message, 0, (Sprite)null, false); } } private static bool CanWrite(CookingStation station, ZNetView view, Player player) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)view != (Object)null && view.IsValid() && view.IsOwner() && !((Character)player).IsDead() && !((Character)player).IsTeleporting()) { return PrivateArea.CheckAccess(((Component)station).transform.position, 0f, false, true); } return false; } private void Awake() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable at startup. Restart after changes; empty extra cooking slots before reducing capacity/removing the mod."); PreventBurning = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "PreventBurning", true, "Keep cooked food from burning on supported food stations."); AutoPop = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "AutoPop", true, "Drop all completed food beside locally owned stations in the same update using Valheim's native output handler."); SlotMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "SlotMultiplier", 1f, new ConfigDescription("Default keeps vanilla capacity for mixed-mod groups. Opt-in extra food slots require matching settings on every potential owner, including server. Never resize the Frost Foundry. Empty extra slots before reducing; restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 8f), new object[0])); CookTimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Food", "CookTimeMultiplier", 1f, new ConfigDescription("Food cooking-time multiplier. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), new object[0])); CastTimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Foundry", "CastTimeMultiplier", 1f, new ConfigDescription("Frost Foundry processing-time multiplier. 1 preserves native timing and fuel cost; changing time changes fuel consumed. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), new object[0])); AutoPopCasts = ((BaseUnityPlugin)this).Config.Bind<bool>("Foundry", "AutoPopCasts", false, "Opt in to dropping completed casts beside the locally owned foundry. Requires a living local player for native crafter attribution. Default leaves gear on the foundry."); try { if (Done == null || Remove == null || ItemDone == null || AddInput == null || FreeSlot == null || AllowedInput == null || InventoryChanged == null) { throw new MissingMethodException("CookingStation output API unavailable"); } harmony = new Harmony("au.tyvalheim.reliablecooking"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Reliable Cooking 0.1.2 loaded. Food slots are expandable; Frost Foundry keeps its native slot and switches. Inspired by aedenthorn's Cooking Station Tweaks."); } catch (Exception ex) { if (harmony != null) { harmony.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal static void Configure(CookingStation station, float slots, float foodTime, float castTime, bool noBurn) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((Component)station).GetComponent<Configured>() != (Object)null) { return; } bool flag = Rules.Food(((Object)station).name); bool flag2 = ((Object)station).name.Replace("(Clone)", "").Trim() == "piece_FrostFoundry"; if (!flag && !flag2) { return; } List<ItemConversion> list = new List<ItemConversion>(); foreach (ItemConversion item in station.m_conversion) { ItemConversion val = new ItemConversion(); val.m_from = item.m_from; val.m_to = item.m_to; val.m_cookTime = Rules.Duration(item.m_cookTime, flag ? foodTime : castTime); list.Add(val); } int num = ((station.m_slots != null) ? station.m_slots.Length : 0); int num2 = Rules.Slots(num, slots, flag && !station.m_recordCrafter && !station.m_moveObjectWhileCooking); Transform[] slots2 = station.m_slots; ParticleSystem[] donePS = station.m_donePS; ParticleSystem[] burntPS = station.m_burntPS; List<GameObject> list2 = new List<GameObject>(); try { if (num2 > num) { Transform[] array = (Transform[])(object)new Transform[num2]; Array.Copy(station.m_slots, array, num); for (int i = num; i < num2; i++) { Transform val2 = station.m_slots[(i - num) % num]; Transform transform = Object.Instantiate<GameObject>(((Component)val2).gameObject, val2.parent, false).transform; list2.Add(((Component)transform).gameObject); ((Object)transform).name = "ReliableCooking_slot_" + i; float num3 = ((float)(i - num) + 1f) / ((float)(num2 - num) + 1f); transform.position = Vector3.Lerp(station.m_slots[0].position, station.m_slots[num - 1].position, num3); array[i] = transform; } slots2 = array; donePS = Expand(station.m_donePS, array, list2); burntPS = Expand(station.m_burntPS, array, list2); } ((Component)station).gameObject.AddComponent<Configured>(); station.m_slots = slots2; station.m_donePS = donePS; station.m_burntPS = burntPS; station.m_conversion = list; if (flag && noBurn) { station.m_canOvercookItems = false; } } catch { foreach (GameObject item2 in list2) { if (Object.op_Implicit((Object)(object)item2)) { item2.SetActive(false); Object.Destroy((Object)(object)item2); } } throw; } } private static ParticleSystem[] Expand(ParticleSystem[] source, Transform[] slots, List<GameObject> created) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (source == null || source.Length == 0) { return source; } ParticleSystem[] array = (ParticleSystem[])(object)new ParticleSystem[slots.Length]; for (int i = 0; i < slots.Length; i++) { if (i < source.Length) { array[i] = source[i]; continue; } int num = i % source.Length; GameObject val = Object.Instantiate<GameObject>(((Component)source[num]).gameObject, ((Component)source[num]).transform.parent, false); created.Add(val); ((Object)val).name = "ReliableCooking_effect_" + i; val.transform.position = slots[i].position + ((Component)source[num]).transform.position - slots[num].position; array[i] = val.GetComponent<ParticleSystem>(); } return array; } } public sealed class Configured : MonoBehaviour { } public sealed class ConfigurationFailed : MonoBehaviour { } internal static class Rules { internal static bool Food(string name) { name = (name ?? "").Replace("(Clone)", "").Trim(); if (!(name == "piece_cookingstation") && !(name == "piece_cookingstation_iron")) { return name == "piece_oven"; } return true; } internal static int Slots(int original, float multiplier, bool food) { if (!food || original < 2 || float.IsNaN(multiplier) || float.IsInfinity(multiplier)) { return original; } return Math.Max(original, Math.Min(32, (int)Math.Round((float)original * Math.Max(1f, multiplier)))); } internal static float Duration(float original, float multiplier) { if (original <= 0f || float.IsNaN(original) || float.IsInfinity(original)) { return original; } if (float.IsNaN(multiplier) || float.IsInfinity(multiplier)) { return original; } return Math.Max(1f, original * Math.Max(0.05f, Math.Min(10f, multiplier))); } } } namespace ReliableStationSafety { public sealed class PendingFood : MonoBehaviour { internal bool Waiting; internal string Input; internal string Output; internal long Owner; internal string[] Before; internal float Started; internal int AcceptedSlot = -1; internal void Observe(long sender, int slot, string item) { if (Waiting && sender == Owner && slot >= 0 && slot < Before.Length) { if (Before[slot] == "" && (item == Input || item == Output)) { AcceptedSlot = slot; } if (slot == AcceptedSlot && item == "") { Waiting = false; } } } internal bool Confirmed(ZDO state) { if (!Waiting) { return true; } for (int i = 0; i < Before.Length; i++) { string text = state.GetString("slot" + i, ""); if (Before[i] == "" && (text == Input || text == Output)) { Waiting = false; return true; } } return false; } } internal sealed class FoodAttempt { internal PendingFood Pending; internal Inventory Inventory; internal string Name; internal int Count; } internal static class RemoteFoodInput { private const string CapacityKey = "reliable_workstations_food_capacity"; private const string OwnerKey = "reliable_workstations_food_capacity_owner"; internal static int NativeCapacity(string name) { name = (name ?? "").Replace("(Clone)", "").Trim(); return name switch { "piece_cookingstation" => 2, "piece_cookingstation_iron" => 5, "piece_oven" => 4, _ => 0, }; } internal static void Publish(CookingStation station) { if (NativeCapacity(((Object)station).name) != 0 && station.m_slots != null) { ZNetView component = ((Component)station).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); zDO.Set("reliable_workstations_food_capacity", station.m_slots.Length); zDO.Set("reliable_workstations_food_capacity_owner", zDO.GetOwner()); } } } internal static void ObserveSnapshot(CookingStation station) { PendingFood component = ((Component)station).GetComponent<PendingFood>(); if (!((Object)(object)component == (Object)null) && component.Waiting) { ZNetView component2 = ((Component)station).GetComponent<ZNetView>(); if ((Object)(object)component2 != (Object)null && component2.IsValid()) { component.Confirmed(component2.GetZDO()); } } } internal static bool Begin(CookingStation station, Player player, ItemData item, out FoodAttempt attempt, out string message) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) attempt = null; message = null; ZNetView component = ((Component)station).GetComponent<ZNetView>(); if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || !((Humanoid)player).GetInventory().ContainsItem(item) || item.m_stack <= 0 || (Object)(object)component == (Object)null || !component.IsValid() || !component.HasOwner() || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || !PrivateArea.CheckAccess(((Component)station).transform.position, 0f, false, true)) { return false; } ZDO zDO = component.GetZDO(); PendingFood pendingFood = ((Component)station).GetComponent<PendingFood>(); if ((Object)(object)pendingFood == (Object)null) { pendingFood = ((Component)station).gameObject.AddComponent<PendingFood>(); } if (!pendingFood.Confirmed(zDO)) { if (Time.unscaledTime - pendingFood.Started > 3f) { message = "Waiting for the station's previous food input to appear. Remaining food is still in your inventory."; } return false; } int num = NativeCapacity(((Object)station).name); int num2 = zDO.GetInt("reliable_workstations_food_capacity", 0); if (zDO.GetLong("reliable_workstations_food_capacity_owner", 0L) == zDO.GetOwner() && num2 >= num && num2 <= 32) { num = num2; } num = Math.Min(num, station.m_slots.Length); string[] array = new string[num]; bool flag = false; for (int i = 0; i < num; i++) { array[i] = zDO.GetString("slot" + i, ""); if (array[i] == "") { flag = true; } } if (!flag) { message = "The station's confirmed cooking slots are full. Remaining food is still in your inventory."; return false; } string text = null; foreach (ItemConversion item2 in station.m_conversion) { if ((Object)(object)item2.m_from != (Object)null && ((Object)item2.m_from).name == ((Object)item.m_dropPrefab).name) { text = (((Object)(object)item2.m_to != (Object)null) ? ((Object)item2.m_to).name : null); break; } } if (text == null) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); pendingFood.Input = ((Object)item.m_dropPrefab).name; pendingFood.Output = text; pendingFood.Before = array; pendingFood.Owner = zDO.GetOwner(); pendingFood.Started = Time.unscaledTime; pendingFood.AcceptedSlot = -1; pendingFood.Waiting = true; attempt = new FoodAttempt { Pending = pendingFood, Inventory = inventory, Name = item.m_shared.m_name, Count = inventory.CountItems(item.m_shared.m_name, -1, true) }; return true; } internal static void Finish(FoodAttempt attempt) { if (attempt != null && (Object)(object)attempt.Pending != (Object)null && attempt.Inventory.CountItems(attempt.Name, -1, true) >= attempt.Count) { attempt.Pending.Waiting = false; } } } [HarmonyPatch(typeof(CookingStation), "CookItem")] internal static class FoodInputObservation { [HarmonyPriority(801)] private static void Prefix(CookingStation __instance) { RemoteFoodInput.ObserveSnapshot(__instance); } } [HarmonyPatch(typeof(CookingStation), "RPC_SetSlotVisual")] internal static class FoodVisualObservation { private static void Postfix(CookingStation __instance, long __0, int __1, string __2) { PendingFood component = ((Component)__instance).GetComponent<PendingFood>(); if ((Object)(object)component != (Object)null) { component.Observe(__0, __1, __2); } } } [HarmonyPatch(typeof(CookingStation), "UpdateCooking")] internal static class FoodCapacityAdvertisement { private static void Prefix(CookingStation __instance) { RemoteFoodInput.ObserveSnapshot(__instance); } private static void Postfix(CookingStation __instance) { RemoteFoodInput.Publish(__instance); } } }