question

jakekim avatar image
jakekim asked

Saving long text data in one UpdatePlayerData call?

Hello,

I'm porting a Unity title 16C4 with Jump. The game has 144 levels, and I'm saving the states for each level in one long string file. When I load it, I parse the string into each level's data and store internally in game's dictionary.

I've been using PlayerPref to save / long the long string file. However, when I try to save the long string file using UpdatePlayerData, I get this error.

Got error setting user data - Invalid input parameters.

I assume the string is too long, so I tried calling UpdatePlayerDate for each value, which gave me this error.

The client has exceeded the maximum API request rate and is being throttled

How can I go about this? It seems I should somehow send the long string file and parse it in the game.

https://community.playfab.com/questions/10755/optimizing-player-data.html

It seems possible in the comment from the link above, as you said "Yes, encoding player data as a JSON object and stringifying that as the data in a player data key/value pair is a very common usage pattern. It would definitely be more efficient in our current data model.", but how could I do this..? Is there any sample for it?

Thanks!

-Jake

apisPlayer Dataunity3dsdks
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image
brendan answered

Yes, if you're using the player data model for your saves (as opposed to the Entity model), you'll want to collect that info into very few keys - one, ideally. Can you provide the code snippet showing how you're making the call, and let us know the details of the parameters you're passing in? How long is the total save data string?

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

jakekim avatar image
jakekim answered

Here's the sample save file. The real file is longer, but if it is longer than this, it throws invalid parameter error.

rooms2-save-json-string.txt

Here's the part getting and parsing (deserializing) the user data into SortedDictionary<string, string>.

PlayFabClientAPI.GetUserData(request,
			(result) =>
			{
				Debug.Log("Got user data");
				if ((result.Data == null) || (result.Data.Count == 0))
				{
					Debug.Log("No user data available");
				}
				else
				{
					playerData = result.Data;
                   
                    //update your local copy of playerData

                        SortedDictionary<string, string> dataList = PlayFab.Json.PlayFabSimpleJson.DeserializeObject<SortedDictionary<string, string>>(item.Value.Value);

                    }
                }Here's the part exporting the save data. The data used in game is stored in dataList.protected string ExportSaveData(bool isEncrypt = false)
    {
        string serializedReplay = PlayFab.Json.PlayFabSimpleJson.SerializeObject(dataList);
        return serializedReplay;
    }

    //  datalist 
    SortedDictionary<string, string> dataList = new SortedDictionary<string, string>();

It is updating fine with shorter save file, but when I parse it upon receiving back the saved data, it gives me this InvalidCastException: Cannot cast from source type to destination type error.


7 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

brendan avatar image brendan commented ·

What are the details of the invalid parameter error? The errorDetails in the response will tell you specifically what the problem was.

For parsing the data when you retrieve it, what exactly is "item" in your code snippet? What your code shows is that you get the result back, and you set playerData to result.Data. That, then, should be what you deserialize, in order to get the JSON.

0 Likes 0 ·
jakekim avatar image jakekim brendan commented ·

Sorry, some line of the code was deleted, here's clean one.

The item variable is just a var in foreach loop to search playerData. I get correct key and value when I print item.Key and item.Value.Value

public void GetPlayerData() {
		GetUserDataRequest request = new GetUserDataRequest()
		{
			PlayFabId = PlayFabId,
			Keys = null 
		};
		PlayFabClientAPI.GetUserData(request,
			(result) =>
			{
				Debug.Log("Got user data");


				if ((result.Data == null) || (result.Data.Count == 0))
				{
					Debug.Log("No user data available");
				}
				else
				{
                    playerData = result.Data;


                    foreach (var item in playerData)
					{
						Debug.Log(item.Key + "<color=orange> = </color>" + item.Value.Value);
                        SortedDictionary<string, string> dataList = PlayFab.Json.PlayFabSimpleJson.DeserializeObject<SortedDictionary<string, string>>(item.Value.Value);


                        foreach(var value in dataList)
                            print("<color=orange>value : </color>" + value);
                    }
                }
	}

0 Likes 0 ·
jakekim avatar image jakekim jakekim commented ·

I figured out Deserialization issue! The below code works.

playerData = result.Data;


                    foreach (var item in playerData)
					{
						Debug.Log(item.Key + "<color=orange> = </color>" + item.Value.Value);
                        var dataListTemp = PlayFab.Json.JsonWrapper.DeserializeObject<Dictionary<string, string>>(item.Value.Value);


                        SortedDictionary<string, string> dataList = new SortedDictionary<string, string>(dataListTemp);


                        foreach (var value in dataList)
                            print("<color=orange>value : </color>" + value);
                    }

Now, the only issue left is saving long string into PlayFab via Json.

0 Likes 0 ·
jakekim avatar image jakekim brendan commented ·

On line DeserializeObject is called, I get the error below.

InvalidCastException: Cannot cast from source type to destination type. PlayFab.Json.PlayFabSimpleJson.DeserializeObject[SortedDictionary`2] (System.String json, IJsonSerializerStrategy jsonSerializerStrategy) (at Assets/PlayFabSdk/Shared/Internal/SimpleJson.cs:608) PlayFabManager.<GetPlayerData>m__4 (PlayFab.ClientModels.GetUserDataResult result) (at Assets/Scripts/PlayFabManager.cs:101) PlayFab.Internal.PlayFabHttp+<MakeApiCall>c__AnonStorey0`1[PlayFab.ClientModels.GetUserDataResult].<>m__1 () (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabHTTP.cs:202) PlayFab.Internal.PlayFabWww+<MakeApiCall>c__AnonStorey3.<>m__0 (System.String response) (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:139) UnityEngine.Debug:LogException(Exception) PlayFab.Internal.<MakeApiCall>c__AnonStorey3:<>m__0(String) (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:143) PlayFab.Internal.<PostPlayFabApiCall>c__Iterator1:MoveNext() (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:215) UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

0 Likes 0 ·
jakekim avatar image jakekim brendan commented ·

And here's the details of errors when I try to save longer string into PlayFab.

First I get this

Got error setting user data UnityEngine.Debug:Log(Object) PlayFabManager:<UpdatePlayerData>m__3(PlayFabError) (at Assets/Scripts/PlayFabManager.cs:73) PlayFab.Internal.<MakeApiCall>c__AnonStorey3:<>m__0(String) (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:152) PlayFab.Internal.<PostPlayFabApiCall>c__Iterator1:MoveNext() (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:215) UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

and this right after

Invalid input parameters UnityEngine.Debug:Log(Object) PlayFabManager:<UpdatePlayerData>m__3(PlayFabError) (at Assets/Scripts/PlayFabManager.cs:74) PlayFab.Internal.<MakeApiCall>c__AnonStorey3:<>m__0(String) (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:152) PlayFab.Internal.<PostPlayFabApiCall>c__Iterator1:MoveNext() (at Assets/PlayFabSdk/Shared/Internal/PlayFabHttp/PlayFabWWW.cs:215) UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

0 Likes 0 ·
brendan avatar image brendan jakekim commented ·

First, I'd simplify this to a for. Something like:

for (var key in playerData) {
  if (playerData.hasOwnProperty(key)) {
    var keyVal = playerData[key];
}
}

At that point, keyVal contains a UserDataRecord that you can then use to get to the Value, and deserialize it, if you're storing JSON in the data.

I'd also recommend having a look at the way we use the data in the UnicornBattle demo app (https://github.com/PlayFab/UnicornBattle/blob/ff1285d8a13fb3e6f59f8df838a83a1a825258f2/UnicornBattle/Assets/Scripts/PF_StaticsAndHelpers/PF_PlayerData.cs).

0 Likes 0 ·
jakekim avatar image jakekim commented ·

It's all good now! There's no more error when updating save data, and the data's saving / loading good :) Thanks!

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.