Decompiled source of FrizzQOL Smelter Control v0.1.0

FrizzQOL.SmelterControl.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("FrizzQOL.SmelterControl")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FrizzQOL.SmelterControl")]
[assembly: AssemblyTitle("FrizzQOL.SmelterControl")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SmelterControl
{
	[HarmonyPatch(typeof(Smelter), "Awake")]
	internal static class SmelterAwakePatch
	{
		private static void Postfix(Smelter __instance)
		{
			try
			{
				SmelterApplier.Apply(__instance);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce(ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			try
			{
				SmelterApplier.ApplyPrefabs(__instance);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce(ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "RPC_AddOre")]
	internal static class AddOreCapPatch
	{
		private static readonly MethodInfo QueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

		private static readonly FieldInfo NetView = AccessTools.Field(typeof(Smelter), "m_nview");

		private static bool Prefix(Smelter __instance)
		{
			try
			{
				if (QueueSize == null)
				{
					Plugin.LogOnce("Smelter.GetQueueSize was missing.");
				}
				if (NetView == null)
				{
					Plugin.LogOnce("Smelter.m_nview was missing.");
				}
				if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).gameObject == (Object)null || !SmelterRules.IsControlled(SmelterRules.PrefabName(((Object)((Component)__instance).gameObject).name)))
				{
					return true;
				}
				if (!IsOwner(__instance))
				{
					return true;
				}
				int queueSize = 0;
				if (QueueSize != null && QueueSize.Invoke(__instance, null) is int num)
				{
					queueSize = num;
				}
				return !SmelterRules.OreIsFull(queueSize, __instance.m_maxOre);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce(ex.Message);
				return true;
			}
		}

		internal static bool IsOwner(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null)
			{
				return false;
			}
			if (NetView == null)
			{
				Plugin.LogOnce("Smelter.m_nview was missing.");
				return false;
			}
			object? value = NetView.GetValue(smelter);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				return val.IsOwner();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "RPC_AddFuel")]
	internal static class AddFuelCapPatch
	{
		private static readonly MethodInfo FuelAmount = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);

		private static bool Prefix(Smelter __instance)
		{
			try
			{
				if (FuelAmount == null)
				{
					Plugin.LogOnce("Smelter.GetFuel was missing.");
				}
				if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).gameObject == (Object)null || !SmelterRules.IsControlled(SmelterRules.PrefabName(((Object)((Component)__instance).gameObject).name)))
				{
					return true;
				}
				if (!AddOreCapPatch.IsOwner(__instance) || (Object)(object)__instance.m_fuelItem == (Object)null)
				{
					return true;
				}
				float fuel = 0f;
				if (FuelAmount != null && FuelAmount.Invoke(__instance, null) is float num)
				{
					fuel = num;
				}
				return !SmelterRules.FuelIsFull(fuel, __instance.m_maxFuel);
			}
			catch (Exception ex)
			{
				Plugin.LogOnce(ex.Message);
				return true;
			}
		}
	}
	[BepInPlugin("com.frizzqol.smeltercontrol", "FrizzQOL Smelter Control", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal struct StationSettings
		{
			public float Seconds;

			public int Input;

			public int Fuel;
		}

		public const string PluginGuid = "com.frizzqol.smeltercontrol";

		public const string PluginName = "FrizzQOL Smelter Control";

		public const string PluginVersion = "0.1.0";

		internal static ConfigEntry<float> SmelterSeconds;

		internal static ConfigEntry<int> SmelterOre;

		internal static ConfigEntry<int> SmelterFuel;

		internal static ConfigEntry<float> BlastSeconds;

		internal static ConfigEntry<int> BlastOre;

		internal static ConfigEntry<int> BlastFuel;

		internal static ConfigEntry<float> KilnSeconds;

		internal static ConfigEntry<int> KilnWood;

		private static readonly HashSet<string> Logged = new HashSet<string>();

		private const long ConfigReloadQuietTicks = 2500000L;

		private Harmony _harmony;

		private FileSystemWatcher _configWatcher;

		private long _configQuietUntilTicks;

		private int _applyQueued;

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			Instance = this;
			SmelterSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Smelter", "Seconds", 0f, "Seconds per bar. 0 keeps the normal 30. Below 0 does the same.");
			SmelterOre = ((BaseUnityPlugin)this).Config.Bind<int>("Smelter", "Ore", 0, "Ore the smelter will hold. 0 keeps the normal 10. Below 0 does the same.");
			SmelterFuel = ((BaseUnityPlugin)this).Config.Bind<int>("Smelter", "Fuel", 0, "Coal the smelter will hold. 0 keeps the normal 20. Below 0 does the same.");
			BlastSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("BlastFurnace", "Seconds", 0f, "Seconds per bar. 0 keeps the normal 30. Below 0 does the same.");
			BlastOre = ((BaseUnityPlugin)this).Config.Bind<int>("BlastFurnace", "Ore", 0, "Ore the blast furnace will hold. 0 keeps the normal 10. Below 0 does the same.");
			BlastFuel = ((BaseUnityPlugin)this).Config.Bind<int>("BlastFurnace", "Fuel", 0, "Coal the blast furnace will hold. 0 keeps the normal 20. Below 0 does the same.");
			KilnSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("CharcoalKiln", "Seconds", 0f, "Seconds per coal. 0 keeps the normal 15. Below 0 does the same.");
			KilnWood = ((BaseUnityPlugin)this).Config.Bind<int>("CharcoalKiln", "Wood", 0, "Wood the kiln will hold. 0 keeps the normal 25. Below 0 does the same.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				QueueApply();
			};
			WatchConfigFile();
			_harmony = new Harmony("com.frizzqol.smeltercontrol");
			try
			{
				_harmony.PatchAll();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"FrizzQOL Smelter Control 0.1.0 loaded");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patch failed: " + ex.ToString()));
				_harmony.UnpatchSelf();
			}
		}

		private void QueueApply()
		{
			if (Interlocked.CompareExchange(ref _applyQueued, 1, 0) == 0)
			{
				if ((Object)(object)ThreadingHelper.Instance != (Object)null)
				{
					ThreadingHelper.Instance.StartSyncInvoke((Action)ApplyQueued);
				}
				else
				{
					ApplyQueued();
				}
			}
		}

		private void ApplyQueued()
		{
			Interlocked.Exchange(ref _applyQueued, 0);
			SmelterApplier.ApplyAll();
		}

		private void WatchConfigFile()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			string directoryName = Path.GetDirectoryName(configFilePath);
			string fileName = Path.GetFileName(configFilePath);
			if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName))
			{
				_configWatcher = new FileSystemWatcher(directoryName, fileName);
				_configWatcher.NotifyFilter = NotifyFilters.LastWrite;
				_configWatcher.Changed += OnConfigFileChanged;
				_configWatcher.EnableRaisingEvents = true;
			}
		}

		private void OnConfigFileChanged(object sender, FileSystemEventArgs args)
		{
			long ticks = DateTime.UtcNow.Ticks;
			long num = Interlocked.Read(in _configQuietUntilTicks);
			if (ticks >= num && Interlocked.CompareExchange(ref _configQuietUntilTicks, ticks + 2500000, num) == num)
			{
				if ((Object)(object)ThreadingHelper.Instance != (Object)null)
				{
					ThreadingHelper.Instance.StartSyncInvoke((Action)ReloadConfigFile);
				}
				else
				{
					ReloadConfigFile();
				}
			}
		}

		private void ReloadConfigFile()
		{
			((BaseUnityPlugin)this).Config.Reload();
		}

		internal static StationSettings ConfigFor(string prefabName)
		{
			return prefabName switch
			{
				"piece_blastfurnace" => new StationSettings
				{
					Seconds = ReadSeconds(BlastSeconds, "BlastFurnace Seconds"),
					Input = ReadCount(BlastOre, "BlastFurnace Ore"),
					Fuel = ReadCount(BlastFuel, "BlastFurnace Fuel")
				}, 
				"piece_charcoalkiln" => new StationSettings
				{
					Seconds = ReadSeconds(KilnSeconds, "CharcoalKiln Seconds"),
					Input = ReadCount(KilnWood, "CharcoalKiln Wood"),
					Fuel = 0
				}, 
				"piece_smelter" => new StationSettings
				{
					Seconds = ReadSeconds(SmelterSeconds, "Smelter Seconds"),
					Input = ReadCount(SmelterOre, "Smelter Ore"),
					Fuel = ReadCount(SmelterFuel, "Smelter Fuel")
				}, 
				_ => new StationSettings
				{
					Seconds = 0f,
					Input = 0,
					Fuel = 0
				}, 
			};
		}

		internal static void LogOnce(string message)
		{
			if (!string.IsNullOrEmpty(message) && !Logged.Contains(message))
			{
				Logged.Add(message);
				LogWarning(message);
			}
		}

		internal static void LogError(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)message);
			}
		}

		private static float ReadSeconds(ConfigEntry<float> entry, string label)
		{
			float num = entry?.Value ?? 0f;
			if (num < 0f)
			{
				LogOnce(label + " below 0 keeps the normal value.");
			}
			return num;
		}

		private static int ReadCount(ConfigEntry<int> entry, string label)
		{
			int num = entry?.Value ?? 0;
			if (num < 0)
			{
				LogOnce(label + " below 0 keeps the normal value.");
			}
			return num;
		}

		private static void LogWarning(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
			}
		}
	}
	internal static class SmelterApplier
	{
		private struct VanillaStats
		{
			public float Seconds;

			public int Ore;

			public int Fuel;
		}

		private static readonly Dictionary<string, VanillaStats> Vanilla = new Dictionary<string, VanillaStats>();

		internal static void Apply(Smelter smelter)
		{
			if (!((Object)(object)smelter == (Object)null))
			{
				string objectName = (((Object)(object)((Component)smelter).gameObject != (Object)null) ? ((Object)((Component)smelter).gameObject).name : null);
				ApplyNamed(smelter, SmelterRules.PrefabName(objectName));
			}
		}

		internal static void ApplyPrefabs(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null)
			{
				return;
			}
			if (!(AccessTools.Field(typeof(ZNetScene), "m_prefabs")?.GetValue(scene) is List<GameObject> list))
			{
				Plugin.LogOnce("ZNetScene prefab list was missing. Smelter config was not applied.");
				return;
			}
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			for (int i = 0; i < list.Count; i++)
			{
				GameObject val = list[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string text = SmelterRules.PrefabName(((Object)val).name);
				if (SmelterRules.IsControlled(text))
				{
					Smelter component = val.GetComponent<Smelter>();
					switch (text)
					{
					case "piece_smelter":
						flag |= (Object)(object)component != (Object)null;
						break;
					case "piece_blastfurnace":
						flag2 |= (Object)(object)component != (Object)null;
						break;
					case "piece_charcoalkiln":
						flag3 |= (Object)(object)component != (Object)null;
						break;
					}
					ApplyNamed(component, text);
				}
			}
			if (!flag)
			{
				Plugin.LogOnce("Missing smelter prefab: piece_smelter");
			}
			if (!flag2)
			{
				Plugin.LogOnce("Missing smelter prefab: piece_blastfurnace");
			}
			if (!flag3)
			{
				Plugin.LogOnce("Missing smelter prefab: piece_charcoalkiln");
			}
		}

		internal static void ApplyAll()
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				ApplyPrefabs(ZNetScene.instance);
			}
			Smelter[] array = Object.FindObjectsByType<Smelter>((FindObjectsSortMode)0);
			for (int i = 0; i < array.Length; i++)
			{
				Apply(array[i]);
			}
		}

		private static void ApplyNamed(Smelter smelter, string prefabName)
		{
			if (!((Object)(object)smelter == (Object)null) && SmelterRules.IsControlled(prefabName))
			{
				if (!Vanilla.ContainsKey(prefabName))
				{
					Vanilla[prefabName] = new VanillaStats
					{
						Seconds = smelter.m_secPerProduct,
						Ore = smelter.m_maxOre,
						Fuel = smelter.m_maxFuel
					};
				}
				VanillaStats vanillaStats = Vanilla[prefabName];
				Plugin.StationSettings stationSettings = Plugin.ConfigFor(prefabName);
				smelter.m_secPerProduct = SmelterRules.ResolveSeconds(stationSettings.Seconds, vanillaStats.Seconds);
				smelter.m_maxOre = SmelterRules.ResolveCount(stationSettings.Input, vanillaStats.Ore);
				if (SmelterRules.WritesFuel(prefabName, (Object)(object)smelter.m_fuelItem != (Object)null))
				{
					smelter.m_maxFuel = SmelterRules.ResolveCount(stationSettings.Fuel, vanillaStats.Fuel);
				}
			}
		}
	}
	public static class SmelterRules
	{
		public const string SmelterPrefab = "piece_smelter";

		public const string BlastFurnacePrefab = "piece_blastfurnace";

		public const string CharcoalKilnPrefab = "piece_charcoalkiln";

		public static float ResolveSeconds(float configured, float vanilla)
		{
			if (configured <= 0f)
			{
				return vanilla;
			}
			return configured;
		}

		public static int ResolveCount(int configured, int vanilla)
		{
			if (configured <= 0)
			{
				return vanilla;
			}
			return configured;
		}

		public static bool IsControlled(string prefabName)
		{
			if (!(prefabName == "piece_smelter") && !(prefabName == "piece_blastfurnace"))
			{
				return prefabName == "piece_charcoalkiln";
			}
			return true;
		}

		public static bool WritesFuel(string prefabName, bool hasFuelItem)
		{
			if (!hasFuelItem)
			{
				return false;
			}
			if (!(prefabName == "piece_smelter"))
			{
				return prefabName == "piece_blastfurnace";
			}
			return true;
		}

		public static bool OreIsFull(int queueSize, int maxOre)
		{
			return queueSize >= maxOre;
		}

		public static bool FuelIsFull(float fuel, int maxFuel)
		{
			return fuel > (float)(maxFuel - 1);
		}

		public static string PrefabName(string objectName)
		{
			if (string.IsNullOrEmpty(objectName))
			{
				return "";
			}
			int num = objectName.IndexOf("(Clone)");
			if (num < 0)
			{
				return objectName;
			}
			return objectName.Substring(0, num).Trim();
		}
	}
}