Hello!
How can I load (and save) custom data from every slots (like save time, game time, player level etc) for display in load menu? See example.
Save System slots and custom data
Re: Save System slots and custom data
I created custom saver
Save:
Load:
I don't know how good this way is.
Code: Select all
public class SlotDataParser
{
private readonly DataContractJsonSerializer serializer
= new DataContractJsonSerializer(typeof(SlotData));
public string Serialize(SlotData data)
{
var stream = new MemoryStream();
try {
serializer.WriteObject(stream, data);
var json = stream.ToArray();
return Encoding.UTF8.GetString(json, 0, json.Length);
} finally {
stream.Close();
}
}
public SlotData Deserialize(string json)
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
try {
var data = (SlotData) serializer.ReadObject(stream);
return data;
} finally {
stream.Close();
}
}
}
Code: Select all
var data = new SlotData();
// ... fill data
SaveSystem.currentSavedGameData.SetData(SlotData.KEY, SaveSystem.NoSceneIndex, parser.Serialize(data));
SaveSystem.SaveToSlot(0);
Code: Select all
var save = SaveSystem.storer.RetrieveSavedGameData(slot);
var slotData = save.GetData(SlotData.KEY);
var data = parser.Deserialize(slotData);
Re: Save System slots and custom data
Hi,
If your saved games remain medium to small in size, that's a fine solution, and I'd continue using it. Each saved game will contain data like this:
[SavedGameData: {slotData}, {regularSaveData} ]
where {slotData} is the extra info (save time, game time, player level, etc)
However, if your saved games become very large, then to show the custom data for all of the saved games you will need to read all of the saved games first, which could take more time than you want. Instead, you could save each slot like this:
{slotData}, [SavedGameData: {regularSaveData} ]
This way, when showing the "load game" screen, you can read each slot's {slotData} without having to read and parse the regular save data.
One way to do this is to make your own subclass of SavedGameDataStorer (or one of its subclasses such as DiskSavedGameDataStorer).
Before saving, store the slot data in a key just like in your post.
Then override StoreSavedGameData(slot, savedGameData) to do this: (pseudocode)
Override RetrieveSavedGameData(slot, savedGameData) to skip the slotData: (pseudocode)
Finally, add a new method called something like RetrieveSlotData(int slot): (pseudocode)
To show the "load game" panel, use RetrieveSlotData() to get the slot data without having to read each saved game's full data.
If your saved games remain medium to small in size, that's a fine solution, and I'd continue using it. Each saved game will contain data like this:
[SavedGameData: {slotData}, {regularSaveData} ]
where {slotData} is the extra info (save time, game time, player level, etc)
However, if your saved games become very large, then to show the custom data for all of the saved games you will need to read all of the saved games first, which could take more time than you want. Instead, you could save each slot like this:
{slotData}, [SavedGameData: {regularSaveData} ]
This way, when showing the "load game" screen, you can read each slot's {slotData} without having to read and parse the regular save data.
One way to do this is to make your own subclass of SavedGameDataStorer (or one of its subclasses such as DiskSavedGameDataStorer).
Before saving, store the slot data in a key just like in your post.
Then override StoreSavedGameData(slot, savedGameData) to do this: (pseudocode)
Code: Select all
public void StoreSavedGameData(int slot, SavedGameData savedGameData)
{
// Get the slot data and save it first:
var slotData = save.GetData(SlotData.KEY);
/* save slotData here */
// Then save the regular save data:
/* save savedGameData here */
}
Code: Select all
public SavedGameData RetrieveSavedGameData(int slot)
{
// Skip slotData:
/* read past slotData */
// Read regular save data:
/* read and return savedGameData */
}
Code: Select all
public SlotData RetrieveSlotData(int slot)
{
/* read and return slotData */
// No need to read regular save data; just return slotData.
}
Re: Save System slots and custom data
Wow, thanks!
I will definitely try this. It looks most logical.
I will definitely try this. It looks most logical.
Re: Save System slots and custom data
Save files separately, so as not to break the working.
I created custom disk storer:
Сustom SaveSystem wrapper:
I created custom disk storer:
Code: Select all
public class SavedGameDataStorer : DiskSavedGameDataStorer
{
public override void StoreSavedGameData(int slot, SavedGameData save)
{
var slotData = save.GetData(SaveSystem.SlotDataKey);
var filePath = GetSlotDataFilename(slot);
if (debug) {
Debug.Log($"Save System: SavedGameDataStorer - Saving Slot Data{filePath}: {slotData}");
}
WriteStringToFile(
filePath,
encrypt ? EncryptionUtility.Encrypt(slotData, encryptionPassword) : slotData
);
save.SetData(SaveSystem.SlotDataKey, SaveSystem.NoSceneIndex, null);
base.StoreSavedGameData(slot, save);
}
public SlotData RetrieveSlotData(int slot)
{
var filePath = GetSlotDataFilename(slot);
var data = ReadStringFromFile(filePath);
if (encrypt) {
data = EncryptionUtility.TryDecrypt(data, encryptionPassword, out var plainText)
? plainText
: string.Empty;
}
if (debug) {
Debug.Log($"Save System: SavedGameDataStorer - Loading Slot Data {filePath}: {data}");
}
return SaveSystem.Deserialize<SlotData>(data);
}
private static string GetSlotDataFilename(int slot)
{
return Application.persistentDataPath + "/save_meta_" + slot + ".dat";
}
}
Code: Select all
public class SaveSystem : PixelCrushers.SaveSystem
{
public const string SlotDataKey = "slotData";
public static void SaveToSlot(int slot, SlotData slotData)
{
slotData.slot = slot;
slotData.date = DateTimeOffset.Now.ToUnixTimeSeconds();
var data = Serialize(slotData);
currentSavedGameData.SetData(SlotDataKey, NoSceneIndex, data);
SaveToSlot(slot);
}
public static SlotData RetrieveSlotData(int slot)
{
return ((SavedGameDataStorer) storer).RetrieveSlotData(slot);
}
}
Re: Save System slots and custom data
Thanks for sharing!