From 6e43f2fdcdf1e33a6326fb16461106a3c9a633c4 Mon Sep 17 00:00:00 2001 From: DragonFire47 <46509577+DragonFire47@users.noreply.github.com> Date: Fri, 19 Aug 2022 16:38:27 -0700 Subject: [PATCH] -Initial file merge -SaveDataManager Init added to PLGlobalStart --- PulsarModLoader/Patches/PLGlobalStart.cs | 5 +- PulsarModLoader/PulsarModLoader.csproj | 2 + PulsarModLoader/SaveData/PMLSaveData.cs | 13 + PulsarModLoader/SaveData/SaveDataManager.cs | 269 ++++++++++++++++++++ 4 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 PulsarModLoader/SaveData/PMLSaveData.cs create mode 100644 PulsarModLoader/SaveData/SaveDataManager.cs diff --git a/PulsarModLoader/Patches/PLGlobalStart.cs b/PulsarModLoader/Patches/PLGlobalStart.cs index 9d1d974..364a123 100644 --- a/PulsarModLoader/Patches/PLGlobalStart.cs +++ b/PulsarModLoader/Patches/PLGlobalStart.cs @@ -29,7 +29,10 @@ static void Prefix() //Modmanager GUI Init. new GameObject("ModManager", typeof(CustomGUI.GUIMain)) { hideFlags = HideFlags.HideAndDontSave }; - + + //SaveDataManager Init() + new SaveData.SaveDataManager(); + //ModLoading string modsDir = Path.Combine(Directory.GetCurrentDirectory(), "Mods"); ModManager.Instance.LoadModsDirectory(modsDir); diff --git a/PulsarModLoader/PulsarModLoader.csproj b/PulsarModLoader/PulsarModLoader.csproj index 55c4956..24a2def 100644 --- a/PulsarModLoader/PulsarModLoader.csproj +++ b/PulsarModLoader/PulsarModLoader.csproj @@ -215,6 +215,8 @@ + + diff --git a/PulsarModLoader/SaveData/PMLSaveData.cs b/PulsarModLoader/SaveData/PMLSaveData.cs new file mode 100644 index 0000000..ec1aa3f --- /dev/null +++ b/PulsarModLoader/SaveData/PMLSaveData.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace PulsarModLoader.SaveData +{ + public abstract class PMLSaveData + { + public PulsarMod MyMod; + public abstract string Identifier(); + public virtual uint VersionID => 0; + public abstract MemoryStream SaveData(); + public abstract void LoadData(MemoryStream dataStream, uint VersionID); + } +} diff --git a/PulsarModLoader/SaveData/SaveDataManager.cs b/PulsarModLoader/SaveData/SaveDataManager.cs new file mode 100644 index 0000000..5189d2f --- /dev/null +++ b/PulsarModLoader/SaveData/SaveDataManager.cs @@ -0,0 +1,269 @@ +using HarmonyLib; +using PulsarModLoader; +using PulsarModLoader.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace PulsarModLoader.SaveData +{ + class SaveDataManager + { + public SaveDataManager() + { + ModManager.Instance.OnModSuccessfullyLoaded += OnModLoaded; + ModManager.Instance.OnModUnloaded += OnModRemoved; + Instance = this; + } + public static SaveDataManager Instance; + + static public string SaveDir = Directory.GetCurrentDirectory() + "/Saves"; + static public string LocalSaveDir = SaveDir + "/Local"; + + List SaveConfigs = new List(); + + void OnModLoaded(string modName, PulsarMod mod) + { + mod.GetType().Assembly.GetTypes().AsParallel().ForAll((type) => + { + if (typeof(PMLSaveData).IsAssignableFrom(type) && !type.IsAbstract) + { + PMLSaveData SaveData = (PMLSaveData)Activator.CreateInstance(type); + SaveData.MyMod = mod; + SaveConfigs.Add(SaveData); + } + }); + } + + void OnModRemoved(PulsarMod mod) + { + List saveConfigsToRemove = new List(); + SaveConfigs.AsParallel().ForAll((arg) => + { + if (arg.GetType().Assembly == mod.GetType().Assembly) + { + saveConfigsToRemove.Add(arg); + } + }); + for (byte s = 0; s < saveConfigsToRemove.Count; s++) + { + SaveConfigs.Remove(saveConfigsToRemove[s]); + } + } + + public static string getPMLSaveFileName(string inFileName) + { + if(inFileName.EndsWith("LastRecoveredSave.plsave")) //Fix LastRecoveredSave + { + return inFileName.Replace("LastRecoveredSave.plsave", "LastRecoveredMSave.pmlsave"); + } + return inFileName.Replace(".plsave", ".pmlsave"); + } + + public void SaveDatas(string inFileName) + { + //Stop if no save configs to save. Additionally check for older .pmlsave file under the same name and delete. + string fileName = getPMLSaveFileName(inFileName); + if (SaveConfigs.Count == 0) + { + if(File.Exists(fileName)) + { + Logger.Info("Old PMLSave found with no new data to save. Deleting old data."); + File.Delete(fileName); + } + return; + } + + //Start Saving, create temp file + string tempText = fileName + "_temp"; + FileStream fileStream = File.Create(tempText); + BinaryWriter binaryWriter = new BinaryWriter(fileStream); + + //save for mods + binaryWriter.Write(SaveConfigs.Count); //int32 representing total configs + foreach (PMLSaveData saveData in SaveConfigs) + { + try + { + PulsarModLoader.Utilities.Logger.Info($"Writing: {saveData.MyMod.HarmonyIdentifier()}::{saveData.Identifier()}"); + MemoryStream dataStream = saveData.SaveData(); //Collect Save data from mod + int bytecount = (int)dataStream.Length; + + //SaveDataHeader + binaryWriter.Write(saveData.MyMod.HarmonyIdentifier()); //Write Mod Identifier + binaryWriter.Write(saveData.Identifier()); //Write PMLSaveData Identifier + binaryWriter.Write(saveData.VersionID); //Write PMLSaveData VersionID + binaryWriter.Write(bytecount); //Write stream byte count + dataStream.Position = 0; //Reset position of dataStream for reading + + byte[] buffer = new byte[bytecount]; + dataStream.Read(buffer, 0, bytecount); //move data to filestream + binaryWriter.BaseStream.Write(buffer, 0, bytecount); + + dataStream.Close(); + } + catch (Exception ex) + { + Logger.Info($"Failed to save a mod data.\n{ex.Message}"); + } + } + + //Finish Saving, close and save file to actual location + binaryWriter.Close(); + fileStream.Close(); + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + File.Move(tempText, fileName); + string relativeFileName = PLNetworkManager.Instance.FileNameToRelative(fileName); + Logger.Info("PMLSaveManager has saved file: " + relativeFileName); + + + //Save to Steam + /*bool localFile = false; + if (relativeFileName.StartsWith(LocalSaveDir)) + { + localFile = true; + } + if (!PLServer.Instance.IronmanModeIsActive && !localFile) + { + Logger.Info("Should be saving file to steam cloud"); + PLNetworkManager.Instance.SteamCloud_WriteFileName(relativeFileName, delegate (RemoteStorageFileWriteAsyncComplete_t pCallback, bool bIOFailure) + { + OnRemoteFileWriteAsyncComplete(pCallback, bIOFailure, relativeFileName); + }); + }*/ + } + + public void LoadDatas(string inFileName) + { + //start reading + string fileName = getPMLSaveFileName(inFileName); + if(!File.Exists(fileName)) + { + return; + } + FileStream fileStream = File.OpenRead(fileName); + BinaryReader binaryReader = new BinaryReader(fileStream); + + //read for mods + int count = binaryReader.ReadInt32(); //int32 representing total configs + string missingMods = ""; + string VersionMismatchedMods = ""; + for (int i = 0; i < count; i++) + { + //SaveDataHeader + string harmonyIdent = binaryReader.ReadString(); //HarmonyIdentifier + string SavDatIdent = binaryReader.ReadString(); //SaveDataIdentifier + uint VersionID = binaryReader.ReadUInt32(); //VersionID + int bytecount = binaryReader.ReadInt32(); //ByteCount + PulsarModLoader.Utilities.Logger.Info($"Reading SaveData: {harmonyIdent}::{SavDatIdent} SaveDataVersion: {VersionID} bytecount: {bytecount} Pos: {binaryReader.BaseStream.Position}"); + + + bool foundReader = false; + foreach (PMLSaveData savedata in SaveConfigs) + { + if (savedata.MyMod.HarmonyIdentifier() == harmonyIdent && savedata.Identifier() == SavDatIdent) + { + if(VersionID != savedata.VersionID) + { + Logger.Info($"Mismatched SaveData VersionID. Read: {VersionID} SaveData: {savedata.VersionID}"); + VersionMismatchedMods += "\n" + harmonyIdent; + } + MemoryStream stream = new MemoryStream(); //initialize new memStream + + byte[] buffer = new byte[bytecount]; + binaryReader.BaseStream.Read(buffer, 0, bytecount); //move data to memStream + stream.Write(buffer, 0, bytecount); + + stream.Position = 0; //Reset position + try + { + savedata.LoadData(stream, VersionID); //Send memStream to PMLSaveData + } + catch (Exception ex) + { + Logger.Info($"Failed to load {harmonyIdent}::{SavDatIdent}\n{ex.Message}"); + } + stream.Close(); + foundReader = true; + } + } + if (!foundReader) + { + binaryReader.BaseStream.Position += bytecount; + missingMods += ("\n" + harmonyIdent); + } + } + + //Finish Reading + binaryReader.Close(); + fileStream.Close(); + Logger.Info("PMLSaveManager has read file: " + PLNetworkManager.Instance.FileNameToRelative(fileName)); + + if (missingMods.Length > 0) + { + PLNetworkManager.Instance.MainMenu.AddActiveMenu(new PLErrorMessageMenu($"Warning: Found save data for following missing mods: {missingMods}")); + Logger.Info($"Warning: Found save data for following missing mods: {missingMods}"); + } + if (!string.IsNullOrEmpty(VersionMismatchedMods)) + { + PLNetworkManager.Instance.MainMenu.AddActiveMenu(new PLErrorMessageMenu($"Warning: The following mods used in this save have been updated: {VersionMismatchedMods}")); + Logger.Info($"Warning: The following mods used in this save have been updated: {VersionMismatchedMods}"); + } + } + } + [HarmonyPatch(typeof(PLSaveGameIO), "SaveToFile")] + class SavePatch + { + static void Postfix(string inFileName) + { + SaveDataManager.Instance.SaveDatas(inFileName); + } + } + [HarmonyPatch(typeof(PLSaveGameIO), "LoadFromFile")] + class LoadPatch + { + static void Postfix(string inFileName) + { + SaveDataManager.Instance.LoadDatas(inFileName); + } + } + + + [HarmonyPatch(typeof(PLSaveGameIO), "DeleteSaveGame")] + class DeletePatch + { + static void Postfix(PLSaveGameIO __instance) + { + string fileName = SaveDataManager.getPMLSaveFileName(__instance.LatestSaveGameFileName); + if (fileName != "") + { + try + { + Logger.Info("DeleteSaveGame " + fileName); + File.Delete(__instance.LatestSaveGameFileName); + } + catch (Exception ex) + { + Logger.Info("DeleteSaveGame EXCEPTION: " + ex.Message + ": Could not delete save file!"); + } + } + } + } + + [HarmonyPatch(typeof(PLUILoadMenu), "OnClickDeleteSaveFile")] + class LoadMenuDeletePatch + { + static void Postfix(string inFileName) + { + if (inFileName.EndsWith(".plsave")) + { + string PMLname = SaveDataManager.getPMLSaveFileName(inFileName); + typeof(PLUILoadMenu).GetMethod("OnClickDeleteSaveFile", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(PLUILoadMenu.Instance, new object[] { PMLname }); + } + } + } +}