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;
}
}