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 BatterySaveFix v1.1.0
BatterySaveFix.dll
Decompiled 6 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; 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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Kai")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BatterySaveFix")] [assembly: AssemblyTitle("BatterySaveFix")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 BatterySaveFix { internal static class BatteryRuntimeState { internal static readonly Dictionary<string, int> LiveBatteryByInstanceName = new Dictionary<string, int>(); internal static void Clear() { LiveBatteryByInstanceName.Clear(); } } internal static class BatterySaveRuntimeLogic { private static readonly MethodInfo? ItemBattery_BatteryInit = AccessTools.Method(typeof(ItemBattery), "BatteryInit", (Type[])null, (Type[])null); internal static void TryStartBatteryInit(ItemBattery battery) { try { if (!((Object)(object)battery == (Object)null) && !(ItemBattery_BatteryInit == null) && ItemBattery_BatteryInit.Invoke(battery, null) is IEnumerator enumerator) { ((MonoBehaviour)battery).StartCoroutine(enumerator); } } catch (Exception arg) { BatterySaveFix.Logger.LogWarning((object)$"Failed to start BatteryInit: {arg}"); } } internal static void RebuildLiveBatteryCache() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } BatteryRuntimeState.Clear(); try { ItemBattery[] array = Object.FindObjectsOfType<ItemBattery>(true); int num = 0; ItemBattery[] array2 = array; foreach (ItemBattery val in array2) { if ((Object)(object)val == (Object)null) { continue; } ItemAttributes component = ((Component)val).GetComponent<ItemAttributes>(); if (!((Object)(object)component == (Object)null)) { string instanceName = component.instanceName; if (!string.IsNullOrWhiteSpace(instanceName)) { int value = Mathf.RoundToInt(val.batteryLife); BatteryRuntimeState.LiveBatteryByInstanceName[instanceName] = value; num++; } } } BatterySaveFix.Logger.LogInfo((object)$"Live battery cache rebuilt. count={num}"); } catch (Exception arg) { BatterySaveFix.Logger.LogWarning((object)$"Failed to rebuild live battery cache: {arg}"); } } internal static int RepairLoadedOrSavingData(StatsManager sm, bool applyRuntimeBattery) { if ((Object)(object)sm == (Object)null) { return 0; } if (sm.item == null || sm.itemStatBattery == null || sm.itemsPurchased == null) { return 0; } Dictionary<string, int> item = sm.item; Dictionary<string, int> itemStatBattery = sm.itemStatBattery; Dictionary<string, int> itemsPurchased = sm.itemsPurchased; int num = 0; int number; foreach (string item2 in item.Keys.ToList()) { if (TryGetSuffixNumber(item2, out number) && !itemStatBattery.ContainsKey(item2)) { int value = 100; if (applyRuntimeBattery && BatteryRuntimeState.LiveBatteryByInstanceName.TryGetValue(item2, out var value2)) { value = value2; } itemStatBattery[item2] = value; } } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (KeyValuePair<string, int> item3 in itemsPurchased) { string key = item3.Key; int num2 = Mathf.Max(0, item3.Value); if (num2 > 0) { List<string> instanceKeysTopFirst = GetInstanceKeysTopFirst(item, key); for (int i = 0; i < num2 && i < instanceKeysTopFirst.Count; i++) { hashSet.Add(instanceKeysTopFirst[i]); } } } int count = item.Count; int count2 = itemStatBattery.Count; foreach (string item4 in item.Keys.ToList()) { if (TryGetSuffixNumber(item4, out number) && !hashSet.Contains(item4)) { item.Remove(item4); num++; } } foreach (string item5 in itemStatBattery.Keys.ToList()) { if (TryGetSuffixNumber(item5, out number) && !hashSet.Contains(item5)) { itemStatBattery.Remove(item5); } } if (applyRuntimeBattery) { foreach (string item6 in hashSet) { if (BatteryRuntimeState.LiveBatteryByInstanceName.TryGetValue(item6, out var value3)) { itemStatBattery[item6] = value3; } else if (!itemStatBattery.ContainsKey(item6)) { itemStatBattery[item6] = 100; } } } BatterySaveFix.Logger.LogInfo((object)$"Prune complete. item: {count} -> {item.Count}, battery: {count2} -> {itemStatBattery.Count}, removed={num}"); return num; } private static List<string> GetInstanceKeysTopFirst(Dictionary<string, int> itemDict, string baseName) { string baseName2 = baseName; int number2; return itemDict.Keys.Where((string k) => TryGetBaseName(k) == baseName2 && TryGetSuffixNumber(k, out number2)).OrderBy(delegate(string k) { TryGetSuffixNumber(k, out var number); return number; }).ToList(); } internal static string TryGetBaseName(string key) { int num = key.LastIndexOf('/'); if (num <= 0) { return key; } return key.Substring(0, num); } internal static bool TryGetSuffixNumber(string key, out int number) { number = 0; int num = key.LastIndexOf('/'); if (num <= 0 || num >= key.Length - 1) { return false; } return int.TryParse(key.Substring(num + 1), out number); } } [HarmonyPatch(typeof(ItemBattery), "Start")] internal static class ItemBattery_Start_Patch { private static void Postfix(ItemBattery __instance) { if (SemiFunc.IsMasterClientOrSingleplayer()) { BatterySaveRuntimeLogic.TryStartBatteryInit(__instance); } } } [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class StatsManager_LoadGame_Patch { private static void Prefix(string fileName) { if (SemiFunc.IsMasterClientOrSingleplayer()) { BatterySaveFix.Logger.LogInfo((object)("LoadGame Prefix called. fileName=" + fileName)); } } private static void Postfix(StatsManager __instance, string fileName) { BatterySaveFix.Logger.LogInfo((object)("LoadGame Postfix called. fileName=" + fileName)); try { int num = BatterySaveRuntimeLogic.RepairLoadedOrSavingData(__instance, applyRuntimeBattery: false); BatterySaveFix.Logger.LogInfo((object)$"LoadGame repaired = {num}"); } catch (Exception arg) { BatterySaveFix.Logger.LogError((object)$"LoadGame repair failed: {arg}"); } } } [HarmonyPatch(typeof(StatsManager), "SaveGame")] internal static class StatsManager_SaveGame_Patch { private static void Prefix(StatsManager __instance, string fileName) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } BatterySaveFix.Logger.LogInfo((object)("SaveGame Prefix called. fileName=" + fileName)); try { BatterySaveRuntimeLogic.RebuildLiveBatteryCache(); int num = BatterySaveRuntimeLogic.RepairLoadedOrSavingData(__instance, applyRuntimeBattery: true); BatterySaveFix.Logger.LogInfo((object)$"SaveGame repaired = {num}"); } catch (Exception arg) { BatterySaveFix.Logger.LogError((object)$"SaveGame repair failed: {arg}"); } } } [BepInPlugin("Kai.BatterySaveFix", "BatterySaveFix", "1.1.0")] public class BatterySaveFix : BaseUnityPlugin { internal static BatterySaveFix Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Patch(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } internal void Patch() { //IL_0019: 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_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } }