From b21db135dbb267f14ba001c06d5e767684e16748 Mon Sep 17 00:00:00 2001 From: thehun927 Date: Mon, 18 May 2026 22:56:16 -0400 Subject: [PATCH] remembering save states and splitting files into easier to read format --- AbsorbentSoil/AbsorbentSoilMod.cs | 363 +----------------- AbsorbentSoil/Helpers/PotKeyHelper.cs | 47 +++ AbsorbentSoil/Helpers/SaveSlotHelper.cs | 37 ++ .../Patches/PlantAdditiveAppliedPatch.cs | 33 ++ AbsorbentSoil/Patches/PlantInitializePatch.cs | 37 ++ .../Patches/PotApplyAdditivePatch.cs | 63 +++ .../Patches/PotHarvestDestroyPatches.cs | 47 +++ .../Patches/PourSoilTaskOnInitialPourPatch.cs | 50 +++ AbsorbentSoil/State/AdditiveMemory.cs | 104 +++++ AbsorbentSoil/State/PersistenceStore.cs | 200 ++++++++++ AbsorbentSoil/State/SoilHelper.cs | 96 +++++ .../bin/Release/net6.0/AbsorbentSoil.pdb | Bin 23124 -> 26396 bytes .../net6.0/AbsorbentSoil.AssemblyInfo.cs | 2 +- .../AbsorbentSoil.AssemblyInfoInputs.cache | 2 +- ...sorbentSoil.csproj.CoreCompileInputs.cache | 2 +- .../obj/Release/net6.0/AbsorbentSoil.dll | Bin 14336 -> 22528 bytes .../obj/Release/net6.0/AbsorbentSoil.pdb | Bin 23124 -> 26396 bytes .../obj/Release/net6.0/ref/AbsorbentSoil.dll | Bin 8192 -> 9728 bytes .../Release/net6.0/refint/AbsorbentSoil.dll | Bin 8192 -> 9728 bytes 19 files changed, 721 insertions(+), 362 deletions(-) create mode 100644 AbsorbentSoil/Helpers/PotKeyHelper.cs create mode 100644 AbsorbentSoil/Helpers/SaveSlotHelper.cs create mode 100644 AbsorbentSoil/Patches/PlantAdditiveAppliedPatch.cs create mode 100644 AbsorbentSoil/Patches/PlantInitializePatch.cs create mode 100644 AbsorbentSoil/Patches/PotApplyAdditivePatch.cs create mode 100644 AbsorbentSoil/Patches/PotHarvestDestroyPatches.cs create mode 100644 AbsorbentSoil/Patches/PourSoilTaskOnInitialPourPatch.cs create mode 100644 AbsorbentSoil/State/AdditiveMemory.cs create mode 100644 AbsorbentSoil/State/PersistenceStore.cs create mode 100644 AbsorbentSoil/State/SoilHelper.cs diff --git a/AbsorbentSoil/AbsorbentSoilMod.cs b/AbsorbentSoil/AbsorbentSoilMod.cs index b880cf6..0c06dcd 100644 --- a/AbsorbentSoil/AbsorbentSoilMod.cs +++ b/AbsorbentSoil/AbsorbentSoilMod.cs @@ -1,16 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using HarmonyLib; using MelonLoader; -using Il2CppInterop.Runtime; -using Il2CppScheduleOne.Growing; -using Il2CppScheduleOne.ItemFramework; -using Il2CppScheduleOne.ObjectScripts; -using Il2CppScheduleOne.PlayerTasks.Tasks; -using Il2CppScheduleOne.ObjectScripts.Soil; -using UnityEngine; [assembly: MelonInfo(typeof(AbsorbentSoil.AbsorbentSoilMod), "Absorbent Soil", "0.1.0", "AttilaG")] [assembly: MelonGame(null, "Schedule I")] @@ -23,6 +12,7 @@ namespace AbsorbentSoil public override void OnInitializeMelon() { + PersistenceStore.EnsureLoaded(); MelonLogger.Msg("Absorbent Soil loaded. Additive retention is active for long-life soils."); } @@ -36,355 +26,10 @@ namespace AbsorbentSoil sceneName.IndexOf("load", StringComparison.OrdinalIgnoreCase) >= 0)) { AdditiveMemory.Clear(); - MelonLogger.Msg($"Cleared additive memory on scene load: {sceneName}"); + SoilHelper.Clear(); + PersistenceStore.ReloadNow(); + MelonLogger.Msg($"Reloaded persisted memory on scene load: {sceneName}"); } } } - - internal static class AdditiveMemory - { - private static readonly Dictionary> AdditivesByPotKey = new(); - - public static void Remember(Pot pot, string additiveId) - { - if (pot == null || string.IsNullOrWhiteSpace(additiveId)) - return; - - string key = PotKeyHelper.GetPotKey(pot); - if (string.IsNullOrWhiteSpace(key)) - return; - - if (!AdditivesByPotKey.TryGetValue(key, out HashSet set)) - { - set = new HashSet(StringComparer.OrdinalIgnoreCase); - AdditivesByPotKey[key] = set; - } - - if (set.Add(additiveId) && AbsorbentSoilMod.VerboseLogging) - MelonLogger.Msg($"Remembered additive '{additiveId}' for pot '{key}'."); - } - - public static IReadOnlyList Get(Pot pot) - { - if (pot == null) - return Array.Empty(); - - string key = PotKeyHelper.GetPotKey(pot); - if (string.IsNullOrWhiteSpace(key)) - return Array.Empty(); - - return AdditivesByPotKey.TryGetValue(key, out HashSet set) - ? set.ToArray() - : Array.Empty(); - } - - public static bool HasStored(Pot pot) - { - if (pot == null) - return false; - - string key = PotKeyHelper.GetPotKey(pot); - return !string.IsNullOrWhiteSpace(key) && - AdditivesByPotKey.TryGetValue(key, out HashSet set) && - set.Count > 0; - } - - public static void Forget(Pot pot) - { - if (pot == null) - return; - - string key = PotKeyHelper.GetPotKey(pot); - if (!string.IsNullOrWhiteSpace(key)) - AdditivesByPotKey.Remove(key); - } - - public static void Clear() => AdditivesByPotKey.Clear(); - } - - internal static class PotKeyHelper - { - public static string GetPotKey(Pot pot) - { - if (pot == null) - return string.Empty; - - // Most Schedule I buildable/grid items have a GUID inherited from a base class. - // Use reflection so this survives minor EA API shifts. - object guid = ReadMember(pot, "GUID") ?? ReadMember(pot, "Guid") ?? ReadMember(pot, "guid"); - if (guid != null && !string.IsNullOrWhiteSpace(guid.ToString())) - return guid.ToString(); - - // Fallback for testing only. Not stable across reloads, but better than hard failing. - return $"scene:{pot.GetInstanceID()}"; - } - - private static object ReadMember(object instance, string name) - { - if (instance == null || string.IsNullOrWhiteSpace(name)) - return null; - - Type type = instance.GetType(); - - PropertyInfo prop = AccessTools.Property(type, name); - if (prop != null) - { - try { return prop.GetValue(instance); } catch { } - } - - FieldInfo field = AccessTools.Field(type, name); - if (field != null) - { - try { return field.GetValue(instance); } catch { } - } - - return null; - } - } - - internal static class SoilHelper - { - private static readonly Dictionary RemainingUsesByPotKey = new(); - - public static int DecrementRemainingSoilUses(Pot pot) - { - if (pot == null) - return 0; - - string key = PotKeyHelper.GetPotKey(pot); - if (string.IsNullOrWhiteSpace(key)) - return 0; - - int current = RemainingUsesByPotKey.TryGetValue(key, out int uses) ? uses : 0; - int next = Math.Max(0, current - 1); - - RemainingUsesByPotKey[key] = next; - return next; - } - - public static void SetRemainingSoilUses(Pot pot, int uses) - { - if (pot == null) - return; - - string key = PotKeyHelper.GetPotKey(pot); - if (string.IsNullOrWhiteSpace(key)) - return; - - RemainingUsesByPotKey[key] = uses; - - if (AbsorbentSoilMod.VerboseLogging) - MelonLogger.Msg($"Tracked soil uses for pot '{key}': {uses}"); - } - - public static int GetRemainingSoilUses(Pot pot) - { - if (pot == null) - return 0; - - string key = PotKeyHelper.GetPotKey(pot); - if (string.IsNullOrWhiteSpace(key)) - return 0; - - return RemainingUsesByPotKey.TryGetValue(key, out int uses) ? uses : 0; - } - - public static bool CanCaptureNewAdditives(Pot pot) - { - return GetRemainingSoilUses(pot) > 1; - } - - public static bool CanReapplyRetainedAdditives(Pot pot) - { - return GetRemainingSoilUses(pot) > 0; - } - - public static void Forget(Pot pot) - { - string key = PotKeyHelper.GetPotKey(pot); - if (!string.IsNullOrWhiteSpace(key)) - RemainingUsesByPotKey.Remove(key); - } - } - - [HarmonyPatch(typeof(Pot), "ApplyAdditive")] - internal static class Pot_ApplyAdditive_Patch - { - private static bool _suppressCapture; - - public static void Postfix(Pot __instance, AdditiveDefinition __result, string additiveID, bool isInitialApplication) - { - try - { - if (_suppressCapture) - return; - - if (__instance == null || __result == null || string.IsNullOrWhiteSpace(additiveID)) - return; - - if (!SoilHelper.CanCaptureNewAdditives(__instance)) - return; - - AdditiveMemory.Remember(__instance, additiveID); - } - catch (Exception ex) - { - MelonLogger.Warning($"ApplyAdditive postfix failed: {ex}"); - } - } - - public static void ReapplyWithoutRecapture(Pot pot, string additiveID) - { - if (pot == null || string.IsNullOrWhiteSpace(additiveID)) - return; - - MethodInfo applyAdditive = AccessTools.Method(typeof(Pot), "ApplyAdditive"); - if (applyAdditive == null) - { - MelonLogger.Warning("Could not find Pot.ApplyAdditive via reflection."); - return; - } - - try - { - _suppressCapture = true; - applyAdditive.Invoke(pot, new object[] { additiveID, true }); - } - catch (Exception ex) - { - MelonLogger.Warning($"Failed to reapply additive '{additiveID}': {ex}"); - } - finally - { - _suppressCapture = false; - } - } - } - - [HarmonyPatch(typeof(Plant), nameof(Plant.Initialize))] - internal static class Plant_Initialize_Patch - { - public static void Postfix(Plant __instance) - { - try - { - MelonLogger.Msg($"Plant.Initialize fired. Plant={__instance?.name}, Pot={__instance?.Pot?.name}"); - - if (__instance == null || __instance.Pot == null) - return; - - Pot actualPot = __instance.Pot; - var additiveIds = AdditiveMemory.Get(actualPot); - - MelonLogger.Msg($"Plant.Initialize pot key={PotKeyHelper.GetPotKey(actualPot)}, remembered additives={additiveIds.Count}"); - - foreach (string additiveId in additiveIds) - Pot_ApplyAdditive_Patch.ReapplyWithoutRecapture(actualPot, additiveId); - } - catch (Exception ex) - { - MelonLogger.Warning($"Plant.Initialize postfix failed: {ex}"); - } - } - } - - [HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")] - internal static class Pot_OnPlantFullyHarvested_Patch - { - private static void Postfix(Pot __instance) - { - try - { - int remaining = SoilHelper.DecrementRemainingSoilUses(__instance); - - MelonLogger.Msg($"[Absorbent Soil] Harvest consumed soil use. Remaining uses={remaining}"); - - if (remaining <= 0) - { - AdditiveMemory.Forget(__instance); - SoilHelper.Forget(__instance); - MelonLogger.Msg($"[Absorbent Soil] Soil depleted. Cleared additives for pot '{PotKeyHelper.GetPotKey(__instance)}'."); - } - } - catch (Exception ex) - { - MelonLogger.Warning($"OnPlantFullyHarvested postfix failed: {ex}"); - } - } - } - - [HarmonyPatch(typeof(Plant), nameof(Plant.AdditiveApplied))] - internal static class Plant_AdditiveApplied_Patch - { - public static void Postfix(Plant __instance, AdditiveDefinition additive, bool isInitialApplication) - { - try - { - MelonLogger.Msg($"Plant.AdditiveApplied fired. Plant={__instance?.name}, Pot={__instance?.Pot?.name}, Additive={additive?.ID}, Initial={isInitialApplication}"); - - if (__instance == null || __instance.Pot == null || additive == null) - return; - - string additiveId = additive.ID; - if (string.IsNullOrWhiteSpace(additiveId)) - return; - - AdditiveMemory.Remember(__instance.Pot, additiveId); - } - catch (Exception ex) - { - MelonLogger.Warning($"Plant.AdditiveApplied postfix failed: {ex}"); - } - } - } - - [HarmonyPatch(typeof(PourSoilTask), "OnInitialPour")] - internal static class PourSoilTask_OnInitialPour_Patch - { - private static unsafe void Postfix(PourSoilTask __instance) - { - try - { - nint basePtr = (nint)IL2CPP.Il2CppObjectBaseToPtrNotNull(__instance); - - nint soilDefPtr = *(nint*)(basePtr + 0xD0); - nint growContainerPtr = *(nint*)(basePtr + 0xE8); - - SoilDefinition soilDef = soilDefPtr != 0 - ? new SoilDefinition(soilDefPtr) - : null; - - GrowContainer growContainer = growContainerPtr != 0 - ? new GrowContainer(growContainerPtr) - : null; - - Pot pot = growContainer?.TryCast(); - - MelonLogger.Msg($"PourSoilTask.OnInitialPour offset-read fired. Soil={soilDef?.ID}, Uses={soilDef?.Uses}, Pot={pot?.name}"); - - if (pot == null || soilDef == null) - return; - - SoilHelper.SetRemainingSoilUses(pot, soilDef.Uses); - AdditiveMemory.Forget(pot); - - MelonLogger.Msg($"[Absorbent Soil] Fresh soil added. Pot='{PotKeyHelper.GetPotKey(pot)}', Soil='{soilDef.ID}', Uses={soilDef.Uses}. Cleared old additives."); - } - catch (Exception ex) - { - MelonLogger.Warning($"PourSoilTask.OnInitialPour postfix failed: {ex}"); - } - } - } - - [HarmonyPatch(typeof(Pot), "Destroy")] - internal static class Pot_Destroy_Patch - { - public static void Prefix(Pot __instance) - { - // If the pot itself is destroyed/sold, stop tracking its additives. - try { AdditiveMemory.Forget(__instance); } - catch { } - } - } } diff --git a/AbsorbentSoil/Helpers/PotKeyHelper.cs b/AbsorbentSoil/Helpers/PotKeyHelper.cs new file mode 100644 index 0000000..544dfc8 --- /dev/null +++ b/AbsorbentSoil/Helpers/PotKeyHelper.cs @@ -0,0 +1,47 @@ +using System; +using System.Reflection; +using HarmonyLib; +using Il2CppScheduleOne.ObjectScripts; + +namespace AbsorbentSoil +{ + internal static class PotKeyHelper + { + public static string GetPotKey(Pot pot) + { + if (pot == null) + return string.Empty; + + // Most Schedule I buildable/grid items have a GUID inherited from a base class. + // Use reflection so this survives minor EA API shifts. + object guid = ReadMember(pot, "GUID") ?? ReadMember(pot, "Guid") ?? ReadMember(pot, "guid"); + if (guid != null && !string.IsNullOrWhiteSpace(guid.ToString())) + return guid.ToString(); + + // Fallback for testing only. Not stable across reloads, but better than hard failing. + return $"scene:{pot.GetInstanceID()}"; + } + + private static object ReadMember(object instance, string name) + { + if (instance == null || string.IsNullOrWhiteSpace(name)) + return null; + + Type type = instance.GetType(); + + PropertyInfo prop = AccessTools.Property(type, name); + if (prop != null) + { + try { return prop.GetValue(instance); } catch { } + } + + FieldInfo field = AccessTools.Field(type, name); + if (field != null) + { + try { return field.GetValue(instance); } catch { } + } + + return null; + } + } +} diff --git a/AbsorbentSoil/Helpers/SaveSlotHelper.cs b/AbsorbentSoil/Helpers/SaveSlotHelper.cs new file mode 100644 index 0000000..9e1b61e --- /dev/null +++ b/AbsorbentSoil/Helpers/SaveSlotHelper.cs @@ -0,0 +1,37 @@ +using Il2CppScheduleOne.Persistence; +using MelonLoader; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +internal static class SaveSlotHelper +{ + public static string GetCurrentSaveKey() + { + try + { + string path = LoadManager.Instance?.LoadedGameFolderPath; + + if (!string.IsNullOrWhiteSpace(path)) + return HashPath(path); + + string saveName = SaveManager.Instance?.SaveName; + + if (!string.IsNullOrWhiteSpace(saveName)) + return SaveManager.SanitizeFileName(saveName); + + return "global"; + } + catch + { + return "global"; + } + } + + private static string HashPath(string path) + { + using var sha1 = SHA1.Create(); + byte[] bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(path.ToLowerInvariant())); + return System.BitConverter.ToString(bytes).Replace("-", "").Substring(0, 12); + } +} \ No newline at end of file diff --git a/AbsorbentSoil/Patches/PlantAdditiveAppliedPatch.cs b/AbsorbentSoil/Patches/PlantAdditiveAppliedPatch.cs new file mode 100644 index 0000000..4a7957d --- /dev/null +++ b/AbsorbentSoil/Patches/PlantAdditiveAppliedPatch.cs @@ -0,0 +1,33 @@ +using System; +using HarmonyLib; +using Il2CppScheduleOne.Growing; +using Il2CppScheduleOne.ItemFramework; +using MelonLoader; + +namespace AbsorbentSoil +{ + [HarmonyPatch(typeof(Plant), nameof(Plant.AdditiveApplied))] + internal static class Plant_AdditiveApplied_Patch + { + public static void Postfix(Plant __instance, AdditiveDefinition additive, bool isInitialApplication) + { + try + { + MelonLogger.Msg($"Plant.AdditiveApplied fired. Plant={__instance?.name}, Pot={__instance?.Pot?.name}, Additive={additive?.ID}, Initial={isInitialApplication}"); + + if (__instance == null || __instance.Pot == null || additive == null) + return; + + string additiveId = additive.ID; + if (string.IsNullOrWhiteSpace(additiveId)) + return; + + AdditiveMemory.Remember(__instance.Pot, additiveId); + } + catch (Exception ex) + { + MelonLogger.Warning($"Plant.AdditiveApplied postfix failed: {ex}"); + } + } + } +} diff --git a/AbsorbentSoil/Patches/PlantInitializePatch.cs b/AbsorbentSoil/Patches/PlantInitializePatch.cs new file mode 100644 index 0000000..39b1f56 --- /dev/null +++ b/AbsorbentSoil/Patches/PlantInitializePatch.cs @@ -0,0 +1,37 @@ +using System; +using HarmonyLib; +using Il2CppScheduleOne.Growing; +using Il2CppScheduleOne.ObjectScripts; +using MelonLoader; + +namespace AbsorbentSoil +{ + [HarmonyPatch(typeof(Plant), nameof(Plant.Initialize))] + internal static class Plant_Initialize_Patch + { + public static void Postfix(Plant __instance) + { + try + { + PersistenceStore.EnsureLoaded(); + + MelonLogger.Msg($"Plant.Initialize fired. Plant={__instance?.name}, Pot={__instance?.Pot?.name}"); + + if (__instance == null || __instance.Pot == null) + return; + + Pot actualPot = __instance.Pot; + var additiveIds = AdditiveMemory.Get(actualPot); + + MelonLogger.Msg($"Plant.Initialize pot key={PotKeyHelper.GetPotKey(actualPot)}, remembered additives={additiveIds.Count}"); + + foreach (string additiveId in additiveIds) + Pot_ApplyAdditive_Patch.ReapplyWithoutRecapture(actualPot, additiveId); + } + catch (Exception ex) + { + MelonLogger.Warning($"Plant.Initialize postfix failed: {ex}"); + } + } + } +} diff --git a/AbsorbentSoil/Patches/PotApplyAdditivePatch.cs b/AbsorbentSoil/Patches/PotApplyAdditivePatch.cs new file mode 100644 index 0000000..a4607f8 --- /dev/null +++ b/AbsorbentSoil/Patches/PotApplyAdditivePatch.cs @@ -0,0 +1,63 @@ +using System; +using System.Reflection; +using HarmonyLib; +using Il2CppScheduleOne.ItemFramework; +using Il2CppScheduleOne.ObjectScripts; +using MelonLoader; + +namespace AbsorbentSoil +{ + [HarmonyPatch(typeof(Pot), "ApplyAdditive")] + internal static class Pot_ApplyAdditive_Patch + { + private static bool _suppressCapture; + + public static void Postfix(Pot __instance, AdditiveDefinition __result, string additiveID, bool isInitialApplication) + { + try + { + if (_suppressCapture) + return; + + if (__instance == null || __result == null || string.IsNullOrWhiteSpace(additiveID)) + return; + + if (!SoilHelper.CanCaptureNewAdditives(__instance)) + return; + + AdditiveMemory.Remember(__instance, additiveID); + } + catch (Exception ex) + { + MelonLogger.Warning($"ApplyAdditive postfix failed: {ex}"); + } + } + + public static void ReapplyWithoutRecapture(Pot pot, string additiveID) + { + if (pot == null || string.IsNullOrWhiteSpace(additiveID)) + return; + + MethodInfo applyAdditive = AccessTools.Method(typeof(Pot), "ApplyAdditive"); + if (applyAdditive == null) + { + MelonLogger.Warning("Could not find Pot.ApplyAdditive via reflection."); + return; + } + + try + { + _suppressCapture = true; + applyAdditive.Invoke(pot, new object[] { additiveID, true }); + } + catch (Exception ex) + { + MelonLogger.Warning($"Failed to reapply additive '{additiveID}': {ex}"); + } + finally + { + _suppressCapture = false; + } + } + } +} diff --git a/AbsorbentSoil/Patches/PotHarvestDestroyPatches.cs b/AbsorbentSoil/Patches/PotHarvestDestroyPatches.cs new file mode 100644 index 0000000..6640dc2 --- /dev/null +++ b/AbsorbentSoil/Patches/PotHarvestDestroyPatches.cs @@ -0,0 +1,47 @@ +using System; +using HarmonyLib; +using Il2CppScheduleOne.ObjectScripts; +using MelonLoader; + +namespace AbsorbentSoil +{ + [HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")] + internal static class Pot_OnPlantFullyHarvested_Patch + { + private static void Postfix(Pot __instance) + { + try + { + int remaining = SoilHelper.DecrementRemainingSoilUses(__instance); + + MelonLogger.Msg($"[Absorbent Soil] Harvest consumed soil use. Remaining uses={remaining}"); + + if (remaining <= 0) + { + AdditiveMemory.Forget(__instance); + SoilHelper.Forget(__instance); + MelonLogger.Msg($"[Absorbent Soil] Soil depleted. Cleared additives for pot '{PotKeyHelper.GetPotKey(__instance)}'."); + } + } + catch (Exception ex) + { + MelonLogger.Warning($"OnPlantFullyHarvested postfix failed: {ex}"); + } + } + } + + [HarmonyPatch(typeof(Pot), "Destroy")] + internal static class Pot_Destroy_Patch + { + public static void Prefix(Pot __instance) + { + // If the pot itself is destroyed/sold, stop tracking its additives. + try + { + AdditiveMemory.Forget(__instance); + SoilHelper.Forget(__instance); + } + catch { } + } + } +} diff --git a/AbsorbentSoil/Patches/PourSoilTaskOnInitialPourPatch.cs b/AbsorbentSoil/Patches/PourSoilTaskOnInitialPourPatch.cs new file mode 100644 index 0000000..b11c28b --- /dev/null +++ b/AbsorbentSoil/Patches/PourSoilTaskOnInitialPourPatch.cs @@ -0,0 +1,50 @@ +using System; +using HarmonyLib; +using Il2CppInterop.Runtime; +using Il2CppScheduleOne.Growing; +using Il2CppScheduleOne.ItemFramework; +using Il2CppScheduleOne.ObjectScripts; +using Il2CppScheduleOne.PlayerTasks.Tasks; +using MelonLoader; + +namespace AbsorbentSoil +{ + [HarmonyPatch(typeof(PourSoilTask), "OnInitialPour")] + internal static class PourSoilTask_OnInitialPour_Patch + { + private static unsafe void Postfix(PourSoilTask __instance) + { + try + { + nint basePtr = (nint)IL2CPP.Il2CppObjectBaseToPtrNotNull(__instance); + + nint soilDefPtr = *(nint*)(basePtr + 0xD0); + nint growContainerPtr = *(nint*)(basePtr + 0xE8); + + SoilDefinition soilDef = soilDefPtr != 0 + ? new SoilDefinition(soilDefPtr) + : null; + + GrowContainer growContainer = growContainerPtr != 0 + ? new GrowContainer(growContainerPtr) + : null; + + Pot pot = growContainer?.TryCast(); + + MelonLogger.Msg($"PourSoilTask.OnInitialPour offset-read fired. Soil={soilDef?.ID}, Uses={soilDef?.Uses}, Pot={pot?.name}"); + + if (pot == null || soilDef == null) + return; + + SoilHelper.SetRemainingSoilUses(pot, soilDef.Uses); + AdditiveMemory.Forget(pot); + + MelonLogger.Msg($"[Absorbent Soil] Fresh soil added. Pot='{PotKeyHelper.GetPotKey(pot)}', Soil='{soilDef.ID}', Uses={soilDef.Uses}. Cleared old additives."); + } + catch (Exception ex) + { + MelonLogger.Warning($"PourSoilTask.OnInitialPour postfix failed: {ex}"); + } + } + } +} diff --git a/AbsorbentSoil/State/AdditiveMemory.cs b/AbsorbentSoil/State/AdditiveMemory.cs new file mode 100644 index 0000000..a153d0c --- /dev/null +++ b/AbsorbentSoil/State/AdditiveMemory.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Il2CppScheduleOne.ObjectScripts; +using MelonLoader; + +namespace AbsorbentSoil +{ + internal static class AdditiveMemory + { + private static readonly Dictionary> AdditivesByPotKey = new(); + + public static void Remember(Pot pot, string additiveId) + { + if (pot == null || string.IsNullOrWhiteSpace(additiveId)) + return; + + string key = PotKeyHelper.GetPotKey(pot); + if (string.IsNullOrWhiteSpace(key)) + return; + + if (!AdditivesByPotKey.TryGetValue(key, out HashSet set)) + { + set = new HashSet(StringComparer.OrdinalIgnoreCase); + AdditivesByPotKey[key] = set; + } + + if (set.Add(additiveId)) + { + if (AbsorbentSoilMod.VerboseLogging) + MelonLogger.Msg($"Remembered additive '{additiveId}' for pot '{key}'."); + + PersistenceStore.SaveNow(); + } + } + + public static IReadOnlyList Get(Pot pot) + { + if (pot == null) + return Array.Empty(); + + string key = PotKeyHelper.GetPotKey(pot); + if (string.IsNullOrWhiteSpace(key)) + return Array.Empty(); + + return AdditivesByPotKey.TryGetValue(key, out HashSet set) + ? set.ToArray() + : Array.Empty(); + } + + public static bool HasStored(Pot pot) + { + if (pot == null) + return false; + + string key = PotKeyHelper.GetPotKey(pot); + return !string.IsNullOrWhiteSpace(key) && + AdditivesByPotKey.TryGetValue(key, out HashSet set) && + set.Count > 0; + } + + public static void Forget(Pot pot) + { + if (pot == null) + return; + + string key = PotKeyHelper.GetPotKey(pot); + if (!string.IsNullOrWhiteSpace(key) && AdditivesByPotKey.Remove(key)) + PersistenceStore.SaveNow(); + } + + public static Dictionary> Export() + { + return AdditivesByPotKey.ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value.ToList(), + StringComparer.OrdinalIgnoreCase); + } + + public static void Import(Dictionary> additivesByPotKey) + { + AdditivesByPotKey.Clear(); + + if (additivesByPotKey == null) + return; + + foreach (var kvp in additivesByPotKey) + { + if (string.IsNullOrWhiteSpace(kvp.Key) || kvp.Value == null) + continue; + + var set = new HashSet( + kvp.Value.Where(id => !string.IsNullOrWhiteSpace(id)), + StringComparer.OrdinalIgnoreCase); + + if (set.Count > 0) + AdditivesByPotKey[kvp.Key] = set; + } + } + + // Memory-only clear for changing scenes/saves. Do not call SaveNow here or you can wipe the JSON. + public static void Clear() => AdditivesByPotKey.Clear(); + } +} diff --git a/AbsorbentSoil/State/PersistenceStore.cs b/AbsorbentSoil/State/PersistenceStore.cs new file mode 100644 index 0000000..553d139 --- /dev/null +++ b/AbsorbentSoil/State/PersistenceStore.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using MelonLoader; +using MelonLoader.Preferences; +using MelonLoader.Utils; +using System.Security.Cryptography; +using System.Text; +using Il2CppScheduleOne.Persistence; + +namespace AbsorbentSoil +{ + internal static class PersistenceStore + { + private const int CurrentVersion = 1; + private static bool _loaded; + private static bool _isLoading; + + private static string DirectoryPath => Path.Combine( + MelonEnvironment.UserDataDirectory, + "AbsorbentSoil", + GetCurrentSaveKey()); + + private static string FilePath => Path.Combine(DirectoryPath, "state.json"); + + private static string GetCurrentSaveKey() + { + try + { + string loadedPath = LoadManager.Instance?.LoadedGameFolderPath; + + if (!string.IsNullOrWhiteSpace(loadedPath)) + return HashPath(loadedPath); + + string saveName = SaveManager.Instance?.SaveName; + + if (!string.IsNullOrWhiteSpace(saveName)) + return SaveManager.SanitizeFileName(saveName); + + return "global"; + } + catch + { + return "global"; + } + } + + private static string HashPath(string path) + { + using SHA1 sha1 = SHA1.Create(); + + byte[] bytes = sha1.ComputeHash( + Encoding.UTF8.GetBytes(path.ToLowerInvariant())); + + return BitConverter + .ToString(bytes) + .Replace("-", "") + .Substring(0, 12); + } + + public static void EnsureLoaded() + { + if (_loaded) + return; + + LoadNow(); + } + + public static void ReloadNow() + { + _loaded = false; + LoadNow(); + } + + public static void LoadNow() + { + if (_isLoading) + return; + + _isLoading = true; + try + { + _loaded = true; + if (AbsorbentSoilMod.VerboseLogging) + { + MelonLogger.Msg($"[Absorbent Soil] LoadedGameFolderPath = {LoadManager.Instance?.LoadedGameFolderPath}"); + MelonLogger.Msg($"[Absorbent Soil] SaveName = {SaveManager.Instance?.SaveName}"); + MelonLogger.Msg($"[Absorbent Soil] SaveKey = {GetCurrentSaveKey()}"); + MelonLogger.Msg($"[Absorbent Soil] Persistence file = {FilePath}"); + } + + if (!File.Exists(FilePath)) + { + if (AbsorbentSoilMod.VerboseLogging) + MelonLogger.Msg("[Absorbent Soil] No persistence file found yet."); + return; + } + + string json = File.ReadAllText(FilePath); + PersistedState state = JsonSerializer.Deserialize(json); + if (state == null || state.Pots == null) + return; + + AdditiveMemory.Import(state.Pots.ToDictionary( + kvp => kvp.Key, + kvp => (IEnumerable)(kvp.Value?.Additives ?? new List()), + StringComparer.OrdinalIgnoreCase)); + + SoilHelper.Import(state.Pots.ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value?.RemainingUses ?? 0, + StringComparer.OrdinalIgnoreCase)); + + MelonLogger.Msg($"[Absorbent Soil] Loaded persisted state for {state.Pots.Count} pot(s)."); + } + catch (Exception ex) + { + MelonLogger.Warning($"[Absorbent Soil] Failed to load persisted state: {ex}"); + } + finally + { + _isLoading = false; + } + } + + public static void SaveNow() + { + if (_isLoading) + return; + + try + { + Directory.CreateDirectory(DirectoryPath); + + Dictionary> additiveSnapshot = AdditiveMemory.Export(); + Dictionary soilSnapshot = SoilHelper.Export(); + + var keys = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (string key in additiveSnapshot.Keys) + keys.Add(key); + foreach (string key in soilSnapshot.Keys) + keys.Add(key); + + var state = new PersistedState + { + Version = CurrentVersion, + UpdatedUtc = DateTime.UtcNow, + Pots = new Dictionary(StringComparer.OrdinalIgnoreCase) + }; + + foreach (string key in keys) + { + additiveSnapshot.TryGetValue(key, out List additives); + soilSnapshot.TryGetValue(key, out int remainingUses); + + additives ??= new List(); + + // Avoid keeping dead/empty records forever. + if (remainingUses <= 0 && additives.Count == 0) + continue; + + state.Pots[key] = new PersistedPotState + { + RemainingUses = remainingUses, + Additives = additives + .Where(id => !string.IsNullOrWhiteSpace(id)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(id => id, StringComparer.OrdinalIgnoreCase) + .ToList() + }; + } + + var options = new JsonSerializerOptions { WriteIndented = true }; + File.WriteAllText(FilePath, JsonSerializer.Serialize(state, options)); + + if (AbsorbentSoilMod.VerboseLogging) + MelonLogger.Msg($"[Absorbent Soil] Saved persisted state for {state.Pots.Count} pot(s)."); + } + catch (Exception ex) + { + MelonLogger.Warning($"[Absorbent Soil] Failed to save persisted state: {ex}"); + } + } + + private sealed class PersistedState + { + public int Version { get; set; } + public DateTime UpdatedUtc { get; set; } + public Dictionary Pots { get; set; } = new(StringComparer.OrdinalIgnoreCase); + } + + private sealed class PersistedPotState + { + public int RemainingUses { get; set; } + public List Additives { get; set; } = new(); + } + } +} diff --git a/AbsorbentSoil/State/SoilHelper.cs b/AbsorbentSoil/State/SoilHelper.cs new file mode 100644 index 0000000..9408b69 --- /dev/null +++ b/AbsorbentSoil/State/SoilHelper.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using Il2CppScheduleOne.ObjectScripts; +using MelonLoader; + +namespace AbsorbentSoil +{ + internal static class SoilHelper + { + private static readonly Dictionary RemainingUsesByPotKey = new(); + + public static int DecrementRemainingSoilUses(Pot pot) + { + if (pot == null) + return 0; + + string key = PotKeyHelper.GetPotKey(pot); + if (string.IsNullOrWhiteSpace(key)) + return 0; + + int current = RemainingUsesByPotKey.TryGetValue(key, out int uses) ? uses : 0; + int next = Math.Max(0, current - 1); + + RemainingUsesByPotKey[key] = next; + PersistenceStore.SaveNow(); + return next; + } + + public static void SetRemainingSoilUses(Pot pot, int uses) + { + if (pot == null) + return; + + string key = PotKeyHelper.GetPotKey(pot); + if (string.IsNullOrWhiteSpace(key)) + return; + + RemainingUsesByPotKey[key] = uses; + + if (AbsorbentSoilMod.VerboseLogging) + MelonLogger.Msg($"Tracked soil uses for pot '{key}': {uses}"); + + PersistenceStore.SaveNow(); + } + + public static int GetRemainingSoilUses(Pot pot) + { + if (pot == null) + return 0; + + string key = PotKeyHelper.GetPotKey(pot); + if (string.IsNullOrWhiteSpace(key)) + return 0; + + return RemainingUsesByPotKey.TryGetValue(key, out int uses) ? uses : 0; + } + + public static bool CanCaptureNewAdditives(Pot pot) + { + return GetRemainingSoilUses(pot) > 1; + } + + public static bool CanReapplyRetainedAdditives(Pot pot) + { + return GetRemainingSoilUses(pot) > 0; + } + + public static void Forget(Pot pot) + { + string key = PotKeyHelper.GetPotKey(pot); + if (!string.IsNullOrWhiteSpace(key) && RemainingUsesByPotKey.Remove(key)) + PersistenceStore.SaveNow(); + } + + public static Dictionary Export() + { + return new Dictionary(RemainingUsesByPotKey, StringComparer.OrdinalIgnoreCase); + } + + public static void Import(Dictionary remainingUsesByPotKey) + { + RemainingUsesByPotKey.Clear(); + + if (remainingUsesByPotKey == null) + return; + + foreach (var kvp in remainingUsesByPotKey) + { + if (!string.IsNullOrWhiteSpace(kvp.Key)) + RemainingUsesByPotKey[kvp.Key] = Math.Max(0, kvp.Value); + } + } + + public static void Clear() => RemainingUsesByPotKey.Clear(); + } +} diff --git a/AbsorbentSoil/bin/Release/net6.0/AbsorbentSoil.pdb b/AbsorbentSoil/bin/Release/net6.0/AbsorbentSoil.pdb index f219977c6072a785522a930ab67962c11329d22a..b55dff98920217d5f5580a3ef878626aaa1b509f 100644 GIT binary patch delta 5279 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 JDxMps>mO5q7We=F diff --git a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs index bc3144f..f285866 100644 --- a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs +++ b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+df8a62295eea38ad16e750280eea878da48af3ae")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+162a1ce7951370e4cbd8c674c284b30f8a85cbd4")] [assembly: System.Reflection.AssemblyProductAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyTitleAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache index 4f9bf71..73a7919 100644 --- a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache +++ b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache @@ -1 +1 @@ -205b227cc6bf36a9df78289d110ec64054a771277aee03d496358bdc65fdfb7b +67d62e0901b35175e30da93a43a11d964fe004e1a9681a2e3903abe5b9ef6961 diff --git a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache index 62a182f..8297418 100644 --- a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache +++ b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -ddb36bf3fc2e77af19df7cc3d5e652bab5098b8451766fb4c9483694133ef288 +5c030080472ae1536e3626b995d635d8af1656efb3cdf494e4087a4281d4934d diff --git a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.dll b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.dll index 6da0a1d3d8309c36b9660eaff160ba38ee3704b2..f598550c94347ef5c13ab7d45ff6aab71782d92e 100644 GIT binary patch literal 22528 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 Ozi_|*+1>vW7WltRCiAHP literal 14336 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 diff --git a/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.pdb b/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.pdb index f219977c6072a785522a930ab67962c11329d22a..b55dff98920217d5f5580a3ef878626aaa1b509f 100644 GIT binary patch delta 5279 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 JDxMps>mO5q7We=F diff --git a/AbsorbentSoil/obj/Release/net6.0/ref/AbsorbentSoil.dll b/AbsorbentSoil/obj/Release/net6.0/ref/AbsorbentSoil.dll index 145db4b0a93089f68fc2399ed33669ecf50f46ef..d279ecee844fac2ae536f966b53250e223881b1f 100644 GIT binary patch literal 9728 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 XE;IiB1=*;+&EDX)-)QdtJOcj(8_Wt| delta 2032 zcmZ`)TWC~A82)C?C7Vs$?4D$^n#(pCQ!kNpb2DqTZ8SxVu|yLSk=EiddlD8k8nbB) zrJhwmEUD6EAjCe{LLV$ekaTSyEq%}zebB!7BDPSJ+M-xQ8jAFvIlG5IahCJV|9#&- z*E4fAyEEJU#km#p%~$g%lm}SRP{tBK6$t@Pd~+c8-Eip9ZlGTZ2Y@*(x4g1E3zQST zO-7wq&ew^3tUuUok#KfYPaKr-#G9o^Ieo1Yp!@m)K>lc69@2JP915`U8{V!OZdW_}HXub-N7Escsw^ z#2t#mGTt`S`!3ZFh|kHkTMi#RNL&_99q%0iN+nrf?WiV!!ZUy2EA%YZ=-NfHs#3lgnaZ+{S)mx6dkR>XLCV@+NRg+wT(5kR`g&yrtR1 zOvPq&kCiEwEwFOM-q1YHQfym+RVh|cU{S^X=3b`9J}h|E%5xQSIlYnB@l1P&UEOl# zXP3^=irFG2*W!6M(X+_AiDCTVdtN)OkU5J(`cqn%*KQVn8ZYSd71a>HhSNf198__iAA8EHkh^H2WAiGTVTL;5 zctJUux;8o%v|>J2ikDN&{E_aUR>+y5i#Vqkf^oKrZpQ_q1!Z`PwV)DP32WgHHj>+d zW=s;c;|Icxxam^W1D&^^4>7_)tR#FNt%M^=KSbDrGt{^TLBbIgj}+n=I9y`_pYuk- zGOdU3j5Z=;{S4tb{Tsrl5ukgu8G2Uk-QwcjqV1$t=9eVCF2p6Yk;RhnzD`A|>LI0Z znh5QAp|lmd>K$7D7x%z;*Hdhcjz>ag=R>{Ke9Nq^+fM|8V={D^o`Q6d&9 zYD;}x^oDm@gVU24=Sa)I)OaR+#KE5FW8&vQ-Nr+tiYvOwKS`m)bi?LYe|Nbw;mDs~! zZU&47&H{9vIkTKlDOl7>vykN&U|b=|^869^)PUu9$$?VnWLgs;PA0Ra0?-Jp2`*kP zd$}exnzYxqwQcBh9J@Vfr&`xLU7d-xWP*6IE19x8lJ;o3?Lee!SF@AiLjAaSUKbXj zx)VHmR?(^Yr6O1RsW9W`#rgVaA>vVSIUcdX1I673*2=g$x4fFy4dj1|uVW&?@8m-b z3%oytQ6ynwJ=*BE0iAH@XVX!XY6`97IOrlhL4K0Z^OK|{$+yv=GDoR}4M*&4oadX` K)cb*bPaDVqYA!0ScN!u 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 XE;IiB1=*;+&EDX)-)QdtJOcj(8_Wt| delta 2032 zcmZ`)TWC~A82)C?C7Vs$?4D$^n#(pCQ!kNpb2DqTZ8SxVu|yLSk=EiddlD8k8nbB) zrJhwmEUD6EAjCe{LLV$ekaTSyEq%}zebB!7BDPSJ+M-xQ8jAFvIlG5IahCJV|9#&- z*E4fAyEEJU#km#p%~$g%lm}SRP{tBK6$t@Pd~+c8-Eip9ZlGTZ2Y@*(x4g1E3zQST zO-7wq&ew^3tUuUok#KfYPaKr-#G9o^Ieo1Yp!@m)K>lc69@2JP915`U8{V!OZdW_}HXub-N7Escsw^ z#2t#mGTt`S`!3ZFh|kHkTMi#RNL&_99q%0iN+nrf?WiV!!ZUy2EA%YZ=-NfHs#3lgnaZ+{S)mx6dkR>XLCV@+NRg+wT(5kR`g&yrtR1 zOvPq&kCiEwEwFOM-q1YHQfym+RVh|cU{S^X=3b`9J}h|E%5xQSIlYnB@l1P&UEOl# zXP3^=irFG2*W!6M(X+_AiDCTVdtN)OkU5J(`cqn%*KQVn8ZYSd71a>HhSNf198__iAA8EHkh^H2WAiGTVTL;5 zctJUux;8o%v|>J2ikDN&{E_aUR>+y5i#Vqkf^oKrZpQ_q1!Z`PwV)DP32WgHHj>+d zW=s;c;|Icxxam^W1D&^^4>7_)tR#FNt%M^=KSbDrGt{^TLBbIgj}+n=I9y`_pYuk- zGOdU3j5Z=;{S4tb{Tsrl5ukgu8G2Uk-QwcjqV1$t=9eVCF2p6Yk;RhnzD`A|>LI0Z znh5QAp|lmd>K$7D7x%z;*Hdhcjz>ag=R>{Ke9Nq^+fM|8V={D^o`Q6d&9 zYD;}x^oDm@gVU24=Sa)I)OaR+#KE5FW8&vQ-Nr+tiYvOwKS`m)bi?LYe|Nbw;mDs~! zZU&47&H{9vIkTKlDOl7>vykN&U|b=|^869^)PUu9$$?VnWLgs;PA0Ra0?-Jp2`*kP zd$}exnzYxqwQcBh9J@Vfr&`xLU7d-xWP*6IE19x8lJ;o3?Lee!SF@AiLjAaSUKbXj zx)VHmR?(^Yr6O1RsW9W`#rgVaA>vVSIUcdX1I673*2=g$x4fFy4dj1|uVW&?@8m-b z3%oytQ6ynwJ=*BE0iAH@XVX!XY6`97IOrlhL4K0Z^OK|{$+yv=GDoR}4M*&4oadX` K)c