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
Answer by Brendan · May 28, 2018 at 09:24 PM
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?
Answer by JakeKim · May 29, 2018 at 01:20 PM
Here's the sample save file. The real file is longer, but if it is longer than this, it throws invalid parameter error.
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.
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.
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); } } }
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.
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)
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)
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).
It's all good now! There's no more error when updating save data, and the data's saving / loading good :) Thanks!