Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EnergyCrystalAutoRefill Fix v4.0.1
EnergyCrystalAutoRefill Fix.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; 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("Codex")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("4.0.1.0")] [assembly: AssemblyInformationalVersion("4.0.1")] [assembly: AssemblyProduct("EnergyCrystalShopStockFix")] [assembly: AssemblyTitle("EnergyCrystalShopStockFix")] [assembly: AssemblyVersion("4.0.1.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 REPOJP.EnergyCrystalShopStockFix { [BepInPlugin("REPOJP.EnergyCrystalShopStockFix", "Energy Crystal Auto Refill - Shop Stock Fix", "4.0.1")] [BepInDependency("REPOJP.EnergyCrystalAutoRefill", "4.0.0")] public sealed class EnergyCrystalShopStockFixPlugin : BaseUnityPlugin { public const string PluginGuid = "REPOJP.EnergyCrystalShopStockFix"; public const string PluginName = "Energy Crystal Auto Refill - Shop Stock Fix"; public const string PluginVersion = "4.0.1"; internal const string TargetPluginGuid = "REPOJP.EnergyCrystalAutoRefill"; internal const string PowerCrystalItemName = "Item Power Crystal"; internal const string ChargeTotalStatName = "chargingStationChargeTotal"; internal const string SaveStateDictionaryName = "EnergyCrystalShopStockFix"; private Harmony? _harmony; internal static ManualLogSource ModLogger { get; private set; } private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown ModLogger = ((BaseUnityPlugin)this).Logger; VirtualCrystalTracker.InitializeRuntime(); _harmony = new Harmony("REPOJP.EnergyCrystalShopStockFix"); try { _harmony.PatchAll(typeof(ShopInventoryScopePatch)); _harmony.PatchAll(typeof(CurrentPurchaseCountPatch)); _harmony.PatchAll(typeof(TotalPurchaseCountPatch)); _harmony.PatchAll(typeof(CrystalConsumedPatch)); _harmony.PatchAll(typeof(NewRunPatch)); _harmony.PatchAll(typeof(StatsManagerStartPatch)); _harmony.PatchAll(typeof(LoadGamePatch)); PatchAutoRefillMethod(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Shop stock fix loaded for EnergyCrystalAutoRefill 4.0.0."); } catch (Exception arg) { _harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to apply shop stock fix: {arg}"); } } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } VirtualCrystalTracker.ClearTransientState(); } private static void PatchAutoRefillMethod(Harmony harmony) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_00e0: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("REPOJP.EnergyCrystalAutoRefill", out var value)) { throw new InvalidOperationException("Required plugin 'REPOJP.EnergyCrystalAutoRefill' is not loaded."); } Type type = AccessTools.Inner(((object)value.Instance).GetType(), "ChargingStationUpdatePostfixPatch"); if ((object)type == null) { throw new MissingMemberException(((object)value.Instance).GetType().FullName, "ChargingStationUpdatePostfixPatch"); } MethodInfo methodInfo = AccessTools.Method(type, "TryAddPowerCrystalPurchase", new Type[3] { typeof(ChargingStation), typeof(int), typeof(int) }, (Type[])null); if ((object)methodInfo == null) { throw new MissingMethodException(type.FullName, "TryAddPowerCrystalPurchase(ChargingStation, int, int)"); } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(AutoRefillTrackingPatch), "Prefix", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.Method(typeof(AutoRefillTrackingPatch), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } internal readonly struct PurchaseCountSnapshot { internal int Current { get; } internal int Total { get; } internal PurchaseCountSnapshot(int current, int total) { Current = current; Total = total; } internal static PurchaseCountSnapshot Capture() { StatsManager instance = StatsManager.instance; if (instance == null) { return default(PurchaseCountSnapshot); } instance.itemsPurchased.TryGetValue("Item Power Crystal", out var value); instance.itemsPurchasedTotal.TryGetValue("Item Power Crystal", out var value2); return new PurchaseCountSnapshot(value, value2); } } internal static class VirtualCrystalTracker { private const string RecordVersionKey = "RecordVersion"; private const string CurrentOffsetKey = "CurrentAutoRefillCrystals"; private const string TotalOffsetKey = "TotalAutoRefillCrystals"; private const int CurrentRecordVersion = 1; private static readonly Dictionary<string, int> SaveState = new Dictionary<string, int>(); private static readonly FieldInfo DictionaryRegistryField = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries"); private static readonly FieldInfo EnergyPerCrystalField = AccessTools.Field(typeof(ChargingStation), "energyPerCrystal"); private static readonly FieldInfo MaxCrystalsField = AccessTools.Field(typeof(ChargingStation), "maxCrystals"); private static int _currentAutoRefillCrystals; private static int _totalAutoRefillCrystals; private static int _shopInventoryQueryDepth; private static bool _legacyRepairPending; internal static bool IsShopInventoryQuery => _shopInventoryQueryDepth > 0; internal static void InitializeRuntime() { SaveState.Clear(); _currentAutoRefillCrystals = 0; _totalAutoRefillCrystals = 0; _shopInventoryQueryDepth = 0; _legacyRepairPending = false; } internal static void RegisterSaveState(StatsManager stats) { if (!(DictionaryRegistryField.GetValue(stats) is SortedDictionary<string, Dictionary<string, int>> sortedDictionary)) { throw new InvalidOperationException("StatsManager dictionary registry is unavailable."); } if (sortedDictionary.TryGetValue("EnergyCrystalShopStockFix", out var value)) { if (value == SaveState) { return; } SaveState.Clear(); foreach (KeyValuePair<string, int> item in value) { SaveState[item.Key] = item.Value; } sortedDictionary["EnergyCrystalShopStockFix"] = SaveState; } else { sortedDictionary.Add("EnergyCrystalShopStockFix", SaveState); } } internal static void BeginLoad(StatsManager stats) { RegisterSaveState(stats); SaveState.Clear(); _currentAutoRefillCrystals = 0; _totalAutoRefillCrystals = 0; _shopInventoryQueryDepth = 0; _legacyRepairPending = false; } internal static void FinishLoad() { if (SaveState.TryGetValue("RecordVersion", out var value) && value > 0) { _currentAutoRefillCrystals = GetSavedNonNegative("CurrentAutoRefillCrystals"); _totalAutoRefillCrystals = GetSavedNonNegative("TotalAutoRefillCrystals"); _legacyRepairPending = false; EnergyCrystalShopStockFixPlugin.ModLogger.LogDebug((object)($"Loaded per-save refill offsets: current={_currentAutoRefillCrystals}, " + $"total={_totalAutoRefillCrystals}.")); } else { _currentAutoRefillCrystals = 0; _totalAutoRefillCrystals = 0; _legacyRepairPending = true; EnergyCrystalShopStockFixPlugin.ModLogger.LogInfo((object)"Loaded a save without shop-stock-fix records. Legacy repair will run before the shop inventory is generated."); } } internal static void NormalizePurchaseCountForShop() { StatsManager instance = StatsManager.instance; ChargingStation instance2 = ChargingStation.instance; if (instance == null || instance2 == null || !instance.itemDictionary.TryGetValue("Item Power Crystal", out var value)) { return; } int num = Math.Max(0, value.maxAmountInShop); instance.itemsPurchased.TryGetValue("Item Power Crystal", out var value2); bool flag = false; if (value2 >= num) { instance.runStats.TryGetValue("chargingStationChargeTotal", out var value3); int num2 = Math.Max(1, (int)EnergyPerCrystalField.GetValue(instance2)); int num3 = Math.Max(0, (int)MaxCrystalsField.GetValue(instance2)); int num4 = Mathf.Clamp(Mathf.CeilToInt((float)value3 / (float)num2), 0, num3); int num5 = num4; int val = Math.Max(0, value2 - num5); if (num5 != value2) { instance.SetItemPurchase(value, num5); if ((Object)(object)PunManager.instance != (Object)null && SemiFunc.IsMasterClientOrSingleplayer()) { PunManager.instance.UpdateStat("itemsPurchased", "Item Power Crystal", num5); } flag = true; } _currentAutoRefillCrystals = 0; if (_legacyRepairPending) { _totalAutoRefillCrystals = Math.Max(_totalAutoRefillCrystals, val); } EnergyCrystalShopStockFixPlugin.ModLogger.LogWarning((object)($"Normalized Power Crystal purchase count for shop: {value2} -> " + $"{num5}. Remaining charge={value3}, " + $"rendered crystals={num4}, " + $"energyPerCrystal={num2}, maxCrystals={num3}, " + $"effective maxAmountInShop={num}.")); } else if (_legacyRepairPending) { EnergyCrystalShopStockFixPlugin.ModLogger.LogInfo((object)("Legacy save did not require repair. Power Crystal purchases=" + $"{value2}, effective maxAmountInShop={num}.")); } if (_legacyRepairPending || flag) { _legacyRepairPending = false; Persist(); } } internal static void RecordRefill(PurchaseCountSnapshot before, PurchaseCountSnapshot after) { int num = Math.Max(0, after.Current - before.Current); int num2 = Math.Max(0, after.Total - before.Total); _currentAutoRefillCrystals += num; _totalAutoRefillCrystals += num2; Persist(); if (num > 0 || num2 > 0) { EnergyCrystalShopStockFixPlugin.ModLogger.LogDebug((object)($"Tracked auto-refill crystals: current +{num}, total +{num2}. " + $"Offsets are now current={_currentAutoRefillCrystals}, total={_totalAutoRefillCrystals}.")); } } internal static void RecordConsumption(PurchaseCountSnapshot before, PurchaseCountSnapshot after) { int num = Math.Max(0, before.Current - after.Current); if (num != 0) { _currentAutoRefillCrystals = Math.Max(0, _currentAutoRefillCrystals - num); Persist(); } } internal static int RemoveCurrentAutoRefills(int purchaseCount) { return Math.Max(0, purchaseCount - _currentAutoRefillCrystals); } internal static int RemoveTotalAutoRefills(int purchaseCount) { return Math.Max(0, purchaseCount - _totalAutoRefillCrystals); } internal static void BeginShopInventoryQuery() { _shopInventoryQueryDepth++; } internal static void EndShopInventoryQuery() { _shopInventoryQueryDepth = Math.Max(0, _shopInventoryQueryDepth - 1); } internal static void Reset() { _currentAutoRefillCrystals = 0; _totalAutoRefillCrystals = 0; _shopInventoryQueryDepth = 0; _legacyRepairPending = false; Persist(); } internal static void ClearTransientState() { _shopInventoryQueryDepth = 0; } private static void Persist() { SaveState["RecordVersion"] = 1; SaveState["CurrentAutoRefillCrystals"] = Math.Max(0, _currentAutoRefillCrystals); SaveState["TotalAutoRefillCrystals"] = Math.Max(0, _totalAutoRefillCrystals); } private static int GetSavedNonNegative(string key) { if (!SaveState.TryGetValue(key, out var value)) { return 0; } return Math.Max(0, value); } } internal static class AutoRefillTrackingPatch { internal static void Prefix(out PurchaseCountSnapshot __state) { __state = PurchaseCountSnapshot.Capture(); } internal static void Postfix(PurchaseCountSnapshot __state) { VirtualCrystalTracker.RecordRefill(__state, PurchaseCountSnapshot.Capture()); } } [HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")] internal static class ShopInventoryScopePatch { [HarmonyPrefix] private static void Prefix() { VirtualCrystalTracker.NormalizePurchaseCountForShop(); VirtualCrystalTracker.BeginShopInventoryQuery(); } [HarmonyFinalizer] private static Exception? Finalizer(Exception? __exception) { VirtualCrystalTracker.EndShopInventoryQuery(); return __exception; } } [HarmonyPatch(typeof(SemiFunc), "StatGetItemsPurchased", new Type[] { typeof(string) })] internal static class CurrentPurchaseCountPatch { [HarmonyPostfix] private static void Postfix(string __0, ref int __result) { if (VirtualCrystalTracker.IsShopInventoryQuery && string.Equals(__0, "Item Power Crystal", StringComparison.Ordinal)) { __result = VirtualCrystalTracker.RemoveCurrentAutoRefills(__result); } } } [HarmonyPatch(typeof(StatsManager), "GetItemsUpgradesPurchasedTotal", new Type[] { typeof(string) })] internal static class TotalPurchaseCountPatch { [HarmonyPostfix] private static void Postfix(string __0, ref int __result) { if (VirtualCrystalTracker.IsShopInventoryQuery && string.Equals(__0, "Item Power Crystal", StringComparison.Ordinal)) { __result = VirtualCrystalTracker.RemoveTotalAutoRefills(__result); } } } [HarmonyPatch(typeof(ChargingStation), "ChargingStationCrystalBrokenRPC")] internal static class CrystalConsumedPatch { [HarmonyPrefix] private static void Prefix(out PurchaseCountSnapshot __state) { __state = PurchaseCountSnapshot.Capture(); } [HarmonyPostfix] private static void Postfix(PurchaseCountSnapshot __state) { VirtualCrystalTracker.RecordConsumption(__state, PurchaseCountSnapshot.Capture()); } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class NewRunPatch { [HarmonyPostfix] private static void Postfix(StatsManager __instance) { VirtualCrystalTracker.RegisterSaveState(__instance); VirtualCrystalTracker.Reset(); } } [HarmonyPatch(typeof(StatsManager), "Start")] internal static class StatsManagerStartPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(StatsManager __instance) { VirtualCrystalTracker.RegisterSaveState(__instance); } } [HarmonyPatch(typeof(StatsManager), "LoadGame", new Type[] { typeof(string), typeof(List<string>) })] internal static class LoadGamePatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(StatsManager __instance) { VirtualCrystalTracker.BeginLoad(__instance); } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { VirtualCrystalTracker.FinishLoad(); } } }