Insanely huge initial commit

This commit is contained in:
2026-02-21 16:40:15 -08:00
parent 208d626100
commit f74c547a13
33825 changed files with 5213498 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
namespace MoreMountains.Tools
{
/// <summary>
/// An interface classes that want to be saved by the MMPersistencyManager need to implement
/// </summary>
public interface IMMPersistent
{
/// <summary>
/// Needs to return a unique Guid used to identify this object
/// </summary>
/// <returns></returns>
string GetGuid();
/// <summary>
/// Returns a savable string containing the object's data
/// </summary>
/// <returns></returns>
string OnSave();
/// <summary>
/// Loads the object's data from the passed string and applies it to its properties
/// </summary>
/// <param name="data"></param>
void OnLoad(string data);
/// <summary>
/// Whether or not this object should be saved
/// </summary>
/// <returns></returns>
bool ShouldBeSaved();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35d5d4d43a5ca4147b2488524fbe815e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// A serializable class used to store scene data, the key is a string (the scene name), the value is a MMPersistencySceneData
/// </summary>
[Serializable]
public class DictionaryStringSceneData : MMSerializableDictionary<string, MMPersistenceSceneData>
{
public DictionaryStringSceneData() : base() { }
protected DictionaryStringSceneData(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
/// <summary>
/// A serializable class used to store object data, the key is a string (the object name), the value is a string (the object data)
/// </summary>
[Serializable]
public class DictionaryStringString : MMSerializableDictionary<string, string>
{
public DictionaryStringString() : base() { }
protected DictionaryStringString(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
/// <summary>
/// A serializable class used to store all the data for a persistence manager, a collection of scene datas
/// </summary>
[Serializable]
public class MMPersistenceManagerData
{
public string PersistenceID;
public string SaveDate;
public DictionaryStringSceneData SceneDatas;
}
/// <summary>
/// A serializable class used to store all the data for a scene, a collection of object datas
/// </summary>
[Serializable]
public class MMPersistenceSceneData
{
public DictionaryStringString ObjectDatas;
}
/// <summary>
/// The various types of persistence events that can be triggered by the MMPersistencyManager
/// </summary>
public enum MMPersistenceEventType { DataSavedToMemory, DataLoadedFromMemory, DataSavedFromMemoryToFile, DataLoadedFromFileToMemory }
/// <summary>
/// A data structure used to store persistence event data.
/// To use :
/// MMPersistencyEvent.Trigger(MMPersistencyEventType.DataLoadedFromFileToMemory, "yourPersistencyID");
/// </summary>
public struct MMPersistenceEvent
{
public MMPersistenceEventType PersistenceEventType;
public string PersistenceID;
public MMPersistenceEvent(MMPersistenceEventType eventType, string persistenceID)
{
PersistenceEventType = eventType;
PersistenceID = persistenceID;
}
static MMPersistenceEvent e;
public static void Trigger(MMPersistenceEventType eventType, string persistencyID)
{
e.PersistenceEventType = eventType;
e.PersistenceID = persistencyID;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c89376b60a9ff4b4d922b8d35778d386
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,331 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this component to a scene and it'll let you save and load the state of objects that implement the IMMPersistent interface
/// You can create your own classes that implement this interface, or use the MMPersistent class that comes with this package
/// It will save their transform data (position, rotation, scale) and their active state
/// Triggering save and load is done via events, and the manager also emits events every time data is loaded or saved
/// </summary>
public class MMPersistenceManager : MMPersistentSingleton<MMPersistenceManager>, MMEventListener<MMGameEvent>
{
[Header("Persistence")]
/// A persistence ID used to identify the data associated to this manager.
/// Usually you'll want to leave this to its default value.
[Tooltip("A persistence ID used to identify the data associated to this manager. Usually you'll want to leave this to its default value.")]
public string PersistenceID = "MMPersistency";
[Header("Events")]
/// whether or not this manager should listen for save events. If you set this to false, you'll have to call SaveToMemory or SaveFromMemoryToFile manually
[Tooltip("whether or not this manager should listen for save events. If you set this to false, you'll have to call SaveToMemory or SaveFromMemoryToFile manually")]
public bool ListenForSaveEvents = true;
/// whether or not this manager should listen for load events. If you set this to false, you'll have to call LoadFromMemory or LoadFromFileToMemory manually
[Tooltip("whether or not this manager should listen for load events. If you set this to false, you'll have to call LoadFromMemory or LoadFromFileToMemory manually")]
public bool ListenForLoadEvents = true;
/// whether or not this manager should listen for save to memory events. If you set this to false, you'll have to call SaveToMemory manually
[Tooltip("whether or not this manager should listen for save to memory events. If you set this to false, you'll have to call SaveToMemory manually")]
public bool ListenForSaveToMemoryEvents = true;
/// whether or not this manager should listen for load from memory events. If you set this to false, you'll have to call LoadFromMemory manually
[Tooltip("whether or not this manager should listen for load from memory events. If you set this to false, you'll have to call LoadFromMemory manually")]
public bool ListenForLoadFromMemoryEvents = true;
/// whether or not this manager should listen for save to file events. If you set this to false, you'll have to call SaveFromMemoryToFile manually
[Tooltip("whether or not this manager should listen for save to file events. If you set this to false, you'll have to call SaveFromMemoryToFile manually")]
public bool ListenForSaveToFileEvents = true;
/// whether or not this manager should listen for load from file events. If you set this to false, you'll have to call LoadFromFileToMemory manually
[Tooltip("whether or not this manager should listen for load from file events. If you set this to false, you'll have to call LoadFromFileToMemory manually")]
public bool ListenForLoadFromFileEvents = true;
/// whether or not this manager should save data to file on save events
[Tooltip("whether or not this manager should save data to file on save events")]
public bool SaveToFileOnSaveEvents = true;
/// whether or not this manager should load data from file on load events
[Tooltip("whether or not this manager should load data from file on load events")]
public bool LoadFromFileOnLoadEvents = true;
/// the debug buttons below are only meant to be used at runtime
[Header("Debug Buttons (Only at Runtime)")]
[MMInspectorButton("SaveToMemory")]
public bool SaveToMemoryButton;
[MMInspectorButton("LoadFromMemory")]
public bool LoadFromMemoryButton;
[MMInspectorButton("SaveFromMemoryToFile")]
public bool SaveToFileButton;
[MMInspectorButton("LoadFromFileToMemory")]
public bool LoadFromFileButton;
[MMInspectorButton("DeletePersistenceFile")]
public bool DeletePersistencyFileButton;
public DictionaryStringSceneData SceneDatas;
public static string _resourceItemPath = "Persistence/";
public static string _saveFolderName = "MMTools/";
public static string _saveFileExtension = ".persistence";
protected string _currentSceneName;
#region INITIALIZATION
/// <summary>
/// Statics initialization to support enter play modes
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
protected static void InitializeStatics()
{
_instance = null;
}
/// <summary>
/// On Awake we initialize our dictionary
/// </summary>
protected override void Awake()
{
base.Awake();
SceneDatas = new DictionaryStringSceneData();
}
#endregion
#region SAVE_AND_LOAD
/// <summary>
/// Saves data from objects that need saving to memory
/// </summary>
public virtual void SaveToMemory()
{
ComputeCurrentSceneName();
SceneDatas.Remove(_currentSceneName);
MMPersistenceSceneData sceneData = new MMPersistenceSceneData();
sceneData.ObjectDatas = new DictionaryStringString();
IMMPersistent[] persistents = FindAllPersistentObjects();
foreach (IMMPersistent persistent in persistents)
{
if (persistent.ShouldBeSaved())
{
sceneData.ObjectDatas.Add(persistent.GetGuid(), persistent.OnSave());
}
}
SceneDatas.Add(_currentSceneName, sceneData);
MMPersistenceEvent.Trigger(MMPersistenceEventType.DataSavedToMemory, PersistenceID);
}
/// <summary>
/// Loads data from memory and applies it to all objects that need it
/// </summary>
public virtual void LoadFromMemory()
{
ComputeCurrentSceneName();
if (!SceneDatas.TryGetValue(_currentSceneName, out MMPersistenceSceneData sceneData))
{
return;
}
if (sceneData.ObjectDatas == null)
{
return;
}
IMMPersistent[] persistents = FindAllPersistentObjects();
foreach (IMMPersistent persistent in persistents)
{
if (sceneData.ObjectDatas.TryGetValue(persistent.GetGuid(), out string data))
{
persistent.OnLoad(sceneData.ObjectDatas[persistent.GetGuid()]);
}
}
MMPersistenceEvent.Trigger(MMPersistenceEventType.DataLoadedFromMemory, PersistenceID);
}
/// <summary>
/// Saves data from memory to a file
/// </summary>
public virtual void SaveFromMemoryToFile()
{
MMPersistenceManagerData saveData = new MMPersistenceManagerData();
saveData.PersistenceID = PersistenceID;
saveData.SaveDate = DateTime.Now.ToString();
saveData.SceneDatas = SceneDatas;
MMSaveLoadManager.Save(saveData, DetermineSaveName(), _saveFolderName);
MMPersistenceEvent.Trigger(MMPersistenceEventType.DataSavedFromMemoryToFile, PersistenceID);
}
/// <summary>
/// Loads data from file and stores it in memory
/// </summary>
public virtual void LoadFromFileToMemory()
{
MMPersistenceManagerData saveData = (MMPersistenceManagerData)MMSaveLoadManager.Load(typeof(MMPersistenceManagerData), DetermineSaveName(), _saveFolderName);
if ((saveData != null) && (saveData.SceneDatas != null))
{
SceneDatas = new DictionaryStringSceneData();
SceneDatas = saveData.SceneDatas;
}
MMPersistenceEvent.Trigger(MMPersistenceEventType.DataLoadedFromFileToMemory, PersistenceID);
}
/// <summary>
/// On Save, we save to memory and to file if needed
/// </summary>
public virtual void Save()
{
SaveToMemory();
if (SaveToFileOnSaveEvents)
{
SaveFromMemoryToFile();
}
}
/// <summary>
/// On Load, we load from memory and from file if needed
/// </summary>
public virtual void Load()
{
if (LoadFromFileOnLoadEvents)
{
LoadFromFileToMemory();
}
LoadFromMemory();
}
#endregion
#region RESET
/// <summary>
/// Deletes all persistence data for the specified scene
/// </summary>
/// <param name="sceneName"></param>
public virtual void DeletePersistencyMemoryForScene(string sceneName)
{
if (!SceneDatas.TryGetValue(_currentSceneName, out MMPersistenceSceneData sceneData))
{
return;
}
SceneDatas.Remove(sceneName);
}
/// <summary>
/// Deletes persistence data from memory and on file for this persistence manager
/// </summary>
public virtual void ResetPersistence()
{
DeletePersistenceMemory();
DeletePersistenceFile();
}
/// <summary>
/// Deletes all persistence data stored in this persistence manager's memory
/// </summary>
public virtual void DeletePersistenceMemory()
{
SceneDatas = new DictionaryStringSceneData();
}
/// <summary>
/// Deletes the save file for this persistence manager
/// </summary>
public virtual void DeletePersistenceFile()
{
MMSaveLoadManager.DeleteSave(DetermineSaveName(), _saveFolderName);
Debug.LogFormat("Persistence save file deleted");
}
#endregion
#region HELPERS
/// <summary>
/// Finds all objects in the scene that implement IMMPersistent and may need saving
/// </summary>
/// <returns></returns>
protected virtual IMMPersistent[] FindAllPersistentObjects()
{
return FindObjectsOfType<MonoBehaviour>(true).OfType<IMMPersistent>().ToArray();
}
/// <summary>
/// Grabs the current scene's name and stores it
/// </summary>
protected virtual void ComputeCurrentSceneName()
{
_currentSceneName = SceneManager.GetActiveScene().name;
}
/// <summary>
/// Determines the name of the file to write to store persistence data
/// </summary>
/// <returns></returns>
protected virtual string DetermineSaveName()
{
return gameObject.name + "_" + PersistenceID + _saveFileExtension;
}
#endregion
#region EVENTS
/// <summary>
/// When we get a MMEvent, we filter on its name and invoke the appropriate methods if needed
/// </summary>
/// <param name="gameEvent"></param>
public virtual void OnMMEvent(MMGameEvent gameEvent)
{
if ((gameEvent.EventName == "Save") && ListenForSaveEvents)
{
Save();
}
if ((gameEvent.EventName == "Load") && ListenForLoadEvents)
{
Load();
}
if ((gameEvent.EventName == "SaveToMemory") && ListenForSaveToMemoryEvents)
{
SaveToMemory();
}
if ((gameEvent.EventName == "LoadFromMemory") && ListenForLoadFromMemoryEvents)
{
LoadFromMemory();
}
if ((gameEvent.EventName == "SaveToFile") && ListenForSaveToFileEvents)
{
SaveFromMemoryToFile();
}
if ((gameEvent.EventName == "LoadFromFile") && ListenForLoadFromFileEvents)
{
LoadFromFileToMemory();
}
}
/// <summary>
/// On enable, we start listening for MMGameEvents
/// </summary>
protected virtual void OnEnable()
{
this.MMEventStartListening<MMGameEvent>();
}
/// <summary>
/// On enable, we stop listening for MMGameEvents
/// </summary>
protected virtual void OnDisable()
{
this.MMEventStopListening<MMGameEvent>();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f66839a3914de3d4db7c5682fc89f2fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
namespace MoreMountains.Tools
{
/// <summary>
/// A persistent class that can save the essential parts of an object :
/// its transform data (position, rotation, scale) and its active state
/// This inherits from MMPersistentBase and implements the IMMPersistent interface
/// It's a good example of how to implement the interface's OnSave and OnLoad methods
/// </summary>
public class MMPersistent : MMPersistentBase
{
[Header("Properties")]
/// whether or not to save this object's position
[Tooltip("whether or not to save this object's position")]
public bool SavePosition = true;
/// whether or not to save this object's rotation
[Tooltip("whether or not to save this object's rotation")]
public bool SaveLocalRotation = true;
/// whether or not to save this object's scale
[Tooltip("whether or not to save this object's scale")]
public bool SaveLocalScale = true;
/// whether or not to save this object's active state
[Tooltip("whether or not to save this object's active state")]
public bool SaveActiveState = true;
/// whether or not to save this object's components' enabled states
[Tooltip("whether or not to save this object's components' enabled states")]
public bool SaveEnabledStates = false;
/// <summary>
/// A struct used to store and serialize the data we want to save
/// </summary>
[Serializable]
public struct Data
{
public Vector3 Position;
public Quaternion LocalRotation;
public Vector3 LocalScale;
public bool ActiveState;
public List<ComponentData> ComponentEnabledStates;
}
[Serializable]
public struct ComponentData
{
public string Name;
public bool EnabledState;
}
/// <summary>
/// On Save, we turn the object's transform data and active state to a Json string and return it to the MMPersistencyManager
/// </summary>
/// <returns></returns>
public override string OnSave()
{
List<ComponentData> saveEnabledStates = null;
if (SaveEnabledStates)
{
saveEnabledStates = GetCurrentComponents();
}
return JsonUtility.ToJson(new Data { Position = this.transform.position,
LocalRotation = this.transform.localRotation,
LocalScale = this.transform.localScale,
ActiveState = this.gameObject.activeSelf,
ComponentEnabledStates = saveEnabledStates
});
}
/// <summary>
/// On load, we read the saved json data and apply it to our object's properties
/// </summary>
/// <param name="data"></param>
public override void OnLoad(string data)
{
if (SavePosition)
{
this.transform.position = JsonUtility.FromJson<Data>(data).Position;
}
if (SaveLocalRotation)
{
this.transform.localRotation = JsonUtility.FromJson<Data>(data).LocalRotation;
}
if (SaveLocalScale)
{
this.transform.localScale = JsonUtility.FromJson<Data>(data).LocalScale;
}
if (SaveActiveState)
{
this.gameObject.SetActive(JsonUtility.FromJson<Data>(data).ActiveState);
}
if (SaveEnabledStates)
{
List<ComponentData> loadedList = JsonUtility.FromJson<Data>(data).ComponentEnabledStates;
Behaviour[] components = gameObject.GetComponents<Behaviour>();
Renderer[] renderers = gameObject.GetComponents<Renderer>();
if (loadedList.Count != components.Length + renderers.Length)
{
return;
}
int total = 0;
for (int i = 0; i < components.Length; i++)
{
if (loadedList[i].Name == components[i].name)
{
components[i].enabled = loadedList[i].EnabledState;
}
total++;
}
for (int j = 0; j < renderers.Length; j++)
{
if (loadedList[total+j].Name == renderers[j].name)
{
renderers[j].enabled = loadedList[total+j].EnabledState;
}
}
}
}
/// <summary>
/// Grabs all components on this object
/// </summary>
protected virtual List<ComponentData> GetCurrentComponents()
{
List<ComponentData> currentComponents = new List<ComponentData>();
Behaviour[] components = gameObject.GetComponents<Behaviour>();
Renderer[] renderers = gameObject.GetComponents<Renderer>();
foreach (Behaviour component in components)
{
currentComponents.Add(new ComponentData { Name = component.name, EnabledState = component.enabled });
}
foreach (Renderer renderer in renderers)
{
currentComponents.Add(new ComponentData { Name = renderer.name, EnabledState = renderer.enabled });
}
return currentComponents;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0397bb261ecd924ba0668927c40ba3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using System;
using System.Linq;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// A base class implementing the IMMPersistent interface, designed to be extended
/// This mostly takes care of the GUID generation and validation
/// </summary>
[AddComponentMenu("")]
public class MMPersistentBase : MonoBehaviour, IMMPersistent
{
[Header("Save")]
/// whether or not this object should be saved
[Tooltip("whether or not this object should be saved")]
public bool SaveActive = true;
[Header("ID")]
/// an optional suffix to add to the GUID, to make it more readable
[Tooltip("an optional suffix to add to the GUID, to make it more readable")]
public string UniqueIDSuffix;
/// the object's unique ID
[Tooltip("the object's unique ID")]
[SerializeField]
[MMReadOnly]
protected string _guid;
/// a debug button used to force a new GUI generation
[MMInspectorButton("GenerateGuid")]
public bool GenerateGuidButton;
/// <summary>
/// On validate, we make sure the object gets a valid GUID
/// </summary>
protected virtual void OnValidate()
{
ValidateGuid();
}
/// <summary>
/// Returns the object's GUID
/// </summary>
/// <returns></returns>
public virtual string GetGuid() => _guid;
/// <summary>
/// Lets you set the object's GUID
/// </summary>
/// <param name="newGUID"></param>
public virtual void SetGuid(string newGUID) => _guid = newGUID;
/// <summary>
/// On save, does nothing, meant to be extended
/// </summary>
/// <returns></returns>
public virtual string OnSave()
{
return string.Empty;
}
/// <summary>
/// On load, does nothing, meant to be extended
/// </summary>
/// <param name="data"></param>
public virtual void OnLoad(string data)
{
}
/// <summary>
/// Lets the persistence manager know whether or not the object should be saved
/// </summary>
/// <returns></returns>
public virtual bool ShouldBeSaved()
{
return SaveActive;
}
/// <summary>
/// Generates a unique ID for the object, using the scene name, the object name, and a GUID
/// </summary>
/// <returns></returns>
public virtual string GenerateGuid()
{
string newGuid = Guid.NewGuid().ToString();
string guid =
this.gameObject.scene.name
+ "-"
+ this.gameObject.name
+ "-"
+ newGuid;
if (!string.IsNullOrEmpty(UniqueIDSuffix))
{
guid += "-" + UniqueIDSuffix;
}
this.SetGuid(guid);
return guid;
}
/// <summary>
/// Checks if the object's ID is unique or not
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public virtual bool GuidIsUnique(string guid)
{
return Resources.FindObjectsOfTypeAll<MMPersistentBase>().Count(x => x.GetGuid() == guid) == 1;
}
/// <summary>
/// Validates the object's GUID, and generates a new one if needed, until a unique one is found
/// </summary>
public virtual void ValidateGuid()
{
if (!this.gameObject.scene.IsValid())
{
_guid = string.Empty;
return;
}
int maxCount = 1000;
int i = 0;
while ( (string.IsNullOrEmpty(_guid) || !GuidIsUnique(_guid) ) && (i < maxCount) )
{
GenerateGuid();
i++;
}
if (i == maxCount)
{
Debug.LogWarning(this.gameObject.name + " couldn't generate a unique GUID after " + maxCount + " tries, you should probably change its UniqueIDSuffix");
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 161799775c19ed44495de8edf8935aa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,199 @@
using UnityEngine;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MoreMountains.Tools
{
/// <summary>
/// Allows the save and load of objects in a specific folder and file.
///
/// How to use (at a minimum) :
///
/// Save : MMSaveLoadManager.Save(TestObject, FileName+SaveFileExtension, FolderName);
///
/// Load : TestObject = (YourObjectClass)MMSaveLoadManager.Load(typeof(YourObjectClass), FileName + SaveFileExtension, FolderName);
///
/// Delete save : MMSaveLoadManager.DeleteSave(FileName+SaveFileExtension, FolderName);
///
/// Delete save folder : MMSaveLoadManager.DeleteSaveFolder(FolderName);
///
/// You can also specify what IMMSaveLoadManagerMethod the system should use. By default it's binary but you can also pick binary encrypted, json, or json encrypted
/// You'll find examples of how to set each of these in the MMSaveLoadTester class
///
/// </summary>
public static class MMSaveLoadManager
{
/// the method to use when saving and loading files (has to be the same at both times of course)
public static IMMSaveLoadManagerMethod SaveLoadMethod = new MMSaveLoadManagerMethodBinary();
/// the default top level folder the system will use to save the file
private const string _baseFolderName = "/MMData/";
/// the name of the save folder if none is provided
private const string _defaultFolderName = "MMSaveLoadManager";
/// <summary>
/// Determines the save path to use when loading and saving a file based on a folder name.
/// </summary>
/// <returns>The save path.</returns>
/// <param name="folderName">Folder name.</param>
static string DetermineSavePath(string folderName = _defaultFolderName)
{
string savePath;
// depending on the device we're on, we assemble the path
if (Application.platform == RuntimePlatform.IPhonePlayer)
{
savePath = Application.persistentDataPath + _baseFolderName;
}
else
{
savePath = Application.persistentDataPath + _baseFolderName;
}
#if UNITY_EDITOR
savePath = Application.dataPath + _baseFolderName;
#endif
savePath = savePath + folderName + "/";
return savePath;
}
/// <summary>
/// Determines the name of the file to save
/// </summary>
/// <returns>The save file name.</returns>
/// <param name="fileName">File name.</param>
static string DetermineSaveFileName(string fileName)
{
return fileName;
}
/// <summary>
/// Save the specified saveObject, fileName and foldername into a file on disk.
/// </summary>
/// <param name="saveObject">Save object.</param>
/// <param name="fileName">File name.</param>
/// <param name="foldername">Foldername.</param>
public static void Save(object saveObject, string fileName, string foldername = _defaultFolderName)
{
string savePath = DetermineSavePath(foldername);
string saveFileName = DetermineSaveFileName(fileName);
// if the directory doesn't already exist, we create it
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
// we serialize and write our object into a file on disk
FileStream saveFile = File.Create(savePath + saveFileName);
SaveLoadMethod.Save(saveObject, saveFile);
saveFile.Close();
}
/// <summary>
/// Load the specified file based on a file name into a specified folder
/// </summary>
/// <param name="fileName">File name.</param>
/// <param name="foldername">Foldername.</param>
public static object Load(System.Type objectType, string fileName, string foldername = _defaultFolderName)
{
string savePath = DetermineSavePath(foldername);
string saveFileName = savePath + DetermineSaveFileName(fileName);
object returnObject;
// if the MMSaves directory or the save file doesn't exist, there's nothing to load, we do nothing and exit
if (!Directory.Exists(savePath) || !File.Exists(saveFileName))
{
return null;
}
FileStream saveFile = File.Open(saveFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
returnObject = SaveLoadMethod.Load(objectType, saveFile);
saveFile.Close();
return returnObject;
}
/// <summary>
/// Removes a save from disk
/// </summary>
/// <param name="fileName">File name.</param>
/// <param name="folderName">Folder name.</param>
public static void DeleteSave(string fileName, string folderName = _defaultFolderName)
{
string savePath = DetermineSavePath(folderName);
string saveFileName = DetermineSaveFileName(fileName);
if (File.Exists(savePath + saveFileName))
{
File.Delete(savePath + saveFileName);
}
if (File.Exists(savePath + saveFileName + ".meta"))
{
File.Delete(savePath + saveFileName + ".meta");
}
}
/// <summary>
/// Deletes the whole save folder
/// </summary>
/// <param name="folderName"></param>
public static void DeleteSaveFolder(string folderName = _defaultFolderName)
{
string savePath = DetermineSavePath(folderName);
if (Directory.Exists(savePath))
{
DeleteDirectory(savePath);
}
}
/// <summary>
/// Deletes all save files saved by this MMSaveLoadManager
/// </summary>
public static void DeleteAllSaveFiles()
{
string savePath = DetermineSavePath("");
savePath = savePath.Substring(0, savePath.Length - 1);
if (savePath.EndsWith("/"))
{
savePath = savePath.Substring(0, savePath.Length - 1);
}
if (Directory.Exists(savePath))
{
DeleteDirectory(savePath);
}
}
/// <summary>
/// Deletes the specified directory
/// </summary>
/// <param name="target_dir"></param>
public static void DeleteDirectory(string target_dir)
{
string[] files = Directory.GetFiles(target_dir);
string[] dirs = Directory.GetDirectories(target_dir);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
if (File.Exists(target_dir + ".meta"))
{
File.Delete(target_dir + ".meta");
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: aa3d0794b4f8e4b40b6a3dde5b794c0b
timeCreated: 1478535356
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// This component, on Awake or on demand, will force a SaveLoadMethod on the MMSaveLoadManager, changing the way it saves data to file.
/// This will impact all classes that use the MMSaveLoadManager (unless they change that method before saving or loading).
/// If you change the method, your previously existing data files won't be compatible, you'll need to delete them and start with new ones.
/// </summary>
public class MMSaveLoadManagerMethod : MonoBehaviour
{
[Header("Save and load method")]
[MMInformation("This component, on Awake or on demand, will force a SaveLoadMethod on the MMSaveLoadManager, changing the way it saves data to file. " +
"This will impact all classes that use the MMSaveLoadManager (unless they change that method before saving or loading)." +
"If you change the method, your previously existing data files won't be compatible, you'll need to delete them and start with new ones.",
MMInformationAttribute.InformationType.Info,false)]
/// the method to use to save to file
[Tooltip("the method to use to save to file")]
public MMSaveLoadManagerMethods SaveLoadMethod = MMSaveLoadManagerMethods.Binary;
/// the key to use to encrypt the file (if using an encryption method)
[Tooltip("the key to use to encrypt the file (if using an encryption method)")]
public string EncryptionKey = "ThisIsTheKey";
protected IMMSaveLoadManagerMethod _saveLoadManagerMethod;
/// <summary>
/// On Awake, we set the MMSaveLoadManager's method to the chosen one
/// </summary>
protected virtual void Awake()
{
SetSaveLoadMethod();
}
/// <summary>
/// Creates a new MMSaveLoadManagerMethod and passes it to the MMSaveLoadManager
/// </summary>
public virtual void SetSaveLoadMethod()
{
switch(SaveLoadMethod)
{
case MMSaveLoadManagerMethods.Binary:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodBinary();
break;
case MMSaveLoadManagerMethods.BinaryEncrypted:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodBinaryEncrypted();
((MMSaveLoadManagerEncrypter)_saveLoadManagerMethod).Key = EncryptionKey;
break;
case MMSaveLoadManagerMethods.Json:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodJson();
break;
case MMSaveLoadManagerMethods.JsonEncrypted:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodJsonEncrypted();
((MMSaveLoadManagerEncrypter)_saveLoadManagerMethod).Key = EncryptionKey;
break;
}
MMSaveLoadManager.SaveLoadMethod = _saveLoadManagerMethod;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b4d071b416837d40b9bab019b738769
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;
namespace MoreMountains.Tools
{
/// <summary>
/// This save load method saves and loads files as binary files
/// </summary>
public class MMSaveLoadManagerMethodBinary : IMMSaveLoadManagerMethod
{
/// <summary>
/// Saves the specified object to disk at the specified location after serializing it
/// </summary>
/// <param name="objectToSave"></param>
/// <param name="saveFile"></param>
public void Save(object objectToSave, FileStream saveFile)
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(saveFile, objectToSave);
saveFile.Close();
}
/// <summary>
/// Loads the specified file from disk and deserializes it
/// </summary>
/// <param name="objectType"></param>
/// <param name="saveFile"></param>
/// <returns></returns>
public object Load(System.Type objectType, FileStream saveFile)
{
object savedObject;
BinaryFormatter formatter = new BinaryFormatter();
savedObject = formatter.Deserialize(saveFile);
saveFile.Close();
return savedObject;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 339f02b3d924b984db779a1af1a59be2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;
namespace MoreMountains.Tools
{
/// <summary>
/// This save load method saves and loads files as encrypted binary files
/// </summary>
public class MMSaveLoadManagerMethodBinaryEncrypted : MMSaveLoadManagerEncrypter, IMMSaveLoadManagerMethod
{
/// <summary>
/// Saves the specified object to disk at the specified location after encrypting it
/// </summary>
/// <param name="objectToSave"></param>
/// <param name="saveFile"></param>
public void Save(object objectToSave, FileStream saveFile)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream();
formatter.Serialize(memoryStream, objectToSave);
memoryStream.Position = 0;
Encrypt(memoryStream, saveFile, Key);
saveFile.Flush();
memoryStream.Close();
saveFile.Close();
}
/// <summary>
/// Loads the specified file from disk, decrypts it, and deserializes it
/// </summary>
/// <param name="objectType"></param>
/// <param name="saveFile"></param>
/// <returns></returns>
public object Load(System.Type objectType, FileStream saveFile)
{
object savedObject;
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream();
try
{
Decrypt(saveFile, memoryStream, Key);
}
catch (CryptographicException ce)
{
Debug.LogError("[MMSaveLoadManager] Encryption key error: " + ce.Message);
return null;
}
memoryStream.Position = 0;
savedObject = formatter.Deserialize(memoryStream);
memoryStream.Close();
saveFile.Close();
return savedObject;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12d7216a3e967954ca8945932aa92fd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace MoreMountains.Tools
{
public class MMSaveLoadManagerMethodJson : IMMSaveLoadManagerMethod
{
/// <summary>
/// Saves the specified object at the specified location after converting it to json
/// </summary>
/// <param name="objectToSave"></param>
/// <param name="saveFile"></param>
public void Save(object objectToSave, FileStream saveFile)
{
string json = JsonUtility.ToJson(objectToSave);
// if you prefer using NewtonSoft's JSON lib uncomment the line below and commment the line above
//string json = Newtonsoft.Json.JsonConvert.SerializeObject(objectToSave);
StreamWriter streamWriter = new StreamWriter(saveFile);
streamWriter.Write(json);
streamWriter.Close();
saveFile.Close();
}
/// <summary>
/// Loads the specified file and decodes it
/// </summary>
/// <param name="objectType"></param>
/// <param name="saveFile"></param>
/// <returns></returns>
public object Load(System.Type objectType, FileStream saveFile)
{
object savedObject; // = System.Activator.CreateInstance(objectType);
StreamReader streamReader = new StreamReader(saveFile, Encoding.UTF8);
string json = streamReader.ReadToEnd();
savedObject = JsonUtility.FromJson(json, objectType);
// if you prefer using NewtonSoft's JSON lib uncomment the line below and commment the line above
//savedObject = Newtonsoft.Json.JsonConvert.DeserializeObject(json,objectType);
streamReader.Close();
saveFile.Close();
return savedObject;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 148ba46d50e5e7346a7ac051cf1b6617
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace MoreMountains.Tools
{
public class MMSaveLoadManagerMethodJsonEncrypted : MMSaveLoadManagerEncrypter, IMMSaveLoadManagerMethod
{
/// <summary>
/// Saves the specified object at the specified location to disk, converts it to json and encrypts it
/// </summary>
/// <param name="objectToSave"></param>
/// <param name="saveFile"></param>
public void Save(object objectToSave, FileStream saveFile)
{
string json = JsonUtility.ToJson(objectToSave);
// if you prefer using NewtonSoft's JSON lib uncomment the line below and commment the line above
//string json = Newtonsoft.Json.JsonConvert.SerializeObject(objectToSave);
using (MemoryStream memoryStream = new MemoryStream())
using (StreamWriter streamWriter = new StreamWriter(memoryStream))
{
streamWriter.Write(json);
streamWriter.Flush();
memoryStream.Position = 0;
Encrypt(memoryStream, saveFile, Key);
}
saveFile.Close();
}
/// <summary>
/// Loads the specified file, decrypts it and decodes it
/// </summary>
/// <param name="objectType"></param>
/// <param name="saveFile"></param>
/// <returns></returns>
public object Load(System.Type objectType, FileStream saveFile)
{
object savedObject = null;
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader streamReader = new StreamReader(memoryStream))
{
try
{
Decrypt(saveFile, memoryStream, Key);
}
catch (CryptographicException ce)
{
Debug.LogError("[MMSaveLoadManager] Encryption key error: " + ce.Message);
return null;
}
memoryStream.Position = 0;
savedObject = JsonUtility.FromJson(streamReader.ReadToEnd(), objectType);
// if you prefer using NewtonSoft's JSON lib uncomment the line below and commment the line above
//savedObject = Newtonsoft.Json.JsonConvert.DeserializeObject(sr.ReadToEnd(), objectType);
}
saveFile.Close();
return savedObject;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8b50f078655cbd34fb69ee48182dbf37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System.Security.Cryptography;
namespace MoreMountains.Tools
{
/// <summary>
/// An interface to implement save and load using different methods (binary, json, etc)
/// </summary>
public interface IMMSaveLoadManagerMethod
{
void Save(object objectToSave, FileStream saveFile);
object Load(System.Type objectType, FileStream saveFile);
}
/// <summary>
/// The possible methods to save and load files to and from disk available in the MMSaveLoadManager
/// </summary>
public enum MMSaveLoadManagerMethods { Json, JsonEncrypted, Binary, BinaryEncrypted };
/// <summary>
/// This class implements methods to encrypt and decrypt streams
/// </summary>
public abstract class MMSaveLoadManagerEncrypter
{
/// <summary>
/// The Key to use to save and load the file
/// </summary>
public virtual string Key { get; set; } = "yourDefaultKey";
protected string _saltText = "SaltTextGoesHere";
/// <summary>
/// Encrypts the specified input stream into the specified output stream using the key passed in parameters
/// </summary>
/// <param name="inputStream"></param>
/// <param name="outputStream"></param>
/// <param name="sKey"></param>
protected virtual void Encrypt(Stream inputStream, Stream outputStream, string sKey)
{
RijndaelManaged algorithm = new RijndaelManaged();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sKey, Encoding.ASCII.GetBytes(_saltText));
algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(inputStream, algorithm.CreateEncryptor(), CryptoStreamMode.Read);
cryptostream.CopyTo(outputStream);
}
/// <summary>
/// Decrypts the input stream into the output stream using the key passed in parameters
/// </summary>
/// <param name="inputStream"></param>
/// <param name="outputStream"></param>
/// <param name="sKey"></param>
protected virtual void Decrypt(Stream inputStream, Stream outputStream, string sKey)
{
RijndaelManaged algorithm = new RijndaelManaged();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sKey, Encoding.ASCII.GetBytes(_saltText));
algorithm.Key = key.GetBytes(algorithm.KeySize / 8);
algorithm.IV = key.GetBytes(algorithm.BlockSize / 8);
CryptoStream cryptostream = new CryptoStream(inputStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read);
cryptostream.CopyTo(outputStream);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b681190ef64d3e2438805b489a9ac9a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a74b1b5d357902346825fdfd125ea399
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,110 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace MoreMountains.Tools
{
/// <summary>
/// A test object to store data to test the MMSaveLoadManager class
/// </summary>
[System.Serializable]
public class MMSaveLoadTestObject
{
public string SavedText;
}
/// <summary>
/// A simple class used in the MMSaveLoadTestScene to test the MMSaveLoadManager class
/// </summary>
public class MMSaveLoadTester : MonoBehaviour
{
[Header("Bindings")]
/// the text to save
[Tooltip("the text to save")]
public InputField TargetInputField;
[Header("Save settings")]
/// the chosen save method (json, encrypted json, binary, encrypted binary)
[Tooltip("the chosen save method (json, encrypted json, binary, encrypted binary)")]
public MMSaveLoadManagerMethods SaveLoadMethod = MMSaveLoadManagerMethods.Binary;
/// the name of the file to save
[Tooltip("the name of the file to save")]
public string FileName = "TestObject";
/// the name of the destination folder
[Tooltip("the name of the destination folder")]
public string FolderName = "MMTest/";
/// the extension to use
[Tooltip("the extension to use")]
public string SaveFileExtension = ".testObject";
/// the key to use to encrypt the file (if needed)
[Tooltip("the key to use to encrypt the file (if needed)")]
public string EncryptionKey = "ThisIsTheKey";
/// Test button
[MMInspectorButton("Save")]
public bool TestSaveButton;
/// Test button
[MMInspectorButton("Load")]
public bool TestLoadButton;
/// Test button
[MMInspectorButton("Reset")]
public bool TestResetButton;
protected IMMSaveLoadManagerMethod _saveLoadManagerMethod;
/// <summary>
/// Saves the contents of the TestObject into a file
/// </summary>
public virtual void Save()
{
InitializeSaveLoadMethod();
MMSaveLoadTestObject testObject = new MMSaveLoadTestObject();
testObject.SavedText = TargetInputField.text;
MMSaveLoadManager.Save(testObject, FileName+SaveFileExtension, FolderName);
}
/// <summary>
/// Loads the saved data
/// </summary>
public virtual void Load()
{
InitializeSaveLoadMethod();
MMSaveLoadTestObject testObject = (MMSaveLoadTestObject)MMSaveLoadManager.Load(typeof(MMSaveLoadTestObject), FileName + SaveFileExtension, FolderName);
TargetInputField.text = testObject.SavedText;
}
/// <summary>
/// Resets all saves by deleting the whole folder
/// </summary>
protected virtual void Reset()
{
MMSaveLoadManager.DeleteSaveFolder(FolderName);
}
/// <summary>
/// Creates a new MMSaveLoadManagerMethod and passes it to the MMSaveLoadManager
/// </summary>
protected virtual void InitializeSaveLoadMethod()
{
switch(SaveLoadMethod)
{
case MMSaveLoadManagerMethods.Binary:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodBinary();
break;
case MMSaveLoadManagerMethods.BinaryEncrypted:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodBinaryEncrypted();
(_saveLoadManagerMethod as MMSaveLoadManagerEncrypter).Key = EncryptionKey;
break;
case MMSaveLoadManagerMethods.Json:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodJson();
break;
case MMSaveLoadManagerMethods.JsonEncrypted:
_saveLoadManagerMethod = new MMSaveLoadManagerMethodJsonEncrypted();
(_saveLoadManagerMethod as MMSaveLoadManagerEncrypter).Key = EncryptionKey;
break;
}
MMSaveLoadManager.SaveLoadMethod = _saveLoadManagerMethod;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58f9c048a89d0d5448bf5972cdc13fad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: