Page 1 of 1

Named save games

Posted: Wed Jan 22, 2020 10:51 pm
by VoodooDetective
To have a name for each slot, do I need to subclass one of the save game storers or is there already the ability to do that somehow.

Also, it seems like the save system was written with the understanding that there would only be one user playing the game per computer. Like the auto-saver takes slot number as a hard-coded parameter. If I want to allow for multiple save games , do I need to modify auto-saver to check lua state for a variable named "currentSave" or something?

Re: Named save games

Posted: Thu Jan 23, 2020 8:48 am
by Tony Li
Hi,
VoodooDetective wrote: Wed Jan 22, 2020 10:51 pmTo have a name for each slot, do I need to subclass one of the save game storers or is there already the ability to do that somehow.
Some options:
  • You can subclass one of the saved game data storers, or the base SavedGameDataStorer class if you want to start with essentially a blank slate. (For example, the ESSavedGameDataStorer for Easy Save 3 subclasses SavedGameDataStorer since it doesn't need to inherit functionality from any of the other storer classes, and some mobile games make a subclass of SavedGameDataStorer that saves games to a server over WWW.)
  • Or you can write a separate "directory" that associates names with slot numbers.
  • Or you can include the name in the saved game data itself. Write a Saver component whose RecordData() method returns the saved game name. ApplyData() can be empty. The caveat with this option is that, to show the list of saved game names in a load menu, you will need to open and deserialize all of the saved games to get the name.
VoodooDetective wrote: Wed Jan 22, 2020 10:51 pmAlso, it seems like the save system was written with the understanding that there would only be one user playing the game per computer. Like the auto-saver takes slot number as a hard-coded parameter. If I want to allow for multiple save games , do I need to modify auto-saver to check lua state for a variable named "currentSave" or something?
Only the AutoSaveLoad component assumes a specified slot number, and that's simple enough that you could subclass it or write a replacement -- or just set its saveSlotNumber in code for the current player.

Other than that, slots are simply arbitrary numbers used to identify saved games. You can manage them however you want for any number of players.

Re: Named save games

Posted: Thu Jan 23, 2020 1:29 pm
by VoodooDetective
Fantastic, thank you!

Re: Named save games

Posted: Thu Jan 23, 2020 1:57 pm
by Tony Li
Glad to help!

Re: Named save games

Posted: Thu Jan 23, 2020 2:39 pm
by VoodooDetective
I went with the "directory" approach. I figured I'd share what I wrote in case anyone else is interested.

Code: Select all

    /// <summary>
    /// Allows you to perform various operations on saved games.
    /// </summary>
    public class SaveGameDirectory
    {
        // Constants
        private const bool PrettyPrint = true;
        private const int MaxSaveGames = 10;
        // Properties
        private static SaveDirectory _directory;
        private static SaveDirectory directory
        {
            get
            {
                if (_directory == null) _directory = ReadOrCreateSaveDirectory();
                return _directory;
            }
        }
        public static Save currentSave;

        public static IEnumerable<Save> ListSaveGames()
        {
            for (int i = 1; i < directory.slotToSave.Length; i++)
            {
                Save save = directory.slotToSave[i];
                if (save != null)
                {
                    yield return save;
                }
            }
            yield break;
        }

        public static int AvailableSaveCount()
        {
            int availableCount = 0;
            for (int i = 1; i < directory.slotToSave.Length; i++)
            {
                if (directory.slotToSave[i] == null)
                {
                    availableCount++;
                }
            }
            return availableCount;
        }

        public static void NewGame(string name)
        {
            int slot = GetSlotForSave(name); // Check if game exists
            if (slot <= 0)
            {
                slot = GetEmptySlot();
                if (slot <= 0)
                {
                    throw new Exception("No save slots available");
                }
                currentSave = new Save();
                currentSave.name = name;
                currentSave.dateCreated = DateTime.Now;
                directory.slotToSave[slot] = currentSave;
            }
            else
            {
                currentSave = directory.slotToSave[slot];
            }
            currentSave.dateLastSaved = DateTime.Now;
            WriteSaveDirectory();
            SaveSystem.SaveToSlot(slot);
        }

        public static void SaveGame()
        {
            if (currentSave == null) throw new Exception("No current game to save!");
            int slot = GetSlotForSave(currentSave.name);
            if (slot <= 0) throw new Exception("No slot found for current save game!");
            directory.slotToSave[slot].dateLastSaved = DateTime.Now;
            WriteSaveDirectory();
            SaveSystem.SaveToSlot(slot);
        }

        public static void LoadGame(string name)
        {
            int slot = GetSlotForSave(name);
            if (slot > 0)
            {
                currentSave = directory.slotToSave[slot];
                SaveSystem.LoadFromSlot(slot);
            }
            else
            {
                throw new Exception("Save game did not exist");
            }
        }

        public static void DeleteGame(string name)
        {
            int slot = GetSlotForSave(name);
            if (slot > 0)
            {
                SaveSystem.DeleteSavedGameInSlot(slot);
                directory.slotToSave[slot] = null;
                WriteSaveDirectory();
            }
        }

        public static bool DoesSaveGameExist(string name)
        {
            return GetSlotForSave(name) > 0;
        }

        private static int GetSlotForSave(string name)
        {
            for (int i = 1; i < directory.slotToSave.Length; i++)
            {
                if (directory.slotToSave[i] != null && directory.slotToSave[i].name == name)
                {
                    return i;
                }
            }
            return 0;
        }

        private static int GetEmptySlot()
        {
            for (int i = 1; i < directory.slotToSave.Length; i++)
            {
                if (directory.slotToSave[i] == null)
                {
                    return i;
                }
            }
            return 0;
        }

        private static void WriteSaveDirectory()
        {
            string serialized = Singletons.YamlSerializer.Serialize(directory);
            File.WriteAllText(GetDirectoryPath(), serialized);
        }

        private static SaveDirectory ReadOrCreateSaveDirectory()
        {
            string path = GetDirectoryPath();
            SaveDirectory saveDirectory;
            if (File.Exists(path))
            {
                string serialized = File.ReadAllText(path);
                saveDirectory = Singletons.YamlSerializer.Deserialize<SaveDirectory>(serialized);
            }
            else
            {
                saveDirectory = new SaveDirectory();
                saveDirectory.slotToSave = new Save[MaxSaveGames + 1]; // 0th index empty
            }
            return saveDirectory;
        }

        private static string GetDirectoryPath()
        {
            return Application.persistentDataPath + "/save_directory";
        }
    }

    /// <summary>
    /// SaveDirectory model.
    /// </summary>
    public class SaveDirectory
    {
        public Save[] slotToSave { get; set; }
    }

    /// <summary>
    /// Save model.
    /// </summary>
    public class Save
    {
        public string name { get; set; }
        public DateTime dateCreated { get; set; }
        public DateTime dateLastSaved { get; set; }
    }

Re: Named save games

Posted: Thu Jan 23, 2020 3:08 pm
by Tony Li
Thanks for sharing!