Serialized Dictionary Saving

Discussion and help for Easy Save 3
Post Reply
Thoranar
Posts: 4
Joined: Wed Oct 02, 2019 6:23 am

Serialized Dictionary Saving

Post by Thoranar »

I'm attempting to save/load an Character Stats system that primarily is driven by a serialized dictionary, meaning a class that derives from Dictionary and ISerializationCallbackReceiver. The dictionary is structured like this:

Code: Select all

[Serializable]
public class StatDictionary : SerializableDictionary<BaseStat, StatRuntimeData> { }

Code: Select all

[Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
...
}
BaseStat is reference to a scriptable object assigned at design time and StatRuntimeData is a non abstract class generated at runtime, with a parameter constructor:

Code: Select all

[Serializable]
public class StatRuntimeData
{
...
	public StatRuntimeData(BaseStat stat, float startingValue, CharacterData charData = null, int startingLevel = 0)
	{
          ...
        }
}
{
the way i'm currently doing the save/load just as a quick test is by these method calls:

Code: Select all

	
public void SaveStats()
	{

		ES3.Save<StatDictionary>(id, stats);
	}

public void LoadStats()
	{
		if (ES3.KeyExists(id))
		{
			Debug.Log("save exists");
			ES3.LoadInto<StatDictionary>(id, this.stats);
		}
	}
Saving creates a file and I see mention of the StatDictionary, but when I load the dictionary is cleared out (no keys/values).

I created a type for the StatDictionary, though no fields or properties were listed. Do I also need to create types for BaseStat and StatRuntimeData? Is there an example of creating a type that does not have a parameterless constructor (the documentation appeared to be lite in this area).
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: Serialized Dictionary Saving

Post by Joel »

Hi there,

Would you be able to show me the fields of your serialisable dictionary? If these aren't appearing in the type manager then they won't be serialisable.

I suspect that they're private, so they don't technically belong to the class which inherits it, so won't be serialised even if they are marked as serialisable.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Thoranar
Posts: 4
Joined: Wed Oct 02, 2019 6:23 am

Re: Serialized Dictionary Saving

Post by Thoranar »

I thought that too and so changed them to Protected fields, but some fields would show up and others would not.

I ended up just taking a step back and seeing what within the dictionary really needed saving. Since the keys are defined at design time and never change at runtime, and since there are several setup/configuration steps for each value that also needs to happen once at runtime (event listeners), I ended up just saving the the current value, current experience, and current level (ints/floats) and feeding those values back into the dictionary on load.
Thoranar
Posts: 4
Joined: Wed Oct 02, 2019 6:23 am

Re: Serialized Dictionary Saving

Post by Thoranar »

Actually bringing this back up since there are a several areas I need to save that I'm using serialized dictionaries.

I'm now working on saving equipment and are having similar issues except now I'm getting various InvalidCastException: Specified cast is not valid errors.

Here is what I'm trying to do:

ItemDictionary class inherits from the same serializeabledictionary class. Here are the fields on that one. By switching to protected instead of private, I do see the fields in the type window and I was able to create a new type for the ItemDictionary

Code: Select all

[Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
	[SerializeField]
	protected List<TKey> keys = new List<TKey>();

	[SerializeField]
	protected List<TValue> values = new List<TValue>();
...
}
Saving produces no errors, but the key and value lists are empty in the save file

Code: Select all

	public void SaveData(string FilePath)
	{
		
		ES3.Save<ItemDictionary>("equipmentList", items, FilePath);
		
	}
Few extra steps in loading, mainly just specific to my game so can be ignored:

Code: Select all

	public void LoadData(string FilePath)
	{

		if (ES3.KeyExists("equipmentList", FilePath))
		{
			UnEquipAll();

			ItemDictionary savedItems = ES3.Load<ItemDictionary>("equipmentList", FilePath);

			foreach (var item in savedItems.Values)
			{
				if (savedItems != null)
					this.EquipItem(item as EquipableItem);
			}
}
And then the error:
EasySaveError.JPG
EasySaveError.JPG (102.89 KiB) Viewed 2118 times
The ItemDictionary is a dictionary of custom classes. The key is a UISlot class(head, shoulder, legs, etc.) and the value is a scriptableobject item created at design time. Here are the fields related to that object (note the equipment types are all enums:

Code: Select all

[CreateAssetMenu(menuName = "Items/Equippable Item")]
public class EquipableItem : Item
{
	[System.Serializable]
	public struct EquipmentModifier
	{
		public BaseStat stat;
		public float value;
		public StatModType type;
	}
	[Space]
	public EquipmentTier Tier;
	public EquipmentSubType EquipType;
	public EquipmentType EquipmentType;
	public AudioClip equipSFX;
	[Space]
	public EquipmentModifier[] modifiers;
...
}
Thoranar
Posts: 4
Joined: Wed Oct 02, 2019 6:23 am

Re: Serialized Dictionary Saving

Post by Thoranar »

I've tried other methods including just saving the values separately as a list, but still run into castexception errors. I've tried creating types for all the various classes with no luck.
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: Serialized Dictionary Saving

Post by Joel »

Hi there,

Please could you create a basic project and instructions to replicate this and PM this to me? It won't be possible for me to replicate your project.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Post Reply