I'm developing my game in Unity and chose to use the Newtonsoft JSON to facilitate saving and loading of game data. All entities are getting saved and loaded without a hitch, but the player data cannot be properly loaded once it is saved.
public sealed class Player : IInitializable, IDisposable, ISavable, IFixedTickable{ private static readonly JsonSerializerSettings s_serializationSettings = new() { SerializationBinder = new DefaultSerializationBinder(), TypeNameHandling = TypeNameHandling.Auto, TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full }; public string GetState() { PlayerState state = new(Wallet.Balance, Experience.Value, SelectedShipType, Inventory.GetItemsSavableStates()); return JsonConvert.SerializeObject(state, Formatting.Indented, s_serializationSettings); } public void SetState(string state) { try { PlayerState playerState = JsonConvert.DeserializeObject<PlayerState>(state, s_serializationSettings); Wallet.AddCredits(playerState.Credits); Experience.Add(playerState.Experience); SelectedShipType = playerState.SelectedShip; IEnumerable<IItem> inventoryContent = _savedItemsFactory.BatchCreate(playerState.InventoryContent); Inventory.TryAddItems(inventoryContent, out _); } catch (Exception) { PlayerState defaultState = PlayerState.Default; Wallet = new(defaultState.Credits); SelectedShipType = defaultState.SelectedShip; } }}public sealed class PlayerState{ public static PlayerState Default => new(100f, 0f, PlayerShipType.TempestMk1, Enumerable.Empty<ItemSavableState>()); public float Credits { get; } public float Experience { get; } public PlayerShipType SelectedShip { get; } public IEnumerable<ItemSavableState> InventoryContent { get; } public PlayerState(float credits, float experience, PlayerShipType selectedShip, IEnumerable<ItemSavableState> inventoryContent) { Credits = Mathf.Clamp(credits, 0f, float.MaxValue); Experience = Mathf.Clamp(experience, 0f, float.MaxValue); SelectedShip = selectedShip; InventoryContent = inventoryContent ?? throw new ArgumentNullException(); }}public abstract class ItemSavableState{ public Size Size { get; } public Quality Quality { get; } public float Price { get; } public ItemSavableState(Size size, Quality quality, float price) { Size = size; Quality = quality; Price = price; } public abstract IItem Recreate(SavedItemsServices services);}public sealed class DefaultSerializationBinder : ISerializationBinder{ public Type BindToType(string assemblyName, string typeName) => Type.GetType($"{typeName}, {assemblyName}"); public void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = serializedType.Assembly.FullName; typeName = serializedType.FullName; }}
The originally saved player data looks like this:
{"Credits": 100.0,"Experience": 0.0,"SelectedShip": 0,"InventoryContent": [{"$type": "SpaceAce.Gameplay.Shooting.Ammo.RegularAmmoSetSavableState, Scripts, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null","Amount": 3250,"HeatGeneration": 4.936358,"Speed": 130.8108,"Damage": 31.3563423,"Size": 1,"Quality": 0,"Price": 55.6946754}]}
But when I enter and exit the game once more, the player saved data loose its inventory content because it wasn't loaded properly into the player inventory in the first place. Other parts of the previous save file are preserved and saved back again.
{"Credits": 100.0,"Experience": 0.0,"SelectedShip": 0,"InventoryContent": []}
When I enter the game again, the same exception repeats over and over.
My understanding is that IEnumerable property of PlayerState has some behaviour that is unclear to me. I've been sweeping through the newtonsoft documentation to find a hint, but as of today amoun to nothing. I specified serialization settings as required and implemented serialization binder. All other saves are loading just fine because they don't contain any IEnumerable collection. What am I missing?