Saving Nested Dict/ List of custom classes

Discussion and help for Easy Save 3
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Saving Nested Dict/ List of custom classes

Post by c166 »

Hey EasySave3 has been great so far, but I've bumped into an issue with regards to saving nested items; and I'm not sure why it's not working/ how to resolve it.

The save appears to work (no errors); but there are problems when I try to load the item (no errors) -> just missing entries; though it retains the
structure. The entries are blank/ null/ 0 and appear to contain default values.

I think the problem may have something to do with the inability to save Collections of Collections (Dict of List). Though I'm not sure how to apply EasySaves example (of Arrays) to fix this.

I have attached two files which show my debug logs that I used to identify the problem.

Code: Select all

public Dictionary<string, List<Deck>> playerDecks = new Dictionary<string , List<Deck>>();

ES3.Save(saveStartingDeckKey, GameManager.Instance.playerDecks);

Dictionary<string, List<Deck>> savedStartDecks = ES3.Load(saveStartingDeckKey, new Dictionary<string, List<Deck>>());
The attributes I am trying to access are:
Deck.DeckEntry.CardTemplate.Name (string)
Deck.DeckEntry.CardTemplate.Id (int)

The expected values are the ones in the save target. I had been able to save items that were not as nested before without any modifications.

I have attempted (without success):
  • Adding CardTemplate, DeckEntry, and Deck as additional Types to be explicitly supported (via the EasySave Editor).
  • Creating another public class (MonoBehaviour) "DeckList" with decks as an attribute (List<Deck>) to use in playerDecks i.e. playerDecks = new Dictionary<string , DeckList>();
There is the following nesting:
  • GameManager.Instance.playerDecks = Dictionary<string, List<Deck>>
  • Deck is a public class with multiple classes (DeckEntry + Deck)
  • DeckEntry references Cardtemplate, which is another class of type Scriptable Object.
Attachments
EasySave_SaveTarget.PNG
EasySave_SaveTarget.PNG (45.96 KiB) Viewed 1519 times
EasySave_LoadFail.PNG
EasySave_LoadFail.PNG (49.55 KiB) Viewed 1519 times
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving Nested Dict/ List of custom classes

Post by Joel »

Hi there,

Unfortunately it’s not possible to replicate this from what you’ve sent. Please could you create a new project with a basic scene which replicates this and private message it to me with instructions.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Re: Saving Nested Dict/ List of custom classes

Post by c166 »

Hey Joel,

I've tried to extract the components into a scene; but there are a lot of finicky components that I'll need to sort out. After giving that a bit of a crack and seeing how much work that involves, I'm wondering if it'll just be more straightforward to try to directly figure out my problem by asking more general questions about EasySave to confirm my assumptions/ provide a better direction for me to explore in as I've been able to get a bit further than before.

General questions/ confirmation EasySave3 able to handle:
  • A Dictionary of Lists? i.e. Dictionary<string, List<SomethingElse>> (Not supported in ES2, but checking to see if it's supported in ES3)
  • The (EasySave2) docs suggest that you should create a Wrapper Class to handle the above i.e. Dictionary<string, Wrapper>; where the Wrapper is List<SomethingElse>
After creating the wrapper class, I am able to Load and Save in the SAME scene.

Question here:
  • Did I even need to create a Wrapper Class in ES3?
However, when I try to load the same item in a DIFFERENT scene, I am getting the error Reference for CCGKit.CardTemplate with ID 5262805120891785167 could not be found in Easy Save's reference manager.. It then results in an error/ null loading.

That occurs when I have an Easy Save3 Manager set up in both scenes.

So a question here is:
  • Do I need an Easy Save3 Manager in every scene? or will that cause conflicts?
  • How do I use ES3 between scenes?
  • I thought EasySave would have used the SO as the reference -> do the SOs need to be placed in a specific file to be picked up between scenes? Are references unique or specific to the scene?
When I do NOT have an Easy Save3 manager in the second scene; the original error occurs (loads empties/ null values BUT has the right "shape/ dimension of data")

The following is from the ES3 save file. The information inside the "decks" segment appears retain the "shape" of the data, but has it's actual values set to 0/ null.

Code: Select all

		"value" : {"WizardDeck":{
				"decks" : [
					{
						"name" : "New deck",
						"cards" : [
							{
								"id" : 1,
								"amount" : 5,
								"cardTemplate" : {
									"_ES3Ref" : "7553921938400959291"
								}
							}
						]
					}
				]

Question here:
  • The data elements themselves appear saved inside the ES3 save file; but I'm having difficulty accessing them. Depending on whether I've got an ES3 Manager in the "other" scene; the errors are different. Which is the right direction to go?
Best Regards,
C
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving Nested Dict/ List of custom classes

Post by Joel »

Hi there,
General questions/ confirmation EasySave3 able to handle:
  • A Dictionary of Lists? i.e. Dictionary<string, List<SomethingElse>> (Not supported in ES2, but checking to see if it's supported in ES3)
  • The (EasySave2) docs suggest that you should create a Wrapper Class to handle the above i.e. Dictionary<string, Wrapper>; where the Wrapper is List<SomethingElse>
Easy Save can handle Dictionaries containing Lists. If you're using Easy Save 3 then you should avoid the documentation for Easy Save 2 as it works very differently to Easy Save 3 and was depreciated quite a long time ago.
  • Do I need an Easy Save3 Manager in every scene? or will that cause conflicts?
You need an Easy Save 3 Manager in every scene that you plan on saving and loading references in.
However, when I try to load the same item in a DIFFERENT scene, I am getting the error Reference for CCGKit.CardTemplate with ID 5262805120891785167 could not be found in Easy Save's reference manager.. It then results in an error/ null loading.
  • How do I use ES3 between scenes?
If you're loading in a different scene to the one you saved in, you can't load references to objects which don't exist in that scene.

Also be aware that fields/properties of UnityEngine.Object types (such as your cardTemplate) are stored by reference, not by value. If you want to load a reference to this in another scene you will need to save it by value and load this before loading anything which references it. You can do this by putting all of your cardTemplate objects into a List and save this. Then before loading anything which references it, load this List so that they exist in the scene.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Re: Saving Nested Dict/ List of custom classes

Post by c166 »

Hey Joel,

Appreciate that reply, that's gotten me to the next set of errors haha

I think I can resolve it in the same way as you've suggested for CardTemplate -> i.e. create a separate save object with a list of references for each set of objects/ types; however, I'm wondering if there's a cleaner solution.

Due to the way that things are set-up, each card template (SOs) also contains a number of attributes which are other objects (SOs). I would need to create a list + save for each set of SO type (which will only increase over time) -> which seems to be clunky. Am also wondering whether there will be runtime/ memory problems in the long run due to this (??)

The SOs already exist in a set of folders; they just don't have an EasySave reference. Is it possible to tag a folder of SOs to add an EasySave reference to everything in there/ get EasySave to check those folders? (Feature suggestion if it's possible/ doesn't exist? hahaha)

Best Regards,
C
Attachments
EasySave_ReferenceMissing.PNG
EasySave_ReferenceMissing.PNG (53.6 KiB) Viewed 1481 times
Last edited by c166 on Sat Sep 10, 2022 1:55 am, edited 1 time in total.
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Re: Saving Nested Dict/ List of custom classes

Post by c166 »

Hey Joel,

So I've added about 5 lists of different objects to Save/ Load between the scenes (still would want to know whether there is an alternative way of doing this as it feels super finicky/ clunky/ inefficient -> I'm searching through each CardTemplate and then looping through each of it's components to create lists + each CardTemplate may be referencing the same SO -> which would mean there's duplicates).
  • For SOs that have other SOs as their components, it seems inefficient/ clunky to have to loop through every iteration to save the items. Is there a better way?
But on top of that, another question with regards to saving Textures via EasySave. I think this cropped up as I needed to save the "Sprite" component, and the Sprite itself has a Texture component which I also need to save (warning about needing to turn on read/ write for the texture).

Question
  • Is saving the "sprite" and then separately saving the "texture" in two separate lists that then get loaded the best way to go about this?
  • Why does Texture2D need this explicit saving when it's in the supported type list??
Additional Question Edit: Looking at the save files that ES3 saves; there are also references to other items that seem like they'd be default/ common use-cases

"Particles" : {
"_ES3Ref" : "3474728139615153974"
},

"Animator" : {
"_ES3Ref" : "5859572041666085503"
},
  • Do I need to do the explicit save/ load for every object/ component that has an _ES3Ref? in the save file
Best Regards,
C
Attachments
EasySave_TextureSaving.PNG
EasySave_TextureSaving.PNG (10.87 KiB) Viewed 1479 times
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving Nested Dict/ List of custom classes

Post by Joel »

Hi there,
c166 wrote: Fri Sep 09, 2022 11:57 pmThe SOs already exist in a set of folders; they just don't have an EasySave reference. Is it possible to tag a folder of SOs to add an EasySave reference to everything in there/ get EasySave to check those folders? (Feature suggestion if it's possible/ doesn't exist? hahaha)
There's no automated way of doing this, though you might be able to create an Editor script which automates. Currently you would need to select all of the ScriptableObjects, right-click and choose Easy Save 3 > Add References to Manager.
So I've added about 5 lists of different objects to Save/ Load between the scenes (still would want to know whether there is an alternative way of doing this as it feels super finicky/ clunky/ inefficient -> I'm searching through each CardTemplate and then looping through each of it's components to create lists + each CardTemplate may be referencing the same SO -> which would mean there's duplicates).
If your SOs are of the same type, could you not use FindObjectsOfType?
https://docs.unity3d.com/ScriptReferenc ... fType.html

Alternatively if you want to avoid duplicates, add them to a HashSet rather than a List.
But on top of that, another question with regards to saving Textures via EasySave. I think this cropped up as I needed to save the "Sprite" component, and the Sprite itself has a Texture component which I also need to save (warning about needing to turn on read/ write for the texture).
Just to check, are these Sprites/Textures generated at runtime? If not then you don't need to save them by
Why does Texture2D need this explicit saving when it's in the supported type list??
If you're getting a warning about needing to turn on read/write for the Texture, it's not that it needs explicit saving, it's that you need to allow the Texture's data to be read/written. However, unless you're procedurally generating Textures then you shouldn't need to save the Texture itself; you just need to ensure that there's a reference to the Texture in your scene so that the reference manager can load a reference to it (or right-click the Texture and select Easy Save 3 > Add Reference(s) to Manager).
Do I need to do the explicit save/ load for every object/ component that has an _ES3Ref? in the save file
If these objects are generated at runtime and aren't loaded with another object, yes. However, if it's a Component then that indicates that it's part of another GameObject, so you should be loading that GameObject rather than it's individual Components. When loading a GameObject it's Components are already added to the manager so you don't need to add them yourself.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Re: Saving Nested Dict/ List of custom classes

Post by c166 »

Hey Joel,

Thanks for that, the tip with regards to selecting the scriptable objects and then adding it directly to the scene has got me a bit further -> and the game appears to load/ use the scriptable objects as expected.

I'm a bit confused as to why that's the case though.

Using the first method that you told me to (save objects in list) and then load them in; I would get the case where the objects are loaded with generic placeholders (instead of the specific scriptable object). They appeared to be captured correctly at the "save" step, but were not correctly captured at the "load" step. After adding the SOs manually -> the generic placeholders were replaced with the "right" objects. I am guessing that is because my manual save/ load of each component object missed a nested component (likely texture??) -> whereas adding them manually meant that all nested components were dealt with (Can you confirm/ clarify?)

Screenshots below (Card Type is probably easiest to follow)

In-scene EasySave Manager References on Save
Saved_from_deck.PNG
Saved_from_deck.PNG (68.94 KiB) Viewed 1386 times
In-scene EasySave Manager References on Load
Loaded_in_scene.PNG
Loaded_in_scene.PNG (39.46 KiB) Viewed 1386 times
In-scene EasySave Manager References on Load after manually adding the SOs to the scene where the SOs needed to be loaded
Loaded_in_scene_after_manual_ad_v2.PNG
Loaded_in_scene_after_manual_ad_v2.PNG (49.93 KiB) Viewed 1386 times
Questions:
  • Why is manually adding the SOs required? (Saving/ Loading via saving the List of SOs results in a bunch of "empty/ holder" references -> examining the save files; it seems that one of the nested items has not explicitly been saved e.g. the texture -> tried to do so, came up with the warning when done programmatically -> manually adding it had no problems)
  • Why does manually adding the SOs replace the "empty/ holder" references
  • Once I manually add the SOs in, I don't need to have the "save list of objects" and then "load list of objects for ES3 to reference" anymore right?
  • Is using both methods in conjunction expected?
Also:
  • Adding the SOs manually to the Load Scene created a reference # in the ES3 Reference Manager
  • Saving the same SO later in the Save scene -> ES3 was able to get the same reference # back for saving without issues
Question:
  • How does that work? I did not explicitly load the SOs in the "save scene"; so how does it generate the same SO/ reference that the "load" scene is using
If your SOs are of the same type, could you not use FindObjectsOfType?
https://docs.unity3d.com/ScriptReferenc ... fType.html
Sorry, I worded it badly -> it's 5 different SO Types; so from my understanding; I'd need a new list for each SO type (each list can contain X number of the same SO type; but a new list is needed if it's a different type).
Just to check, are these Sprites/Textures generated at runtime?
Not sure about what this means. It sounds like sprites/ textures that are generated at runtime would be ones that are created out of a random number generator (or something?) -> the sprites I'm using are "static" and assigned beforehand -> so I don't think they're generated at runtime?

Best Regards,
C
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Saving Nested Dict/ List of custom classes

Post by Joel »

Hi there,
c166 wrote: Sat Sep 17, 2022 2:15 amWhy is manually adding the SOs required? (Saving/ Loading via saving the List of SOs results in a bunch of "empty/ holder" references -> examining the save files; it seems that one of the nested items has not explicitly been saved e.g. the texture -> tried to do so, came up with the warning when done programmatically -> manually adding it had no problems
Why does manually adding the SOs replace the "empty/ holder" references
Adding a ScriptableObject will also add it's dependencies to the reference manager (e.g. Textures). Previously it is likely that there was no reference to the dependencies of this ScriptableObject in your scene (i.e. no reference to this Texture in your scene), so there was no way of loading references to them as they will never automatically be added to the reference manager.
Once I manually add the SOs in, I don't need to have the "save list of objects" and then "load list of objects for ES3 to reference" anymore right?
This is only required for objects generated at runtime. As your ScriptableObjects exist prior to runtime (i.e. in the Assets folder), you don't need to do this.
How does that work? I did not explicitly load the SOs in the "save scene"; so how does it generate the same SO/ reference that the "load" scene is using
If your object exists outside of a scene (i.e. it's an Asset such as a ScriptableObject in a folder), Easy Save has internal logic which ensures that the same ID is generated regardless of scene so that cross-scene references are possible.
Sorry, I worded it badly -> it's 5 different SO Types; so from my understanding; I'd need a new list for each SO type (each list can contain X number of the same SO type; but a new list is needed if it's a different type).
As your SOs aren't generated at runtime then you shouldn't need to worry about this after all. However, if they were, you wouldn't need 5 different lists as you could just add them all to a List<ScriptableObject>. You would need five FindObjectsOfType calls unless
Not sure about what this means. It sounds like sprites/ textures that are generated at runtime would be ones that are created out of a random number generator (or something?) -> the sprites I'm using are "static" and assigned beforehand -> so I don't think they're generated at runtime?
As mentioned above, it sounds like the issue was that there isn't a reference to your Textures in the scene, so the Texture will never be automatically added to the manager. Doing 'Add References to Manager' for either the Texture or your ScriptableObjects (which reference the Texture) is the solution to this issue.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
c166
Posts: 11
Joined: Sat Sep 03, 2022 2:28 am

Re: Saving Nested Dict/ List of custom classes

Post by c166 »

Hey Joel,

Thanks for the detailed explanation; that makes a bit more sense now.
However, if they were, you wouldn't need 5 different lists as you could just add them all to a List<ScriptableObject>. You would need five FindObjectsOfType calls unless
Thanks for that -> didn't think of just creating a list of ScriptableObject as a generic which would cover everything that inherited it + be easier than what I've done (though it turns out it was unnecessary -> it's still been a useful exercise).

Appreciate all the responses/ helping me get pass those hurdles : )

Best Regards,
C
Post Reply