Difficulty with saving a collection of any type

Easy Save 2 has been replaced by Easy Save 3, so is no longer supported.
Locked
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Difficulty with saving a collection of any type

Post by taylank »

Hi,

I am having bit of an issue with saving my custom type in a list. The type in question is below:

Code: Select all

public class ResolvedEntityObject : IResolvedEntityBase
    {
        public string Id;
        public Int2 SavedPosition; // this is also a custom type that I added to the supported types list and it works in other contexts
        public List<IResolvedEntityBase> Properties;

        public ResolvedEntityObject (string id, List<IResolvedEntityBase> properties)
        {
            this.Id = id;
            this.Properties = properties;
        }

        public ResolvedEntityObject ()
        {

        }

        public string GetStringPropertyValueWithId (string id)
        {
            var stringProps = Properties.Where(p => p is ResolvedEntityProperty<string>).Cast<ResolvedEntityProperty<string>>();
            var prop =  stringProps.FirstOrDefault(p => p.PropertyId == id);
            if (prop != null)
            {
                return prop.PropertyValue;
            }
            else
            {
                return null;
            }
        }
    }
And I am trying to save a List<ResolvedEntityObject> which is giving me an error saying ES2 does not support a list of this type. Now I am aware this class includes a list of an interface type, so I went ahead and wrote a custom type for ES2:

Code: Select all

public class ES2UserType_EntityDataStructuresResolvedEntityObject : ES2Type
{
	public override void Write(object obj, ES2Writer writer)
	{
		EntityDataStructures.ResolvedEntityObject data = (EntityDataStructures.ResolvedEntityObject)obj;
        // Add your writer.Write calls here.
        writer.Write(data.Id);

        writer.Write(data.SavedPosition);

        var intProps = data.Properties.Where(p => p is ResolvedIntegerProperty).Cast<ResolvedIntegerProperty>().ToList();
        writer.Write(intProps);

        var floatProps = data.Properties.Where(p => p is ResolvedFloatProperty).Cast<ResolvedFloatProperty>().ToList();
        writer.Write(floatProps);

        var stringProps = data.Properties.Where(p => p is ResolvedStringProperty).Cast<ResolvedStringProperty>().ToList();
        writer.Write(stringProps);
    }
	
	public override object Read(ES2Reader reader)
	{
		EntityDataStructures.ResolvedEntityObject data = new EntityDataStructures.ResolvedEntityObject();
		Read(reader, data);
		return data;
	}

	public override void Read(ES2Reader reader, object c)
	{
		EntityDataStructures.ResolvedEntityObject data = (EntityDataStructures.ResolvedEntityObject)c;
        // Add your reader.Read calls here to read the data into the object.
        data.Id = reader.Read<string>();
        data.SavedPosition = reader.Read<Int2>(); // this is also a custom type that I added to the supported types list and it works in other contexts
        data.Properties = new List<IResolvedEntityBase>();

        var intProps = reader.Read<List<ResolvedIntegerProperty>>();
        var floatProps = reader.Read<List<ResolvedFloatProperty>>();
        var stringProps = reader.Read<List<ResolvedStringProperty>>();
        data.Properties.AddRange(stringProps);
        data.Properties.AddRange(floatProps);
        data.Properties.AddRange(intProps);
	}
	
	/* ! Don't modify anything below this line ! */
	public ES2UserType_EntityDataStructuresResolvedEntityObject():base(typeof(EntityDataStructures.ResolvedEntityObject)){}
}
In the above code, ResolvedIntegerProperty, ResolvedFloatProperty, and ResolvedStringProperty are all simple classes with a string id and value field of appropriate type. I have also added each one of those to the supported types list. Example:

Code: Select all

public class ES2UserType_EntityDataStructuresResolvedStringProperty : ES2Type
{
	public override void Write(object obj, ES2Writer writer)
	{
		EntityDataStructures.ResolvedStringProperty data = (EntityDataStructures.ResolvedStringProperty)obj;
		// Add your writer.Write calls here.
		writer.Write(data.PropertyValue);
		writer.Write(data.PropertyId);

	}
	
	public override object Read(ES2Reader reader)
	{
		EntityDataStructures.ResolvedStringProperty data = new EntityDataStructures.ResolvedStringProperty();
		Read(reader, data);
		return data;
	}

	public override void Read(ES2Reader reader, object c)
	{
		EntityDataStructures.ResolvedStringProperty data = (EntityDataStructures.ResolvedStringProperty)c;
		// Add your reader.Read calls here to read the data into the object.
		data.PropertyValue = reader.Read<System.String>();
		data.PropertyId = reader.Read<System.String>();

	}
	
	/* ! Don't modify anything below this line ! */
	public ES2UserType_EntityDataStructuresResolvedStringProperty():base(typeof(EntityDataStructures.ResolvedStringProperty)){}
}
Still I'm getting the "Easy Save does not support saving of type System.Collections.Generic.List`1[EntityDataStructures.ResolvedEntityObject]" error.

I thought maybe this was due to my ResolvedEntityObject class containing a collection inside, and qualifying as a nested collection, although it really should behave more like a wrapper in this case. But I went ahead and created an EntitySaveWrapper class anyways with a List<ResolvedEntityObject> member, and guess what: I didn't get the same error this time but I got three other errors, one for each Resolved[Type]Property class: "Easy Save does not support saving of type System.Collections.Generic.List`1[EntityDataStructures.ResolvedIntegerProperty]." etc. I suspect those are coming from the custom type code I added manually, and I could try and write a wrapper class for each, but this doesn't really seem right to me. According to the docs on the website ES2 should be able to handle generic collections of a custom type as long as the underlying type has support added for it. Am I wrong? Am I missing something here?

Please help!
Last edited by taylank on Thu Aug 31, 2017 5:53 pm, edited 1 time in total.
User avatar
Joel
Moodkie Staff
Posts: 4852
Joined: Wed Nov 07, 2012 10:32 pm

Re: Difficulty with saving a list of a custom type

Post by Joel »

Hi there,

I believe the error is because you're using the Read methods to read the List, when you should be using the ReadList methods.

i.e.
var intProps = reader.Read<List<ResolvedIntegerProperty>>();
var floatProps = reader.Read<List<ResolvedFloatProperty>>();
var stringProps = reader.Read<List<ResolvedStringProperty>>();
Should be:
var intProps = reader.ReadList<ResolvedIntegerProperty>();
var floatProps = reader.ReadList<ResolvedFloatProperty>();
var stringProps = reader.ReadList<ResolvedStringProperty>();
All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Re: Difficulty with saving a list of a custom type

Post by taylank »

Sadly that did not make a difference. The error happens with the Write method call in any case. Any other ideas?

Edit: In fact I had ES2 autogenerate the type, omitting all the List properties, like below:

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EntityDataStructures;

public class ES2UserType_EntityDataStructuresResolvedEntityObject : ES2Type
{
	public override void Write(object obj, ES2Writer writer)
	{
		EntityDataStructures.ResolvedEntityObject data = (EntityDataStructures.ResolvedEntityObject)obj;
		// Add your writer.Write calls here.
		writer.Write(data.Id);
		writer.Write(data.SavedPosition);

	}
	
	public override object Read(ES2Reader reader)
	{
		EntityDataStructures.ResolvedEntityObject data = new EntityDataStructures.ResolvedEntityObject();
		Read(reader, data);
		return data;
	}

	public override void Read(ES2Reader reader, object c)
	{
		EntityDataStructures.ResolvedEntityObject data = (EntityDataStructures.ResolvedEntityObject)c;
		// Add your reader.Read calls here to read the data into the object.
		data.Id = reader.Read<System.String>();
		data.SavedPosition = reader.Read<Int2>();

	}
	
	/* ! Don't modify anything below this line ! */
	public ES2UserType_EntityDataStructuresResolvedEntityObject():base(typeof(EntityDataStructures.ResolvedEntityObject)){}
}
A list of this type still will not be saved.
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Re: Difficulty with saving a list of a custom type

Post by taylank »

Further experimentation:
I tried saving a List<Int2>, Int2 being an integer tuple with int x and int y properties. I know ES2 does not have a problem with this type elsewhere in code because it saves and loads it just fine. A list of Int2 would also not save, with the same error message.
Is it simply the case that ES2 cannot handle a generic list of a custom type? Does it have to be a list of a type supported by default?
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Re: Difficulty with saving a list of a custom type

Post by taylank »

And as a final sanity check I tried saving a list of System.Int32 and guess what: that also failed.

Code: Select all

saveGameManager.SaveData(new List<int> { 1, 0 }, GameSettings.SaveTagUnitsInPlay);

public void SaveData<T> (T data, string tag)
    {        
        writer.Write(data, tag);   
    }

Sanity check failed. I am currently on Unity Pro 2017.1.0p5 btw.

Update: Int32 array also fails, btw.
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Re: Difficulty with saving a collection of any type

Post by taylank »

OK I think I know what's going on.
The problem is, I have the writer.Write method wrapped in a generic method of SaveData<T> (T data, string tag) so I could decouple it from other classes in my library and just have them call this method.

Code: Select all

public void SaveData<T> (T data, string tag)
    {        
        writer.Write(data, tag);
    }
I think this is confusing ES2 due to reflection stuff that I suspect is going on behind the scenes, and it cannot resolve the runtime type of T when T is a collection. Because I have no issues when I do this for instance:

Code: Select all

var list = new int[] { 1, 0 };        
writer.Write(list, tag);
When I pass the exact same list and tag through the generic argument on SaveData<T> it bombs.

Interestingly the generic pattern works fine when a collection is passed inside a custom wrapper type.

Does this qualify as a bug then?
taylank
Posts: 6
Joined: Wed Aug 30, 2017 10:28 pm

Re: Difficulty with saving a collection of any type

Post by taylank »

Posting my final solution in case anybody using the same pattern runs into the same issue:

Code: Select all

    public void SaveDataAsList<T> (List<T> data, string tag)
    {
        writer.Write(data, tag);
    }
This helps ES2 understand what is being passed in is a list. Otherwise passing a List<T> through SaveData<T> has the same issues as a nested collection.
User avatar
Joel
Moodkie Staff
Posts: 4852
Joined: Wed Nov 07, 2012 10:32 pm

Re: Difficulty with saving a collection of any type

Post by Joel »

Glad you managed to find a solution to your problem, and apologies for the late reply. Looks like we're having problems with our email servers, so notifications of new posts on the forums are arriving very late.

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