using NaughtyAttributes; using System.IO; using System.Collections.Generic; using UnityEngine; using System.Text; namespace GameplayIngredients { [ManagerDefaultPrefab("GameSaveManager")] public class GameSaveManager : Manager { [InfoBox(@"Use PlayerPrefs will store save data in common target platform registry instead of individual files. For some platforms where writing on disk is forbidden, this is required.", EInfoBoxType.Normal)] public bool UsePlayerPrefs = false; [DisableIf("UsePlayerPrefs"), SerializeField, Tooltip("The path where system and user saves will be stored. Relative to the Application.persistantDataPath folder.")] private string savePath = "/"; [SerializeField, Tooltip("The file name of the System Save.")] private string systemSaveName = "System.sav"; [SerializeField, Tooltip("The file format for a user save, use the {0} to specify where the numbering happens.")] private string userSaveName = "User{0}.sav"; Dictionary systemSaveEntries; Dictionary currentUserSaveEntries; [ReorderableList] public Callable[] OnLoad; [ReorderableList] public Callable[] OnSave; const string kPreferencePrefix = "GameplayIngredients.GameSaveManager."; void Awake() { systemSaveEntries = new Dictionary(); currentUserSaveEntries = new Dictionary(); } private void OnEnable() { LoadSystemSave(); } #region SAVE/LOAD public void LoadSystemSave() { systemSaveEntries = LoadFile(systemSaveName); Callable.Call(OnLoad); } public void SaveSystemSave() { SaveFile(systemSaveName, systemSaveEntries); Callable.Call(OnSave); } private byte currentUserIndex = 0; public void LoadUserSave(byte index) { currentUserIndex = index; Callable.Call(OnLoad); currentUserSaveEntries = LoadFile(string.Format(userSaveName, index)); } public void SaveUserSave() { SaveUserSave(currentUserIndex); } public void SaveUserSave(byte index) { // Save currentUserIndex = index; Callable.Call(OnSave); SaveFile(string.Format(userSaveName, index), currentUserSaveEntries); } #endregion #region VALUES public bool HasBool(string name, Location location) { return HasValue(name, location); } public bool HasInt(string name, Location location) { return HasValue(name, location); } public bool HasFloat(string name, Location location) { return HasValue(name, location); } public bool HasString(string name, Location location) { return HasValue(name, location); } public bool GetBool(string name, Location location) { return GetValue(name, location); } public int GetInt(string name, Location location) { return GetValue(name, location); } public float GetFloat(string name, Location location) { return GetValue(name, location); } public string GetString(string name, Location location) { return GetValue(name, location); } public void SetBool(string name, Location location, bool value) { SetValue(name, location, value); } public void SetInt(string name, Location location, int value) { SetValue(name, location, value); } public void SetFloat(string name, Location location, float value) { SetValue(name, location, value); } public void SetString(string name, Location location, string value) { SetValue(name, location, value); } public enum Location { System = 0, User = 1, } Dictionary GetEntriesFor(Location location) { if (location == Location.System) return systemSaveEntries; else return currentUserSaveEntries; } bool HasValue(string name, Location location) { var dict = GetEntriesFor(location); return dict.ContainsKey(name) && dict[name] is T; } T GetValue(string name, Location location) { if (HasValue(name, location)) { var dict = GetEntriesFor(location); return (T)dict[name]; } else return default(T); } void SetValue(string name, Location location, T value) { var dict = GetEntriesFor(location); if (HasValue(name, location)) { dict[name] = value; } else if (dict.ContainsKey(name)) // bad type { Debug.LogWarning(string.Format("GameSaveManager : {0} entry '{1}' changed type to {2}", location, name, typeof(T))); dict[name] = value; } else { dict.Add(name, value); } } #endregion #region SERIALIZATION Dictionary LoadFile(string fileName) { if (UsePlayerPrefs) { if(PlayerPrefs.GetString(kPreferencePrefix + fileName, string.Empty) == string.Empty) SaveFile(fileName, new Dictionary()); } else { if (!File.Exists(Application.persistentDataPath + savePath + fileName)) SaveFile(fileName, new Dictionary()); } var dict = new Dictionary(); string contents = string.Empty; if(UsePlayerPrefs) { contents = PlayerPrefs.GetString(kPreferencePrefix + fileName, string.Empty); } else { contents = File.ReadAllText(Application.persistentDataPath + savePath + fileName); } SerializableOutput data = JsonUtility.FromJson(contents); for(int i = 0; i < data.keys.Length; i++) { string val = data.values[i]; object value; if (data.types[i] == ValueType.Bool) value = bool.Parse(val); else if (data.types[i] == ValueType.Int) value = int.Parse(val); else if (data.types[i] == ValueType.Float) value = float.Parse(val); else value = val; dict.Add(data.keys[i], value); } return dict; } void SaveFile(string filename, Dictionary entries) { int count = entries.Count; SerializableOutput data = new SerializableOutput(); data.keys = new string[count]; data.values = new string[count]; data.types = new ValueType[count]; int i = 0; foreach (var kvp in entries) { data.keys[i] = kvp.Key; object value = kvp.Value; if (value is bool) data.types[i] = ValueType.Bool; else if (value is int) data.types[i] = ValueType.Int; else if (value is float) data.types[i] = ValueType.Float; else data.types[i] = ValueType.String; data.values[i] = kvp.Value.ToString(); i++; } if (UsePlayerPrefs) { PlayerPrefs.SetString(kPreferencePrefix + filename, JsonUtility.ToJson(data)); } else { File.WriteAllText(Application.persistentDataPath + savePath + filename, JsonUtility.ToJson(data)); } } [System.Serializable] public enum ValueType { Bool = 0, Int = 1, Float = 2, String = 3 } [System.Serializable] class SerializableOutput { public string[] keys; public string[] values; public ValueType[] types; } #endregion } }