Decompiled source of AutoOven v1.0.15

AutoOven.dll

Decompiled 2 months 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 BepInEx;
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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MrGay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AutoOven")]
[assembly: AssemblyTitle("AutoOven")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 AutoOven
{
	[BepInPlugin("mrgay.autoven", "Auto Oven", "1.0.15")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class AutoOvenPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(CookingStation), "UpdateCooking")]
		private static class CookingStationUpdatePatch
		{
			private static void Prefix(CookingStation __instance)
			{
				instance?.ProcessCookingStation(__instance);
			}
		}

		private sealed class PersistedContainer
		{
			public ZDO Zdo { get; }

			public Container Prefab { get; }

			public PersistedContainer(ZDO zdo, Container prefab)
			{
				Zdo = zdo;
				Prefab = prefab;
			}
		}

		private sealed class OvenSlots
		{
			public static bool HasFreeSlot(CookingStation oven)
			{
				return FindFreeSlot(oven) >= 0;
			}

			public static int FindFreeSlot(CookingStation oven)
			{
				for (int i = 0; i < oven.m_slots.Length; i++)
				{
					if (!TryGetSlot(oven, i, out string _, out object _))
					{
						return i;
					}
				}
				return -1;
			}

			public static bool SlotContainsItem(CookingStation oven, int slot, string itemName)
			{
				if (TryGetSlot(oven, slot, out string itemName2, out object _))
				{
					return itemName2 == itemName;
				}
				return false;
			}

			public static bool TryTakeFinished(CookingStation oven, out ItemData? item, out int slot)
			{
				item = null;
				slot = -1;
				for (int i = 0; i < oven.m_slots.Length; i++)
				{
					if (TryGetSlot(oven, i, out string itemName, out object status) && !(status?.ToString() != "Done"))
					{
						GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemName);
						ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
						if (!((Object)(object)val == (Object)null))
						{
							item = val.m_itemData.Clone();
							item.m_stack = 1;
							slot = i;
							return true;
						}
					}
				}
				return false;
			}

			public static bool RemoveFinished(CookingStation oven, int slot)
			{
				ZNetView component = ((Component)oven).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					return false;
				}
				component.ClaimOwnership();
				MethodInfo method = ((object)oven).GetType().GetMethod("SetSlot", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null)
				{
					return false;
				}
				object obj = Enum.Parse(method.GetParameters()[3].ParameterType, "NotDone");
				method.Invoke(oven, new object[5] { slot, "", 0f, obj, false });
				((object)oven).GetType().GetMethod("RPC_SetSlotVisual", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(oven, new object[3] { 0L, slot, "" });
				return true;
			}

			public static bool ContainsItem(CookingStation oven, string itemName)
			{
				for (int i = 0; i < oven.m_slots.Length; i++)
				{
					if (TryGetSlot(oven, i, out string itemName2, out object _) && itemName2 == itemName)
					{
						return true;
					}
				}
				return false;
			}

			private static bool TryGetSlot(CookingStation oven, int slot, out string itemName, out object? status)
			{
				itemName = "";
				status = null;
				MethodInfo method = ((object)oven).GetType().GetMethod("GetSlot", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null)
				{
					return false;
				}
				object[] array = new object[5] { slot, null, 0f, null, false };
				method.Invoke(oven, array);
				itemName = (array[1] as string) ?? "";
				status = array[3];
				return itemName.Length > 0;
			}
		}

		public const string PluginGuid = "mrgay.autoven";

		public const string PluginName = "Auto Oven";

		public const string PluginVersion = "1.0.15";

		private const string StoneOvenPrefabName = "piece_oven";

		private readonly List<CookingStation> ovens = new List<CookingStation>();

		private readonly Dictionary<int, float> nextStationScans = new Dictionary<int, float>();

		private ConfigEntry<float> scanDistance;

		private ConfigEntry<float> scanInterval;

		private ConfigEntry<bool> enforceServerConfigSync;

		private ConfigEntry<bool> logScanSummary;

		private ConfigEntry<bool> enableAutoFueling;

		private ConfigSync configSync;

		private float nextScan;

		private bool adapterWarningLogged;

		private int scanCount;

		private int persistedOvenCount;

		private int persistedChestCount;

		private int liveSkippedOvenCount;

		private int hookedStationCount;

		private int stoneOvenPrefabHash;

		private static AutoOvenPlugin? instance;

		private bool stoneOvenHashInitializationFailed;

		private void Awake()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			instance = this;
			scanDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Automation", "distance", 10f, "Maximum distance in meters between an oven and a chest used for input or output.");
			scanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Automation", "interval", 3f, "Seconds between server-side oven scans.");
			enforceServerConfigSync = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enforce_Server_Config_Sync", true, "When enabled, the server controls the automation settings for connected clients.");
			logScanSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Log_Scan_Summary", false, "When enabled, periodically log the number of active ovens found by Auto Oven.");
			enableAutoFueling = ((BaseUnityPlugin)this).Config.Bind<bool>("Automation", "Enable_Auto_Fueling", true, "When enabled, nearby chests supply the oven's configured fuel item automatically. Required for unattended ovens to actually finish cooking and free up their slots.");
			configSync = new ConfigSync("mrgay.autoven")
			{
				DisplayName = "Auto Oven",
				CurrentVersion = "1.0.15",
				MinimumRequiredVersion = "1.0.15",
				ModRequired = true
			};
			((ConditionalConfigSync)configSync).AllowClientConfigUpdatesWhenUnlocked = true;
			((ConditionalConfigSync)configSync).AddLockingConfigEntry<bool>(enforceServerConfigSync);
			((ConditionalConfigSync)configSync).AddConfigEntry<bool>(logScanSummary, (ConfigSyncMode)0);
			((ConditionalConfigSync)configSync).AddConfigEntry<bool>(enableAutoFueling, (ConfigSyncMode)0);
			((ConditionalConfigSync)configSync).AddConfigEntry<float>(scanDistance, (ConfigSyncMode)1, true);
			((ConditionalConfigSync)configSync).AddConfigEntry<float>(scanInterval, (ConfigSyncMode)1, true);
			Harmony.CreateAndPatchAll(typeof(AutoOvenPlugin).Assembly, "mrgay.autoven");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Auto Oven 1.0.15 loaded. Automation runs on the server only.");
		}

		private void Update()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				InitializeStoneOvenHash();
				if (ZNet.instance.GetNrOfPlayers() > 0 && !(Time.time < nextScan))
				{
					nextScan = Time.time + Mathf.Max(0.25f, scanInterval.Value);
					ScanOvens();
				}
			}
		}

		private void ScanOvens()
		{
			scanCount++;
			ovens.Clear();
			ovens.AddRange(Object.FindObjectsOfType<CookingStation>());
			foreach (CookingStation oven in ovens)
			{
				if (!((Object)(object)oven == (Object)null) && ((Behaviour)oven).isActiveAndEnabled)
				{
					ProcessCookingStation(oven);
				}
			}
			ProcessPersistedCookingStations();
			LogPlayerProximityDiagnostics();
			if (logScanSummary.Value && scanCount % 20 == 0)
			{
				int num = GetAllPersistedZdos().Count((ZDO zdo) => zdo.GetPrefab() == stoneOvenPrefabHash);
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Scan {scanCount}: found {ovens.Count} active Cooking Stations; CookingStation update hook processed {hookedStationCount} stations. " + $"Persisted: {persistedOvenCount} oven(s) processed, {liveSkippedOvenCount} skipped as live, {num} total Stone Oven ZDO(s) in world, " + $"{persistedChestCount} chest(s), Enable_Auto_Fueling={enableAutoFueling.Value}."));
				hookedStationCount = 0;
			}
		}

		private void LogPlayerProximityDiagnostics()
		{
			//IL_0090: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if (!logScanSummary.Value || scanCount % 5 != 0 || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			List<PlayerInfo> playerList = ZNet.instance.GetPlayerList();
			if (playerList.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Diagnostics: ZNet.GetPlayerList() returned 0 players server-side.");
				return;
			}
			foreach (ZDO allPersistedZdo in GetAllPersistedZdos())
			{
				if (allPersistedZdo.GetPrefab() != stoneOvenPrefabHash)
				{
					continue;
				}
				float num = float.MaxValue;
				foreach (PlayerInfo item in playerList)
				{
					float num2 = Vector3.Distance(item.m_position, allPersistedZdo.GetPosition());
					if (num2 < num)
					{
						num = num2;
					}
				}
				bool flag = ZNetScene.instance.HaveInstance(allPersistedZdo);
				GameObject val = (flag ? ZNetScene.instance.FindInstance(allPersistedZdo.m_uid) : null);
				bool flag2 = (Object)(object)val != (Object)null && (Object)(object)val.GetComponentInChildren<CookingStation>(true) != (Object)null;
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Diagnostics: Stone Oven {allPersistedZdo.m_uid} at {allPersistedZdo.GetPosition()} - nearest player {num:F1}m away, " + $"ZNetScene.HaveInstance={flag}, hasLiveCookingStation={flag2}."));
			}
		}

		private void ProcessCookingStation(CookingStation oven)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)oven == (Object)null || !((Behaviour)oven).isActiveAndEnabled)
			{
				return;
			}
			ZNetView component = ((Component)oven).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || stoneOvenPrefabHash == 0 || component.GetZDO().GetPrefab() != stoneOvenPrefabHash)
			{
				return;
			}
			int instanceID = ((Object)oven).GetInstanceID();
			if (!nextStationScans.TryGetValue(instanceID, out var value) || !(Time.time < value))
			{
				nextStationScans[instanceID] = Time.time + Mathf.Max(0.25f, scanInterval.Value);
				hookedStationCount++;
				List<Container> list = FindNearbyChests(((Component)oven).transform.position);
				if (logScanSummary.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Processing Stone Oven '{((Object)oven).name}' at {((Component)oven).transform.position}: found {list.Count} nearby chest(s) within {scanDistance.Value}m.");
				}
				UnloadFinishedItems(oven, list);
				if (enableAutoFueling.Value)
				{
					FuelOven(oven, list);
				}
				LoadBakeableItems(oven, list);
			}
		}

		private void InitializeStoneOvenHash()
		{
			if (stoneOvenPrefabHash == 0 && !stoneOvenHashInitializationFailed && !((Object)(object)ZNetScene.instance == (Object)null))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("piece_oven");
				CookingStation val = ((prefab != null) ? prefab.GetComponentInChildren<CookingStation>(true) : null);
				if ((Object)(object)val == (Object)null)
				{
					stoneOvenHashInitializationFailed = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not verify Stone Oven prefab 'piece_oven'. Stone Oven automation remains disabled.");
				}
				else
				{
					stoneOvenPrefabHash = ZNetScene.instance.GetPrefabHash(prefab);
					((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("Detected Stone Oven prefab '{0}' with hash {1} for this server session. ", "piece_oven", stoneOvenPrefabHash) + $"m_requireFire={val.m_requireFire}, m_useFuel={val.m_useFuel}, m_useFueldWhileEmpty={val.m_useFueldWhileEmpty}, " + string.Format("m_fuelItem={0}, m_maxFuel={1}, m_secPerFuel={2}, ", ((Object)(object)val.m_fuelItem != (Object)null) ? ((Object)val.m_fuelItem).name : "null", val.m_maxFuel, val.m_secPerFuel) + $"slots={val.m_slots.Length}."));
				}
			}
		}

		private void ProcessPersistedCookingStations()
		{
			if (stoneOvenPrefabHash == 0 || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			List<PersistedContainer> list = FindPersistedContainers();
			persistedOvenCount = 0;
			persistedChestCount = list.Count;
			liveSkippedOvenCount = 0;
			foreach (ZDO oven in GetAllPersistedZdos())
			{
				if (oven.GetPrefab() != stoneOvenPrefabHash)
				{
					continue;
				}
				if (IsCookingStationLive(oven))
				{
					liveSkippedOvenCount++;
					continue;
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(stoneOvenPrefabHash);
				CookingStation val = ((prefab != null) ? prefab.GetComponentInChildren<CookingStation>(true) : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				List<PersistedContainer> chests = (from container in list
					where Vector3.Distance(oven.GetPosition(), container.Zdo.GetPosition()) <= Mathf.Max(0f, scanDistance.Value)
					orderby Vector3.Distance(oven.GetPosition(), container.Zdo.GetPosition())
					select container).ToList();
				if (ClaimZdoOwnership(oven))
				{
					persistedOvenCount++;
					LogPersistedCookingStationState(oven, val);
					AdvancePersistedCooking(oven, val);
					UnloadPersistedCookingStationItems(oven, val, chests);
					if (enableAutoFueling.Value)
					{
						FuelPersistedCookingStation(oven, val, chests);
					}
					LoadPersistedCookingStationItems(oven, val, chests);
				}
			}
		}

		private void AdvancePersistedCooking(ZDO oven, CookingStation station)
		{
			DateTime time = ZNet.instance.GetTime();
			DateTime dateTime = new DateTime(oven.GetLong(ZDOVars.s_startTime, time.Ticks));
			float num = (float)(time - dateTime).TotalSeconds;
			oven.Set(ZDOVars.s_startTime, time.Ticks);
			if (num <= 0f)
			{
				return;
			}
			float num2 = oven.GetFloat(ZDOVars.s_fuel, 0f);
			bool flag = HasUncookedPersistedItem(oven, station);
			if (station.m_useFuel && station.m_secPerFuel > 0)
			{
				num2 = Mathf.Max(0f, num2 - num / (float)station.m_secPerFuel);
				oven.Set(ZDOVars.s_fuel, num2);
			}
			bool requireFire = station.m_requireFire;
			bool flag2 = station.m_useFuel && num2 > 0f && (station.m_useFueldWhileEmpty || flag);
			bool flag3 = requireFire || flag2;
			if (logScanSummary.Value && scanCount % 20 == 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Persisted Stone Oven fuel={num2:F2}/{station.m_maxFuel}, hasUncookedItem={flag}, " + $"cooking={flag3} (fire={requireFire}, fuel={flag2}), Enable_Auto_Fueling={enableAutoFueling.Value}."));
			}
			if (!flag3)
			{
				if (logScanSummary.Value && scanCount % 20 == 0 && station.m_useFuel && num2 <= 0f)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Persisted Stone Oven has no fuel, so cooking cannot progress until fuel is added (manually or via Enable_Auto_Fueling).");
				}
				return;
			}
			for (int i = 0; i < station.m_slots.Length; i++)
			{
				string itemName = oven.GetString("slot" + i, "");
				if (itemName.Length == 0 || oven.GetInt("slotstatus" + i, 0) != 0)
				{
					continue;
				}
				ItemConversion val = ((IEnumerable<ItemConversion>)station.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion entry) => (Object)(object)entry.m_from != (Object)null && ((Object)((Component)entry.m_from).gameObject).name == itemName));
				if ((Object)(object)val?.m_from == (Object)null)
				{
					oven.Set("slot" + i, "");
					oven.Set("slot" + i, 0f);
					oven.Set("slotstatus" + i, 0);
					continue;
				}
				float num3 = oven.GetFloat("slot" + i, 0f) + num;
				if (station.m_canOvercookItems && (Object)(object)station.m_overCookedItem != (Object)null && num3 > val.m_cookTime * 2f)
				{
					oven.Set("slot" + i, ((Object)((Component)station.m_overCookedItem).gameObject).name);
					oven.Set("slot" + i, num3);
					oven.Set("slotstatus" + i, 2);
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"Persisted Stone Oven slot {i} burnt while unattended.");
					}
				}
				else if (num3 > val.m_cookTime && (Object)(object)val.m_to != (Object)null)
				{
					oven.Set("slot" + i, ((Object)((Component)val.m_to).gameObject).name);
					oven.Set("slot" + i, num3);
					oven.Set("slotstatus" + i, 1);
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"Persisted Stone Oven slot {i} finished cooking while unattended.");
					}
				}
				else
				{
					oven.Set("slot" + i, num3);
				}
			}
			ZDOMan.instance.SetDirtySector(oven);
		}

		private static bool HasUncookedPersistedItem(ZDO oven, CookingStation station)
		{
			for (int i = 0; i < station.m_slots.Length; i++)
			{
				if (oven.GetString("slot" + i, "").Length > 0 && oven.GetInt("slotstatus" + i, 0) == 0)
				{
					return true;
				}
			}
			return false;
		}

		private void UnloadPersistedCookingStationItems(ZDO oven, CookingStation station, List<PersistedContainer> chests)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < station.m_slots.Length; i++)
			{
				string text = oven.GetString("slot" + i, "");
				if (text.Length == 0 || oven.GetInt("slotstatus" + i, 0) == 0)
				{
					continue;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ItemData item = val.m_itemData.Clone();
				item.m_stack = 1;
				PersistedContainer persistedContainer = chests.FirstOrDefault(delegate(PersistedContainer chest)
				{
					Inventory val2 = LoadInventory(chest);
					return val2.HaveItem(item.m_shared.m_name, true) && val2.CanAddItem(item, -1);
				});
				if (persistedContainer == null)
				{
					persistedContainer = chests.FirstOrDefault((PersistedContainer chest) => LoadInventory(chest).CanAddItem(item, -1));
				}
				bool flag = persistedContainer != null && TryAddToPersistedContainer(persistedContainer, item);
				if (!flag)
				{
					ItemDrop.DropItem(item, item.m_stack, oven.GetPosition() + Vector3.up, Quaternion.identity);
				}
				if (logScanSummary.Value)
				{
					string arg = (flag ? $"chest {persistedContainer.Zdo.m_uid}" : "the ground fallback");
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Removed completed {text} from persisted Stone Oven slot {i} and stored it in {arg}.");
				}
				oven.Set("slot" + i, "");
				oven.Set("slot" + i, 0f);
				oven.Set("slotstatus" + i, 0);
				ZDOMan.instance.SetDirtySector(oven);
			}
		}

		private void FuelPersistedCookingStation(ZDO oven, CookingStation station, List<PersistedContainer> chests)
		{
			if ((Object)(object)station.m_fuelItem == (Object)null || station.m_maxFuel <= 0)
			{
				return;
			}
			string fuelName = station.m_fuelItem.m_itemData.m_shared.m_name;
			while (oven.GetFloat(ZDOVars.s_fuel, 0f) < (float)station.m_maxFuel)
			{
				PersistedContainer persistedContainer = chests.FirstOrDefault((PersistedContainer chest) => ClaimZdoOwnership(chest.Zdo) && LoadInventory(chest).GetItem(fuelName, -1, false) != null);
				if (persistedContainer == null)
				{
					break;
				}
				oven.Set(ZDOVars.s_fuel, oven.GetFloat(ZDOVars.s_fuel, 0f) + 1f);
				if (!TryRemoveFromPersistedContainer(persistedContainer, fuelName))
				{
					break;
				}
				ZDOMan.instance.SetDirtySector(oven);
			}
		}

		private void LoadPersistedCookingStationItems(ZDO oven, CookingStation station, List<PersistedContainer> chests)
		{
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			foreach (PersistedContainer chest in chests)
			{
				if (!ClaimZdoOwnership(chest.Zdo))
				{
					continue;
				}
				Inventory val = LoadInventory(chest);
				foreach (ItemData item in val.GetAllItems().ToList())
				{
					string itemName = ((Object)item.m_dropPrefab).name;
					if (!station.m_conversion.Any((ItemConversion conversion) => (Object)(object)conversion.m_from != (Object)null && ((Object)((Component)conversion.m_from).gameObject).name == itemName))
					{
						continue;
					}
					int stack = item.m_stack;
					for (int num = 0; num < stack; num++)
					{
						int num2 = FindFreePersistedCookingStationSlot(oven, station);
						if (num2 < 0)
						{
							return;
						}
						oven.Set("slot" + num2, itemName);
						oven.Set("slot" + num2, 0f);
						oven.Set("slotstatus" + num2, 0);
						oven.Set(ZDOVars.s_cheatedQueued + num2, item.m_cheated);
						ZDOMan.instance.SetDirtySector(oven);
						if (oven.GetString("slot" + num2, "") != itemName)
						{
							oven.Set("slot" + num2, "");
							oven.Set("slot" + num2, 0f);
							oven.Set("slotstatus" + num2, 0);
							continue;
						}
						val.RemoveItem(item, 1);
						SaveInventory(chest, val);
						if (logScanSummary.Value)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded {itemName} into persisted Stone Oven slot {num2} from chest {chest.Zdo.m_uid}.");
						}
					}
				}
			}
		}

		private void LogPersistedCookingStationState(ZDO oven, CookingStation station)
		{
			if (logScanSummary.Value && scanCount % 20 == 0)
			{
				string[] value = (from slot in Enumerable.Range(0, station.m_slots.Length)
					select string.Format("{0}='{1}'/{2:F2}/{3}", slot, oven.GetString("slot" + slot, ""), oven.GetFloat("slot" + slot, 0f), oven.GetInt("slotstatus" + slot, 0))).ToArray();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Persisted Stone Oven slots: " + string.Join(", ", value) + "."));
			}
		}

		private static int FindFreePersistedCookingStationSlot(ZDO oven, CookingStation station)
		{
			for (int i = 0; i < station.m_slots.Length; i++)
			{
				if (oven.GetString("slot" + i, "").Length == 0)
				{
					return i;
				}
			}
			return -1;
		}

		private List<PersistedContainer> FindPersistedContainers()
		{
			List<PersistedContainer> list = new List<PersistedContainer>();
			foreach (ZDO allPersistedZdo in GetAllPersistedZdos())
			{
				if (allPersistedZdo.GetInt(ZDOVars.s_inUse, 0) == 1)
				{
					continue;
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(allPersistedZdo.GetPrefab());
				if (!((Object)(object)prefab == (Object)null))
				{
					Container componentInChildren = prefab.GetComponentInChildren<Container>(true);
					if (!((Object)(object)componentInChildren == (Object)null) && IsPlayerChest(componentInChildren))
					{
						list.Add(new PersistedContainer(allPersistedZdo, componentInChildren));
					}
				}
			}
			return list;
		}

		private static bool IsStoneOven(Smelter smelter)
		{
			return smelter.m_name.IndexOf("oven", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static bool IsPlayerChest(Container container)
		{
			return container.m_name.IndexOf("chest", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static IEnumerable<ZDO> GetAllPersistedZdos()
		{
			if (typeof(ZDOMan).GetField("m_objectsByID", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(ZDOMan.instance) is Dictionary<ZDOID, ZDO> dictionary)
			{
				return dictionary.Values.ToList();
			}
			return Array.Empty<ZDO>();
		}

		private void UnloadPersistedSmelterItems(ZDO oven, Smelter smelter, List<PersistedContainer> chests)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			int num = oven.GetInt(ZDOVars.s_spawnAmount, 0);
			string inputName = oven.GetString(ZDOVars.s_spawnOre, "");
			ItemConversion val = ((IEnumerable<ItemConversion>)smelter.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion entry) => (Object)(object)entry.m_from != (Object)null && ((Object)((Component)entry.m_from).gameObject).name == inputName));
			if (num > 0 && !((Object)(object)val?.m_to == (Object)null))
			{
				ItemData item = val.m_to.m_itemData.Clone();
				item.m_stack = num;
				PersistedContainer persistedContainer = chests.FirstOrDefault((PersistedContainer chest) => LoadInventory(chest).HaveItem(item.m_shared.m_name, true));
				if (persistedContainer == null)
				{
					persistedContainer = chests.FirstOrDefault();
				}
				if (persistedContainer == null || !TryAddToPersistedContainer(persistedContainer, item))
				{
					ItemDrop.DropItem(item, item.m_stack, oven.GetPosition() + Vector3.up, Quaternion.identity);
				}
				oven.Set(ZDOVars.s_spawnOre, "");
				oven.Set(ZDOVars.s_spawnAmount, 0, false);
				ZDOMan.instance.SetDirtySector(oven);
			}
		}

		private void FuelPersistedSmelter(ZDO oven, Smelter smelter, List<PersistedContainer> chests)
		{
			if ((Object)(object)smelter.m_fuelItem == (Object)null || smelter.m_maxFuel <= 0)
			{
				return;
			}
			string fuelName = smelter.m_fuelItem.m_itemData.m_shared.m_name;
			while (oven.GetFloat(ZDOVars.s_fuel, 0f) < (float)smelter.m_maxFuel)
			{
				PersistedContainer persistedContainer = chests.FirstOrDefault((PersistedContainer chest) => LoadInventory(chest).GetItem(fuelName, -1, false) != null);
				if (persistedContainer == null || !TryRemoveFromPersistedContainer(persistedContainer, fuelName))
				{
					break;
				}
				oven.Set(ZDOVars.s_fuel, oven.GetFloat(ZDOVars.s_fuel, 0f) + 1f);
				ZDOMan.instance.SetDirtySector(oven);
			}
		}

		private void LoadPersistedSmelterItems(ZDO oven, Smelter smelter, List<PersistedContainer> chests)
		{
			foreach (PersistedContainer chest in chests)
			{
				Inventory val = LoadInventory(chest);
				foreach (ItemData item in val.GetAllItems().ToList())
				{
					if (oven.GetInt(ZDOVars.s_queued, 0) >= smelter.m_maxOre)
					{
						return;
					}
					string itemName = ((Object)item.m_dropPrefab).name;
					if (smelter.m_conversion.Any((ItemConversion conversion) => (Object)(object)conversion.m_from != (Object)null && ((Object)((Component)conversion.m_from).gameObject).name == itemName))
					{
						val.RemoveItem(item, 1);
						SaveInventory(chest, val);
						int num = oven.GetInt(ZDOVars.s_queued, 0);
						oven.Set("item" + num, itemName);
						oven.Set(ZDOVars.s_queued, num + 1, false);
						oven.Set(ZDOVars.s_cheatedQueued, item.m_cheated);
						ZDOMan.instance.SetDirtySector(oven);
					}
				}
			}
		}

		private static Inventory LoadInventory(PersistedContainer container)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			Inventory val = new Inventory(container.Prefab.m_name, container.Prefab.m_bkg, container.Prefab.m_width, container.Prefab.m_height);
			byte[] byteArray = container.Zdo.GetByteArray(ZDOVars.s_items, (byte[])null);
			if (byteArray != null)
			{
				val.Load(new ZPackage(byteArray));
			}
			return val;
		}

		private static void SaveInventory(PersistedContainer container, Inventory inventory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			inventory.Save(val);
			container.Zdo.Set(ZDOVars.s_items, val.GetArray());
			ZDOMan.instance.SetDirtySector(container.Zdo);
		}

		private static bool TryAddToPersistedContainer(PersistedContainer container, ItemData item)
		{
			if (!ClaimZdoOwnership(container.Zdo))
			{
				return false;
			}
			Inventory val = LoadInventory(container);
			if (!val.AddItem(item))
			{
				return false;
			}
			SaveInventory(container, val);
			return true;
		}

		private static bool TryRemoveFromPersistedContainer(PersistedContainer container, string itemName)
		{
			if (!ClaimZdoOwnership(container.Zdo))
			{
				return false;
			}
			Inventory val = LoadInventory(container);
			ItemData item = val.GetItem(itemName, -1, false);
			if (item == null)
			{
				return false;
			}
			val.RemoveItem(item, 1);
			SaveInventory(container, val);
			return true;
		}

		private static bool IsCookingStationLive(ZDO oven)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNetScene.instance.HaveInstance(oven))
			{
				return false;
			}
			GameObject val = ZNetScene.instance.FindInstance(oven.m_uid);
			CookingStation val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren<CookingStation>(true) : null);
			if ((Object)(object)val2 != (Object)null)
			{
				return ((Behaviour)val2).isActiveAndEnabled;
			}
			return false;
		}

		private static bool ClaimZdoOwnership(ZDO zdo)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				zdo.SetOwner(ZDOMan.GetSessionID());
				return zdo.IsOwner();
			}
			long sessionID = ZDOMan.GetSessionID();
			long owner = zdo.GetOwner();
			if (owner != 0L && owner != sessionID)
			{
				return false;
			}
			zdo.SetOwner(ZDOMan.GetSessionID());
			return zdo.IsOwner();
		}

		private void UnloadFinishedSmelterItems(Smelter oven, List<Container> chests)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)oven).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			int num = zDO.GetInt(ZDOVars.s_spawnAmount, 0);
			string inputName = zDO.GetString(ZDOVars.s_spawnOre, "");
			ItemConversion val = ((IEnumerable<ItemConversion>)oven.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion entry) => (Object)(object)entry.m_from != (Object)null && ((Object)((Component)entry.m_from).gameObject).name == inputName));
			if (num > 0 && !((Object)(object)val?.m_to == (Object)null))
			{
				ItemData item = val.m_to.m_itemData.Clone();
				item.m_stack = num;
				Container val2 = ((IEnumerable<Container>)chests).FirstOrDefault((Func<Container, bool>)((Container chest) => chest.GetInventory().HaveItem(item.m_shared.m_name, true)));
				if (val2 == null)
				{
					val2 = chests.FirstOrDefault();
				}
				if (!((Object)(object)val2 != (Object)null) || !ClaimContainerOwnership(val2) || !val2.GetInventory().AddItem(item))
				{
					ItemDrop.DropItem(item, item.m_stack, ((Component)oven).transform.position + Vector3.up, Quaternion.identity);
				}
				component.ClaimOwnership();
				zDO.Set(ZDOVars.s_spawnOre, "");
				zDO.Set(ZDOVars.s_spawnAmount, 0, false);
			}
		}

		private void FuelSmelter(Smelter oven, List<Container> chests)
		{
			if ((Object)(object)oven.m_fuelItem == (Object)null || oven.m_maxFuel <= 0)
			{
				return;
			}
			ZNetView component = ((Component)oven).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			string fuelName = oven.m_fuelItem.m_itemData.m_shared.m_name;
			ZDO zDO = component.GetZDO();
			while (zDO.GetFloat(ZDOVars.s_fuel, 0f) < (float)oven.m_maxFuel)
			{
				Container val = ((IEnumerable<Container>)chests).FirstOrDefault((Func<Container, bool>)((Container chest) => chest.GetInventory().GetItem(fuelName, -1, false) != null));
				if ((Object)(object)val == (Object)null || !ClaimContainerOwnership(val))
				{
					break;
				}
				ItemData item = val.GetInventory().GetItem(fuelName, -1, false);
				if (item != null)
				{
					component.ClaimOwnership();
					val.GetInventory().RemoveItem(item, 1);
					zDO.Set(ZDOVars.s_fuel, zDO.GetFloat(ZDOVars.s_fuel, 0f) + 1f);
				}
			}
		}

		private void LoadSmelterItems(Smelter oven, List<Container> chests)
		{
			ZNetView component = ((Component)oven).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			foreach (Container chest in chests)
			{
				foreach (ItemData item in chest.GetInventory().GetAllItems().ToList())
				{
					if (zDO.GetInt(ZDOVars.s_queued, 0) >= oven.m_maxOre)
					{
						return;
					}
					string itemName = ((Object)item.m_dropPrefab).name;
					if (oven.m_conversion.Any((ItemConversion conversion) => (Object)(object)conversion.m_from != (Object)null && ((Object)((Component)conversion.m_from).gameObject).name == itemName) && ClaimContainerOwnership(chest))
					{
						component.ClaimOwnership();
						int num = zDO.GetInt(ZDOVars.s_queued, 0);
						chest.GetInventory().RemoveItem(item, 1);
						zDO.Set("item" + num, itemName);
						zDO.Set(ZDOVars.s_queued, num + 1, false);
						zDO.Set(ZDOVars.s_cheatedQueued, item.m_cheated);
					}
				}
			}
		}

		private List<Container> FindNearbyChests(Vector3 position)
		{
			//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)
			return (from container in Object.FindObjectsOfType<Container>()
				where (Object)(object)container != (Object)null && ((Behaviour)container).isActiveAndEnabled && IsPlayerChest(container)
				select new
				{
					Container = container,
					Distance = Vector3.Distance(position, ((Component)container).transform.position)
				} into entry
				where entry.Distance <= Mathf.Max(0f, scanDistance.Value)
				orderby entry.Distance
				select entry.Container).ToList();
		}

		private void LoadBakeableItems(CookingStation oven, List<Container> chests)
		{
			bool flag;
			do
			{
				flag = false;
				foreach (Container chest in chests)
				{
					if (!OvenSlots.HasFreeSlot(oven))
					{
						return;
					}
					foreach (ItemData item in chest.GetInventory().GetAllItems().ToList())
					{
						if (!OvenSlots.HasFreeSlot(oven))
						{
							return;
						}
						if (TryAddToOven(oven, chest, item))
						{
							flag = true;
						}
					}
				}
			}
			while (flag);
		}

		private void FuelOven(CookingStation oven, List<Container> chests)
		{
			ItemDrop fuelItem = oven.m_fuelItem;
			if ((Object)(object)fuelItem == (Object)null || oven.m_maxFuel <= 0)
			{
				return;
			}
			MethodInfo method = ((object)oven).GetType().GetMethod("GetFuel", BindingFlags.Instance | BindingFlags.NonPublic);
			MethodInfo method2 = ((object)oven).GetType().GetMethod("RPC_AddFuel", BindingFlags.Instance | BindingFlags.NonPublic);
			ZNetView component = ((Component)oven).GetComponent<ZNetView>();
			if (method == null || method2 == null || (Object)(object)component == (Object)null || !component.IsValid())
			{
				LogAdapterWarning("CookingStation fuel API or ZNetView was not found.");
				return;
			}
			string name = fuelItem.m_itemData.m_shared.m_name;
			while (Convert.ToSingle(method.Invoke(oven, null)) < (float)oven.m_maxFuel)
			{
				bool flag = false;
				foreach (Container chest in chests)
				{
					Inventory inventory = chest.GetInventory();
					ItemData item = inventory.GetItem(name, -1, false);
					if (item != null && ClaimContainerOwnership(chest))
					{
						component.ClaimOwnership();
						float num = Convert.ToSingle(method.Invoke(oven, null));
						method2.Invoke(oven, new object[1] { 0L });
						if (!(Convert.ToSingle(method.Invoke(oven, null)) <= num))
						{
							inventory.RemoveItem(item, 1);
							flag = true;
							break;
						}
					}
				}
				if (!flag)
				{
					break;
				}
			}
		}

		private void UnloadFinishedItems(CookingStation oven, List<Container> chests)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			int slot;
			do
			{
				if (!OvenSlots.TryTakeFinished(oven, out ItemData item, out slot))
				{
					break;
				}
				Container val = ((IEnumerable<Container>)chests).FirstOrDefault((Func<Container, bool>)delegate(Container chest)
				{
					Inventory inventory = chest.GetInventory();
					return inventory.HaveItem(item.m_shared.m_name, true) && inventory.CanAddItem(item, -1);
				});
				if (val == null)
				{
					val = ((IEnumerable<Container>)chests).FirstOrDefault((Func<Container, bool>)((Container chest) => chest.GetInventory().CanAddItem(item, -1)));
				}
				bool flag = (Object)(object)val != (Object)null && ClaimContainerOwnership(val) && val.GetInventory().AddItem(item);
				if (!flag)
				{
					ItemDrop.DropItem(item, item.m_stack, ((Component)oven).transform.position + Vector3.up, Quaternion.identity);
				}
				if (logScanSummary.Value)
				{
					string text = (flag ? ("chest '" + ((Object)val).name + "'") : "the ground fallback");
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Removed finished '{item.m_shared.m_name}' from oven '{((Object)oven).name}' slot {slot} and stored it in {text}.");
				}
			}
			while (OvenSlots.RemoveFinished(oven, slot));
		}

		private bool TryAddToOven(CookingStation oven, Container chest, ItemData item)
		{
			try
			{
				Inventory inventory = chest.GetInventory();
				ZNetView component = ((Component)oven).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					LogAdapterWarning("CookingStation has no valid ZNetView.");
					return false;
				}
				string itemName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
				if (itemName == null)
				{
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Skipped '" + item.m_shared.m_name + "' from chest '" + ((Object)chest).name + "': item has no m_dropPrefab reference."));
					}
					return false;
				}
				if (!oven.m_conversion.Any((ItemConversion conversion) => (Object)(object)conversion.m_from != (Object)null && ((Object)((Component)conversion.m_from).gameObject).name == itemName))
				{
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Skipped '" + itemName + "': not a bakeable item for oven '" + ((Object)oven).name + "' (no matching m_conversion entry)."));
					}
					return false;
				}
				int num = OvenSlots.FindFreeSlot(oven);
				if (num < 0)
				{
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Skipped '" + itemName + "': oven '" + ((Object)oven).name + "' has no free slot."));
					}
					return false;
				}
				if (!ClaimContainerOwnership(chest))
				{
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Skipped '" + itemName + "': could not claim ownership of chest '" + ((Object)chest).name + "'."));
					}
					return false;
				}
				component.ClaimOwnership();
				MethodInfo method = typeof(CookingStation).GetMethod("RPC_AddItem", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null)
				{
					LogAdapterWarning("CookingStation.RPC_AddItem was not found.");
					return false;
				}
				method.Invoke(oven, new object[3] { 0L, itemName, item.m_cheated });
				if (!OvenSlots.SlotContainsItem(oven, num, itemName))
				{
					if (logScanSummary.Value)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)$"'{itemName}' was not confirmed in oven '{((Object)oven).name}' slot {num} after RPC_AddItem.");
					}
					return false;
				}
				component.InvokeRPC(ZNetView.Everybody, "RPC_SetSlotVisual", new object[2] { num, itemName });
				inventory.RemoveItem(item, 1);
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load " + item.m_shared.m_name + " into an oven: " + ex.GetBaseException().Message));
				return false;
			}
		}

		private static bool ClaimContainerOwnership(Container container)
		{
			ZNetView val = ((Component)container).GetComponent<ZNetView>() ?? ((Component)container).GetComponentInParent<ZNetView>();
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			val.ClaimOwnership();
			return val.IsOwner();
		}

		private void LogAdapterWarning(string message)
		{
			if (!adapterWarningLogged)
			{
				adapterWarningLogged = true;
				((BaseUnityPlugin)this).Logger.LogWarning((object)(message + " Finished-item automation is disabled for this Valheim build."));
			}
		}
	}
}