[HOWTO] How To: Handle Saved Game Versions

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
User avatar
Tony Li
Posts: 22883
Joined: Thu Jul 18, 2013 1:27 pm

[HOWTO] How To: Handle Saved Game Versions

Post by Tony Li »

After you release your game, it's possible that you might change the format of saved games.

The save system stores a version number in each saved game to help you handle different saved game versions. When you save a game, the saved game includes the version number you set in the Save System component's Version field. When you change the type or format of data that's included in a saved game (e.g., if you change the game after release), increment the Save System component's Version value.

To get the version number before loading and restoring a saved game, use SaveSystem.storer.RetrieveSavedGameData(slot) to get the saved game data. Then you can check its version property to determine how to want to handle it. For example, you could use SavedGameData.DeleteData(key) to delete saver data that's no longer valid. Or you could change the data using GetData() and SetData(). Then use SaveSystem.ApplySavedGameData() to apply it. Example:

Code: Select all

var savedGameData = SaveSystem.Storer.RetrieveSavedGameData(1);
if (savedGameData.version == 1)
{
    // In version 1, we saved the player's position under "playerPosition". 
    // But in later versions of the game, we don't save the player's position,
    // so remove this save data.
    savedGameData.DeleteData("playerPosition");
    
    // Then apply it:
    SaveSystem.LoadGame(savedGameData);
}
Alternatively, you can handle versions in saver scripts themselves. For example, say your game has a custom InventorySaver script that you want to make compatible with saved game versions 1 and 2. Then handle it in the ApplyData() method. Say version 1's InventorySaver looked like this:

Code: Select all

public class InventorySaver : Saver // (save system version 1)
{
    [Serializable] public class Data
    {
        public List<int> itemIDs;
    }
    public override string RecordData()
    {
        var data = new Data() { itemIDs = new List<int>(PlayerInventory.ItemIDs) };
        return SaveSystem.Serialize(data);
    }
    public override void ApplyData(string s)
    {
        var data = SaveSystem.Deserialize<Data>(s);
        PlayerInventory.ItemIDs = new List<int>(data.itemIDs);
    }
}
But now your PlayerInventory class has List<string> ItemNames instead of ItemIDs. Change the InventorySaver script to:

Code: Select all

public class InventorySaver : Saver // (save system version 2)
{
    [Serializable] public class DataV1
    {
        public List<int> itemIDs;
    }
    [Serializable] public class DataV2
    {
        public List<string> itemNames;
    }
    public override string RecordData()
    {
        var data = new DataV2() { itemNames = new List<string>(PlayerInventory.ItemNames) };
        return SaveSystem.Serialize(data);
    }
    public override void ApplyData(string s)
    {
        if (SaveSystem.currentSavedGameData.version == 1)
        {
            // This is version 1 save data, so we get the List<int> and handle it:
            var dataV1 = SaveSystem.Deserialize<DataV1>(s);
            PlayerInventory.HandleOldItemIDs(data.itemIDs);
        }
        else
        {        
            // Version 2, so handle normally for version 2:
            var dataV2 = SaveSystem.Deserialize<DataV2>(s);
            PlayerInventory.ItemNames = new List<string>(data.itemNames);
        }
    }
}
Note: If you've added new variables or quests to your dialogue database, make sure to tick the Dialogue Manager's Persistent Data Settings > Initialize New Variables. This tells the Dialogue Manager to restore the list of variables and quests from save data into the Lua environment, but then check if you've added new variables or quests. If so, add those to the Lua environment, too.
Post Reply