How to save a string[ , ,] to Cloudsave? (multidimensional array)

I’m storing some game data in a string[ , , ], and would like to save/load that to/from Cloudsave.
Let’s call the 3 dimensions:

  • “category”
  • “entry”
  • “data”

As far as I understand, Cloudsave only accepts Dictionaries<string,object>.

Therefore, I make:
a Dictionary<string,object> (_saveThingie) containing:
a Dictionary<string,object> of categories (_fullSave) , containing:
many dictionaries<string,object> of entries (_oneCategory), each containing:
many dictionaries<string,string> (_oneEntry) of data.

So far "saveThingie" saves ok, I can also retrieve _fullSave from it with:

var _incoming = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> {"fullSave"});
if (_incoming.TryGetValue("fullSave",out var _fullSaveItem))
{
    _fullSave = _fullSaveItem.Value.GetAs<Dictionary<string, object>>();
}

_incoming is a
System.Collections.Generic.Dictionary 2[System.String,Unity.Services.CloudSave.Model

_fullSave is a
System.Collections.Generic.Dictionary 2[System.String,System.Object]

The structure of _fullSave still looks right to me until here, if I have the keys listed:

foreach (var dictkey in _fullSave.Keys)
{
    Debug.Log(dictkey);
}

Only… I can’t fathom how retrieve the categories and entries as dictionaries :face_with_head_bandage:

I tried sooo many approaches… last one:

var _thisCat = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(_fullSave["cat0"].ToString());

But whatever I try, it returns either a string or a Json.Linq.Object (like the above code).

Questions

  1. Is there any way to get nested dictionaries back?
  2. Is there any easier way to save a string[ , , ] to CloudSave? Maybe the array in a class/struct ???

Why don’t you just flatten the whole thing into a 1D array and add metadata about the dimensions ?
Something like this

using System;

public struct MultiDimString3D
{
    public int width;
    public int height;
    public int depth;
    public string[] flatArray;
    
    MultiDimString3D(string[,,] strings)
    {
        width = strings.GetLength(0);
        height = strings.GetLength(1);
        depth = strings.GetLength(2);
        
        flatArray = new string[width * height * depth];
        
        for(int x = 0; x < width ; ++x)
        {
            for(int y = 0; y < height ; ++y)
            {
                for(int z = 0; z < depth ; ++z)
                {
                    int index = x + (y * height) + (z * width * height);
                    flatArray[index] = strings[x,y,z];
                }
            }
        }
    }
    
    public string[,,] To3DArray()
    {
        string[,,] arr = new string[width , height , depth];
        
        for(int i = 0 ; i < flatArray.Length ; ++i)
        {
            int z = i / (width * height);
            
            int leftover = i % (width * height);
            
            int y = (leftover / height);
            
            int x = leftover % y;
            
            arr[x,y,z] = flatArray[i]; 
        }
        
        return arr;
    }
}

With this class , you can convert to a MultiDimString3D right before sending to cloud save , then use the To3DArray() to get it back to a string[,,] after loading it from cloudsave

Thanks heaps.
Wow… very nice way to generate the indexes. (flatArray size needs to be bigger though)

But how to get that struct to and from Cloudsave???
Do I need to make a class containing such a struct, save an instance of that class within a Dictionary<string,object> or - ??

Sorry if it’s dummy question, I am just trying CloudSave for the first time and I’m pretty “confuzzled”…

So far I have your struct saved on CloudSave.

    public async void SaveArray()
    {
        MultiDimString3D multiDimStr = new MultiDimString3D (_datAr);
        Dictionary<string, object> _saveDict = new Dictionary<string, object>
            {
                { "multDimStr",multiDimStr }
            };
    }
    await CloudSaveService.Instance.Data.Player.SaveAsync(_saveDict);
 }

I try to retrieve it with something like:

    async Task ReadArray()
    {
        var _saveDict= await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "_saveDict" });


    }

Now _saveDict is a System.String,Unity.Services.CloudSave.Models.Item
so… kinda stuck at the same place.

Should there be some “.GetValue()” method used on the retrieved item? Or how do I get the MultiDimString3D back?

Whew… I managed to save/retrieve the string[ , , ] directly to CloudSave.
… after “only” a few hundred fruitless trials.
I’m pretty sure that’s not the “straightforward” way… but it worx.
I save with:

Dictionary<string, object> _saveDict = new Dictionary<string, object>
            {
                {"_dataArray",gui._dataArray},
            };
await CloudSaveService.Instance.Data.Player.SaveAsync(_saveDict);

and retrieve with:

var _savedDictionary = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> {"_dataArray"});
string[,,] _dataArray = _savedDictionary["_dataArray"].Value.GetAs<string[,,]>();

I do not really understand the structure of the “retrieve” part though, just figured it by trial and error.
(i.e. Why do I submit the same key 2 times??)

Anyhow, the trick was to add the 2nd line in the retrieve part (…Value.GetAs())

Now regarding the MultiDimString3D:
I can actually not get it to work like that as:
created index > array length
x + (y * height) + (z * width * height) > width * height * depth
and, when restornig the array, it divides by 0.

During my trials I was using something like that, it would need to be wrapped in a class or struct as well, in order to have width/height/length stored. I actually used it to flatten to a single string.

    public string[] Array3DTo1D(string[,,] _ar)
    {
        int cnt = 0;
        string[] array1D = new string[_ar.Length];
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                for (int k = 0; k < length; k++)
                {
                        array1D[cnt]=(_ar[i, j, k]);
                        cnt++;
                }
            }
        }
        Array1DTo3D(array1D);
        return array1D;
    }

    string[,,] Array1DTo3D(string[] _array1D)
    {
        int cnt = 0;
        string[,,] _array3D = new string[width, height, length];

        while (cnt < width * height * length - 1)
        {
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    for (int k = 0; k < length; k++)
                    {
                        _array3D[i, j, k] = (_array1D[cnt]);
                        cnt++;
                    }
                }
            }
        }
        return _array3D;
    }
}

So… Thanx God, no need of that for me just now, as the string[,] now saves and retrieves with Cloudsave.

Thanks very much @bloodthirst69.