Decompiled source of PlanetMiner v3.1.7

PlanetMiner.dll

Decompiled 3 days ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PlanetMiner")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PlanetMiner")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("14898231-414a-4a7c-a343-fa3adec8e4fa")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace PlanetMiner;

[BepInPlugin("crecheng.PlanetMiner", "PlanetMiner", "3.1.7")]
public class PlanetMiner : BaseUnityPlugin
{
	private sealed class FactoryState
	{
		public readonly object SyncRoot = new object();

		public readonly Dictionary<int, List<int>> VeinsByItem = new Dictionary<int, List<int>>(64);

		public readonly Stack<List<int>> ListPool = new Stack<List<int>>();

		public readonly Dictionary<long, OilFractionState> OilFractions = new Dictionary<long, OilFractionState>();

		public PlanetFactory Factory;

		public VeinData[] VeinPool;

		public int PlanetId;

		public double CostFrac;

		public bool RebuildRequested = true;
	}

	private struct OilFractionState
	{
		public readonly int ItemId;

		public readonly double Fraction;

		public OilFractionState(int itemId, double fraction)
		{
			ItemId = itemId;
			Fraction = fraction;
		}
	}

	public const float DefaultMaxPlanetRadius = 100f;

	public const int uesEnergy = 20000000;

	private static volatile bool _enablePlanetRadiusLimit = false;

	private static volatile float _maxPlanetRadius = 100f;

	private ConfigEntry<bool> _enablePlanetRadiusLimitConfig;

	private ConfigEntry<float> _maxPlanetRadiusConfig;

	private static volatile bool _weaverResolved;

	private static bool _weaverAvailable;

	private static MethodInfo _getOptimizedPlanetMethod;

	private static PropertyInfo _statusProperty;

	private static object _runningEnumValue;

	private static readonly object _weaverInitLock = new object();

	private static readonly ConcurrentDictionary<int, long> _heatValueCache = new ConcurrentDictionary<int, long>();

	private static readonly ConcurrentDictionary<int, bool> _oilItemCache = new ConcurrentDictionary<int, bool>();

	private static readonly ConcurrentDictionary<int, FactoryState> _stateByFactory = new ConcurrentDictionary<int, FactoryState>();

	private static void EnsureWeaverResolved()
	{
		if (_weaverResolved)
		{
			return;
		}
		lock (_weaverInitLock)
		{
			if (_weaverResolved)
			{
				return;
			}
			try
			{
				Type type = Type.GetType("Weaver.Optimizations.IOptimizedPlanet, DSP_Weaver");
				if (type == null)
				{
					return;
				}
				Type type2 = Type.GetType("Weaver.Optimizations.OptimizedStarCluster, DSP_Weaver");
				if (type2 == null)
				{
					return;
				}
				MethodInfo method = type2.GetMethod("GetOptimizedPlanet", BindingFlags.Static | BindingFlags.Public);
				if (method == null)
				{
					return;
				}
				PropertyInfo property = type.GetProperty("Status");
				if (!(property == null))
				{
					Type type3 = Type.GetType("Weaver.Optimizations.OptimizedPlanetStatus, DSP_Weaver");
					if (!(type3 == null))
					{
						_getOptimizedPlanetMethod = method;
						_statusProperty = property;
						_runningEnumValue = Enum.Parse(type3, "Running");
						_weaverAvailable = true;
					}
				}
			}
			catch
			{
				_weaverAvailable = false;
			}
			finally
			{
				_weaverResolved = true;
			}
		}
	}

	private static bool IsOptimizedByWeaver(FactorySystem factorySystem)
	{
		EnsureWeaverResolved();
		if (!_weaverAvailable)
		{
			return false;
		}
		try
		{
			object[] parameters = new object[1] { factorySystem.planet };
			object obj = _getOptimizedPlanetMethod.Invoke(null, parameters);
			if (obj == null)
			{
				return false;
			}
			return _statusProperty.GetValue(obj)?.Equals(_runningEnumValue) ?? false;
		}
		catch
		{
			return false;
		}
	}

	private static long GetHeatValue(int itemId)
	{
		if (_heatValueCache.TryGetValue(itemId, out var value))
		{
			return value;
		}
		value = ((ProtoSet<ItemProto>)(object)LDB.items).Select(itemId)?.HeatValue ?? 0;
		_heatValueCache.TryAdd(itemId, value);
		return value;
	}

	private static bool IsOilItem(int itemId)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected I4, but got Unknown
		if (_oilItemCache.TryGetValue(itemId, out var value))
		{
			return value;
		}
		int num = (int)LDB.veins.GetVeinTypeByItemId(itemId);
		value = num == 7 || num == 16 || num == 19;
		_oilItemCache.TryAdd(itemId, value);
		return value;
	}

	private static long MakeSlotKey(int stationIndex, int slotIndex)
	{
		return ((long)stationIndex << 32) | (uint)slotIndex;
	}

	private static void ClearOilFraction(FactoryState state, int stationIndex, int slotIndex)
	{
		state.OilFractions.Remove(MakeSlotKey(stationIndex, slotIndex));
	}

	private static int AccumulateOilOutput(FactoryState state, int stationIndex, int slotIndex, int itemId, double produced)
	{
		if (produced <= 0.0)
		{
			return 0;
		}
		long key = MakeSlotKey(stationIndex, slotIndex);
		double num = produced;
		if (state.OilFractions.TryGetValue(key, out var value) && value.ItemId == itemId)
		{
			num += value.Fraction;
		}
		int num2 = (int)num;
		double num3 = num - (double)num2;
		if (num3 > 0.0)
		{
			state.OilFractions[key] = new OilFractionState(itemId, num3);
		}
		else
		{
			state.OilFractions.Remove(key);
		}
		return num2;
	}

	private static void AddProductStat(int[] productRegister, int itemId, int amount)
	{
		if (productRegister != null && amount > 0 && (uint)itemId < (uint)productRegister.Length)
		{
			productRegister[itemId] += amount;
		}
	}

	private static void RecycleVeinLists(FactoryState state)
	{
		foreach (KeyValuePair<int, List<int>> item in state.VeinsByItem)
		{
			item.Value.Clear();
			state.ListPool.Push(item.Value);
		}
		state.VeinsByItem.Clear();
	}

	private static List<int> AcquireVeinList(FactoryState state)
	{
		return (state.ListPool.Count > 0) ? state.ListPool.Pop() : new List<int>(8);
	}

	private static void RebuildVeinCache(FactoryState state, VeinData[] veinPool)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: 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_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		RecycleVeinLists(state);
		if (veinPool != null)
		{
			for (int i = 0; i < veinPool.Length; i++)
			{
				VeinData val = veinPool[i];
				if (val.amount > 0 && val.productId > 0)
				{
					if (!state.VeinsByItem.TryGetValue(val.productId, out var value))
					{
						value = AcquireVeinList(state);
						state.VeinsByItem[val.productId] = value;
					}
					value.Add(i);
				}
			}
		}
		state.RebuildRequested = false;
	}

	private static void PrepareFactoryState(FactoryState state, PlanetFactory factory, int planetId)
	{
		VeinData[] veinPool = factory.veinPool;
		if (state.Factory != factory || state.VeinPool != veinPool || state.PlanetId != planetId)
		{
			state.Factory = factory;
			state.VeinPool = veinPool;
			state.PlanetId = planetId;
			state.CostFrac = 0.0;
			state.OilFractions.Clear();
			state.RebuildRequested = true;
		}
		if (state.RebuildRequested)
		{
			RebuildVeinCache(state, veinPool);
		}
	}

	private static double SumOilOutput(List<int> indices, VeinData[] veinPool, int itemId, out bool cacheStale)
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: 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_006f: 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)
		cacheStale = false;
		if (veinPool == null)
		{
			return 0.0;
		}
		double num = 0.0;
		int count = indices.Count;
		for (int i = 0; i < count; i++)
		{
			int num2 = indices[i];
			if (num2 < 0 || num2 >= veinPool.Length)
			{
				cacheStale = true;
				break;
			}
			VeinData val = veinPool[num2];
			if (val.productId != itemId || val.amount <= 0)
			{
				cacheStale = true;
				break;
			}
			num += (double)val.amount / 6000.0;
		}
		return num;
	}

	private static bool TryMine(VeinData[] veinPool, int index, int itemId, float miningRate, PlanetFactory factory, ref double costFrac, out bool cacheStale)
	{
		cacheStale = false;
		if (veinPool == null || index < 0 || index >= veinPool.Length)
		{
			cacheStale = true;
			return false;
		}
		if (veinPool[index].productId != itemId)
		{
			cacheStale = true;
			return false;
		}
		if (veinPool[index].amount > 0)
		{
			bool flag = false;
			if (miningRate > 0f)
			{
				costFrac += miningRate;
				flag = (int)costFrac > 0;
				if (flag)
				{
					costFrac -= 1.0;
				}
			}
			if (flag)
			{
				veinPool[index].amount = veinPool[index].amount - 1;
				factory.veinGroups[veinPool[index].groupIndex].amount--;
				if (veinPool[index].amount <= 0)
				{
					short groupIndex = veinPool[index].groupIndex;
					factory.veinGroups[groupIndex].count--;
					factory.RemoveVeinWithComponents(index);
					factory.RecalculateVeinGroup((int)groupIndex);
					cacheStale = true;
				}
			}
			return true;
		}
		short groupIndex2 = veinPool[index].groupIndex;
		factory.veinGroups[groupIndex2].count--;
		factory.RemoveVeinWithComponents(index);
		factory.RecalculateVeinGroup((int)groupIndex2);
		cacheStale = true;
		return false;
	}

	private void Start()
	{
		_enablePlanetRadiusLimitConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "EnablePlanetRadiusLimit", false, "Whether PlanetMiner is limited by planet radius.");
		_maxPlanetRadiusConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "MaxPlanetRadius", 100f, "PlanetMiner works only when planet.radius is smaller than this value.");
		ApplyConfigValues();
		_enablePlanetRadiusLimitConfig.SettingChanged += OnConfigSettingChanged;
		_maxPlanetRadiusConfig.SettingChanged += OnConfigSettingChanged;
		Harmony.CreateAndPatchAll(typeof(PlanetMiner), (string)null);
	}

	private void OnConfigSettingChanged(object sender, EventArgs e)
	{
		ApplyConfigValues();
	}

	private void ApplyConfigValues()
	{
		_enablePlanetRadiusLimit = _enablePlanetRadiusLimitConfig != null && _enablePlanetRadiusLimitConfig.Value;
		float num = _maxPlanetRadiusConfig?.Value ?? 100f;
		_maxPlanetRadius = ((num > 0f) ? num : 100f);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(FactorySystem), "GameTickLabResearchMode")]
	private static void Miner(FactorySystem __instance)
	{
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0284: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Invalid comparison between Unknown and I4
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0314: Unknown result type (might be due to invalid IL or missing references)
		if (__instance == null || __instance.planet == null)
		{
			return;
		}
		GameHistoryData history = GameMain.history;
		if (history == null)
		{
			return;
		}
		float miningSpeedScale = history.miningSpeedScale;
		if (miningSpeedScale <= 0f)
		{
			return;
		}
		PlanetFactory factory = __instance.factory;
		if (factory == null)
		{
			return;
		}
		PlanetData planet = __instance.planet;
		if (planet == null || (_enablePlanetRadiusLimit && planet.radius >= _maxPlanetRadius))
		{
			return;
		}
		int num = (int)(120f / miningSpeedScale);
		if (num <= 0)
		{
			num = 1;
		}
		int index = factory.index;
		long gameTick = GameMain.gameTick;
		if ((gameTick + index) % num != 0 || IsOptimizedByWeaver(__instance))
		{
			return;
		}
		PlanetTransport transport = factory.transport;
		if (transport == null || transport.stationPool == null)
		{
			return;
		}
		FactoryState orAdd = _stateByFactory.GetOrAdd(index, (int _) => new FactoryState());
		lock (orAdd.SyncRoot)
		{
			PrepareFactoryState(orAdd, factory, __instance.planet.id);
			VeinData[] veinPool = orAdd.VeinPool;
			int[] productRegister = null;
			if (GameMain.statistics != null && GameMain.statistics.production != null)
			{
				FactoryProductionStat[] factoryStatPool = GameMain.statistics.production.factoryStatPool;
				if (factoryStatPool != null && (uint)index < (uint)factoryStatPool.Length)
				{
					productRegister = factoryStatPool[index]?.productRegister;
				}
			}
			int waterItemId = __instance.planet.waterItemId;
			float miningCostRate = history.miningCostRate;
			StationComponent[] stationPool = transport.stationPool;
			double costFrac = orAdd.CostFrac;
			for (int i = 0; i < stationPool.Length; i++)
			{
				StationComponent val = stationPool[i];
				if (val?.storage == null)
				{
					continue;
				}
				StationStore[] storage = val.storage;
				bool flag = false;
				for (int j = 0; j < storage.Length; j++)
				{
					StationStore val2 = storage[j];
					if (val2.count < 0)
					{
						val2.count = 0;
						storage[j].count = 0;
					}
					int itemId = val2.itemId;
					if ((int)val2.localLogic != 2)
					{
						ClearOilFraction(orAdd, i, j);
						continue;
					}
					if (itemId <= 0)
					{
						ClearOilFraction(orAdd, i, j);
						continue;
					}
					List<int> value;
					bool flag2 = orAdd.VeinsByItem.TryGetValue(itemId, out value);
					bool flag3 = flag2 && IsOilItem(itemId);
					if (!flag3)
					{
						ClearOilFraction(orAdd, i, j);
					}
					if (val2.max <= val2.count)
					{
						continue;
					}
					if (flag2)
					{
						if (val.energy < 20000000 && !flag)
						{
							GenerateEnergy(val);
							flag = true;
						}
						if (val.energy < 20000000)
						{
							continue;
						}
						int num2 = 0;
						if (flag3)
						{
							bool cacheStale;
							double produced = SumOilOutput(value, veinPool, itemId, out cacheStale);
							if (cacheStale)
							{
								RebuildVeinCache(orAdd, veinPool);
								if (!orAdd.VeinsByItem.TryGetValue(itemId, out value))
								{
									ClearOilFraction(orAdd, i, j);
									continue;
								}
								produced = SumOilOutput(value, veinPool, itemId, out cacheStale);
								if (cacheStale)
								{
									orAdd.RebuildRequested = true;
									continue;
								}
							}
							num2 = AccumulateOilOutput(orAdd, i, j, itemId, produced);
						}
						else
						{
							int count = value.Count;
							bool flag4 = false;
							for (int k = 0; k < count; k++)
							{
								if (TryMine(veinPool, value[k], itemId, miningCostRate, factory, ref costFrac, out var cacheStale2))
								{
									num2++;
								}
								if (cacheStale2)
								{
									flag4 = true;
									break;
								}
							}
							if (flag4)
							{
								RebuildVeinCache(orAdd, veinPool);
							}
						}
						if (num2 > 0)
						{
							storage[j].count += num2;
							AddProductStat(productRegister, itemId, num2);
							val.energy -= 20000000;
						}
					}
					else if (itemId == waterItemId)
					{
						if (val.energy < 20000000 && !flag)
						{
							GenerateEnergy(val);
							flag = true;
						}
						if (val.energy >= 20000000)
						{
							storage[j].count += 100;
							AddProductStat(productRegister, itemId, 100);
							val.energy -= 20000000;
						}
					}
				}
			}
			orAdd.CostFrac = costFrac;
		}
	}

	private static void GenerateEnergy(StationComponent station)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		int num = station.storage.Length - 2;
		if (num < 0 || num >= station.storage.Length || station.energyMax / 2 <= station.energy)
		{
			return;
		}
		StationStore val = station.storage[num];
		int itemId = val.itemId;
		int count = val.count;
		if (itemId <= 0 || count <= 0)
		{
			return;
		}
		long heatValue = GetHeatValue(itemId);
		if (heatValue <= 0)
		{
			return;
		}
		int num2 = (int)((station.energyMax - station.energy) / heatValue);
		if (num2 > count)
		{
			num2 = count;
		}
		if (num2 <= 0)
		{
			return;
		}
		int num3 = split_inc_level(ref val.count, ref val.inc, num2);
		double num4 = 1.0;
		if (num2 > 0 && num3 > 0)
		{
			int num5 = num3 / num2;
			if (num5 >= 0 && num5 < Cargo.incTableMilli.Length)
			{
				num4 += Cargo.incTableMilli[num5];
			}
		}
		station.energy += (long)((double)(num2 * heatValue) * num4);
		station.storage[num].inc = val.inc;
		station.storage[num].count = val.count;
	}

	private static int split_inc_level(ref int count, ref int totalinc, int requireCount)
	{
		int num = totalinc / count;
		int num2 = totalinc - num * count;
		count -= requireCount;
		num2 -= count;
		num = ((num2 > 0) ? (num * requireCount + num2) : (num * requireCount));
		totalinc -= num;
		return num;
	}
}