Saving a List of Classes that contain Ref and Value Types?

Discussion and help for Easy Save 3
Post Reply
Dageran
Posts: 3
Joined: Tue Dec 31, 2019 6:10 pm

Saving a List of Classes that contain Ref and Value Types?

Post by Dageran »

Hi! I've been integrating ES3 into my game and so far it's been perfect. I've had no trouble making save types of complex data structures-- but I am getting caught up on how to properly tell ES3 to save my game's Inventory.

My Game's inventory is a List of type "ItemInstance" that is a class that contains one ScriptableObject Reference, and one int. I'd like to be able to tell ES3 to save the entire list- but ES3 doesn't seem to understand how to properly save a List of classes when the class contains a mixture of Reference and Value type properties. Specifically, I'm looking for a way to get ES3 to properly save a List of type ItemInstance such that, on load, NEW ItemInstances are generated and the ItemIds are loaded by Reference, while the ItemCount is loaded by value.

Is this currently possible to do, or would I have to deserialize my ItemInstance and turn it into a struct to get ES3 to interpret it correctly? Thanks!

Code and more details about what I've attempted so far is below:

Code: Select all

    [System.Serializable]
    public class ItemInstance
    {
        public TL_InventoryItem ItemId;
        public int ItemCount;

        public ItemInstance()
        {
            ItemId = null;
            ItemCount = 0;
        }

        public ItemInstance(TL_InventoryItem newItemID, int newItemCount)
        {
            ItemId = newItemID;
            ItemCount = newItemCount;
        }
    }
The Class that contains this is itself a Scriptable Object of type TL_Inventory. I've set up my TL_Inventory as an ES3Type. I have flagged _inventoryItems as a saved Property in ES3.

Code: Select all

    public class TL_Inventory : ScriptableObject
    {
        [SerializeField]
        private List<ItemInstance> _inventoryItems = new List<ItemInstance>();
        public List<ItemInstance> InventoryItems { get { return _inventoryItems; } }
}
Finally, I implemented a Saved Inventory that wraps the TL_Inventory and handles the Save/Load interface between my game's API and EasySave's.

Code: Select all

public class SavedInventory : SavedObject<TL_Inventory>
    {
        public SavedInventory(TL_Inventory newObj) : base(newObj) { }

        public override void SaveToFile(string fileName)
        {
            ES3Settings settings = new ES3Settings(true);
            settings.memberReferenceMode = ES3.ReferenceMode.ByRef;

            ES3.Save<TL_Inventory>(SerializedName, Var, fileName, settings);
        }
        public override bool LoadFromFile(string fileName)
        {
            if (ES3.KeyExists(SerializedName, fileName))
            {
                ES3.LoadInto(SerializedName, fileName, Var);
                return true;
            }
            return false;
        }

        public override void ResetData()
        {
            Var.ClearInventory();
        }
    }
With the code above, this is the Save Data for the Inventory that ES3 outputs for an inventory Containing 1 Item with a stack count of 12:

Code: Select all

{"Player Inventory":{"__type":"Tavernlight.Inventory.TL_Inventory,Assembly-CSharp","value":{"_ES3Ref":9118728620226211164,"_ES3Ref":9118728620226211164,"_inventoryItems":[{"ItemId":{"_ES3Ref":1000487261940427266},"ItemCount":12}]}}
The data seems to be saved properly (with ItemId getting an _ES3Ref, and ItemCount getting an int)- but I'm concerned about the preceding _ES3Refs. This saves and loads properly as long as I keep Unity running-- but when I load after a unity Reset, the Length of the List is correct, and the ItemCount is correct- but the ItemId becomes null. I think this is because ES3 is actually trying to save a Reference to the ItemInstance class instance-- which shouldn't be happening. (I want it to generate a new ItemInstance and populate it with the ItemId and ItemCount data.)

I've tried playing around with "ES3.ReferenceMode", but if I save by RefAndValue or by Value it ends up trying to do a deep copy of the content of the ItemId ScriptableObject (which I don't want, since that's the data that defines an item's parameters and doesn't need to be saved.)

Do you have any insight into how I can get ES3 to properly save/load a List of classes like this? Thanks again!
Dageran
Posts: 3
Joined: Tue Dec 31, 2019 6:10 pm

Re: Saving a List of Classes that contain Ref and Value Type

Post by Dageran »

Update:

I've been continuing to poke at the problem and I think that this may a symptom of a different issue with the ES3 Reference Manager. I'm noticing that (in Unity 2019.3) ES3 is saving different ES3Refs for ScriptableObjects every time I restart unity.

In general, saving any ScriptableObject Reference via ES3 seems not to work. I can confirm that, if I Play in edit mode, save the game, then quit/restart Unity, it looks like the ES3 Reference Manager is generating DIFFERENT IDs for the Scriptable Objects. It looks like as long as "Auto Update References" is checked, this will always happen.

A workaround for this appears to be to always save my scene again after pressing play to ensure the Reference IDs are always kept in sync... but this seems like a really poor workflow. Am I missing something about how the ES3 Reference Manager is supposed to work with SO's? Better yet- is there a way to request that ES3 just save Prefab/SO References by GUID, rather than going through the ES3Ref system? (Which seems like it was designed for Scene instances more than project files.)
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving a List of Classes that contain Ref and Value Type

Post by Joel »

Hi there,

Please could you provide more information on how your ScriptableObjects are being managed? Are they in the scene prior to runtime or are they created at runtime? Do they exist in the Assets folder or are they created dynamically?

If you could create a new project with the minimum required to replicate this issue and private message it to me, I'll be happy to look into this further to see what is happening.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Dageran
Posts: 3
Joined: Tue Dec 31, 2019 6:10 pm

Re: Saving a List of Classes that contain Ref and Value Type

Post by Dageran »

Heya,

The scriptableObjects in question don't exist in the scene- they are managed assets in the asset folder that represent abstract data objects. (In this case: Quests, Quest Steps, Player Inventory, etc.)

As far as I can tell, it looks like they're being assigned a new ES3Ref each time Unity starts / the manager boots, even if I've manually disabled the Auto-Update. Does Easy Save 3 require ScriptableObjects to be referenced by scene assets in order to register them correctly?
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving a List of Classes that contain Ref and Value Type

Post by Joel »

Hi there,

When the ScriptableObject is used by your scene, a new instance of it is created and inserted into the scene, meaning that it has a new reference.

You can manually add the reference to the reference manager at runtime, and assign to it your own reference ID. I.e.

Code: Select all

ES3ReferenceMgrBase.Current.Add(yourScriptableObject, (long)12345);
This will ensure that the reference can be resolved to the same reference ID on each load.

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