/// <summary>Restores custom items used by this mod that were modified by <see cref="OnSaving"/>. Intended to be used after the game is saved or a save file is loaded.<para/> /// (On PC versions of this mod, the bags are saved using PyTK, but PyTK doesn't work with Android)</summary> private static void LoadCustomItems() { PlayerBags OwnedBags = PlayerBags.DeserializeFromCurrentSaveFile(); if (OwnedBags == null) { return; } else { BagConfig GlobalConfig = ItemBagsMod.BagConfig; // Index the saved bags by their instance ids Dictionary <int, BagInstance> IndexedBagInstances = new Dictionary <int, BagInstance>(); foreach (BagInstance BagInstance in OwnedBags.Bags) { if (!IndexedBagInstances.ContainsKey(BagInstance.InstanceId)) { IndexedBagInstances.Add(BagInstance.InstanceId, BagInstance); } else { string Warning = string.Format("Warning - multiple bag instances were found with the same InstanceId. Did you manually edit your {0} json file or add multiple .json files to the 'Modded Bags' folder with the same ModUniqueId values? Only the first bag with InstanceId = {1} will be loaded.", PlayerBags.OwnedBagsDataKey, BagInstance.InstanceId); ItemBagsMod.ModInstance.Monitor.Log(Warning, LogLevel.Warn); } } // Decode all of our Encoded custom items back into their original form ReplaceAllInstances(IsEncodedCustomItem, Encoded => { int BagInstanceId = Encoded.ParentSheetIndex - EncodedItemStartIndex; if (IndexedBagInstances.TryGetValue(BagInstanceId, out BagInstance BagInstance)) { if (BagInstance.TryDecode(out ItemBag Replacement)) { return(Replacement); }
/// <summary>Converts all custom items used by this mod into items that actually exist in the vanilla StardewValley game, so that the game won't crash while trying to save.<para/> /// The custom items are saved to a separate file using <see cref="StardewModdingAPI.IDataHelper.WriteSaveData{TModel}(string, TModel)"/></summary> internal static void OnSaving() { if (Constants.TargetPlatform == GamePlatform.Android) { ItemBagsMod.ModInstance.Monitor.Log("ItemBags OnSaving started.", LogLevel.Debug); int CurrentBagId = 0; List <BagInstance> BagInstances = new List <BagInstance>(); // Pre-emptive error-handling - try to find any encoded bags that, for whatever unknown reason, weren't able to be converted back into ItemBags during a Load. // If any were found, then we need to retain the SaveData's BagInstance associated with that item (and not re-use it's Bag InstanceId when assigning to the bags that didn't have any issues during loading) // so that the mod can still try again to load that bag during the next load. // If someone mysteriously loses a bag, I can at least do some manual save editing to restore it, as the data will still be there. List <Item> CorruptedBags = new List <Item>(); ReplaceAllInstances(x => IsEncodedCustomItem(x), x => { CorruptedBags.Add(x); return(x); }); HashSet <int> CorruptedBagIds = new HashSet <int>(CorruptedBags.Select(x => x.ParentSheetIndex - EncodedItemStartIndex)); PlayerBags PreviousBagData = null; if (CorruptedBagIds.Any()) { PreviousBagData = PlayerBags.DeserializeFromCurrentSaveFile(); if (PreviousBagData != null && PreviousBagData.Bags != null) { Dictionary <int, BagInstance> IndexedInstances = new Dictionary <int, BagInstance>(); foreach (BagInstance Instance in PreviousBagData.Bags) { if (!IndexedInstances.ContainsKey(Instance.InstanceId)) { IndexedInstances.Add(Instance.InstanceId, Instance); } } foreach (int CorruptedId in CorruptedBagIds) { if (IndexedInstances.TryGetValue(CorruptedId, out BagInstance CorruptedInstance)) { BagInstances.Add(CorruptedInstance); } } } } // Encode all bags as a regular non-modded item ReplaceAllInstances(IsCustomItem, CustomItem => { if (CustomItem is ItemBag IB) { try { while (CorruptedBagIds.Contains(CurrentBagId)) { CurrentBagId++; } BagInstance Instance = new BagInstance(CurrentBagId, IB); BagInstances.Add(Instance); // Replace the Bag with an arbitrary low-value/non-stackable item (in this case, a Rusty Sword) and store the bag instance's Id in the replacement item's ParentSheetIndex MeleeWeapon Replacement = new MeleeWeapon(0); Replacement.ParentSheetIndex = EncodedItemStartIndex + CurrentBagId; return(Replacement); } finally { CurrentBagId++; } } else { return(CustomItem); } }); PlayerBags OwnedBags = new PlayerBags(); OwnedBags.Bags = BagInstances.ToArray(); OwnedBags.SerializeToCurrentSaveFile(); ItemBagsMod.ModInstance.Monitor.Log("ItemBags OnSaving finished.", LogLevel.Debug); } }