Okay, So I got the friction-less login implemented. I also set up a few virtual currencies through the Game Manager. So now I'm at a point that when the user successfully logs-in, I want to take their saved information from PlayFab and convert it into a Player object that I use to model them client-side.
My question is, is the only way to do such a thing by using GetUserData and cycling through every single key value pair? This can get very messy if a Player object holds many different values (the game currencies, inventory items, messages in an inbox, as well as a bunch of other ints and booleans that toggle different events/settings within the game).
My previous apps that didn't use a BaaS such as PlayFab, I just serialized a Player object using Google JSON library (Gson), which saved me the mess of having to initializing every single boolean and int on a player object. I was thinking I could use SetUserData and set a Key called 'Player' and make its value be the Gson string representation of the Player Object, but would this go against how PlayFab 'should' be used? As in, if I just save entire Player Objects instead of individual key/value pairs, would that mess up how inventories and currencies work with PlayFab?
Or should I only serialize the mundane aspects of the player object like the ints, booleans, etc, and then on log-in, separately initialize catalogs and currencies using the key/value pairs?
Any pointers/help would be greatly appreciated! I just don't want to have to initialize every single member variable/field of my Player object 1 by 1 upon every login.
Quick Example (this isn't how I've necessarily designed the player object, just showing what I mean):
Say by default, new Players are represented by an object that has the following fields:
string displayName;
int coins;
int gems;
Inbox inbox;
bool hasSeenWelcomeMessage;
bool hasPlayedEndlessMode;
bool hasPlayedStoryMode;
As you can see this object can get very large as a game progresses, to hold many more booleans, ints, strings, etc. Before I would turn this entire object into a JSON string using Gson at every save, and at startup I'd acquire the JSON string again and turn it back into an object. Never had to initialize individual fields. However it seems like with PlayFab I need to go through every Key/Value pair and initialize the corresponding field in the Player Object using those values. That can become troublesome. Are there better ways to do this?
Answer by Joshua Strunk · Nov 22, 2016 at 04:59 PM
So short answer is its a mix of both initializing a bunch of different fields and not.
Long answer.
Right now user / character data breaks out on Playfab into a few different structures. I am going to highlight the primary ones for just accounts.
--
Using Typescript type notation
Account Info
VirtualCurrency: { [ currencyId: string] : number}
Inventory: ItemInstance[]
Statistics : { [ statisticId: string ] : number }
Data (all of structure {[ key:string ] : value : string})
---
So in the simplest / quickest example you could simply use a Read/Write Data to just write a single JSON blob for your users data. (Really don't recommend this)
I will just take your example and show how I would break it out.
--
string displayName
Leave this as part of account info TitleDisplayName
--
int coins
int gems
Move these to VirtualCurrency and then use getters / properties to access
public int coins { get { return virtualCurrencies["GC"]; } }
--
Inbox inbox;
Would move this to character read and just serialize it to json as you are already doing?
CharRead:{ inbox: /* json encoded string representing inbox */ }
--
bool hasSeenWelcomeMessage;
bool hasPlayedEndlessMode;
bool hasPlayedStoryMode;
These I would actually move to statistics for analytics purposes. Then just use good old 1 - 0 to represent the true false.
public bool hasSeenWelcomeMessage{ get { return statistics["hasSeenWelcomeMessage"] == 1; } }
Answer by kabumere · Dec 03, 2016 at 10:28 AM
Alright, for early testing I was just serializing the entire Player object using JSON .NET. Now I want to fix the startup system to model what you have suggested. Some questions though:
1) Why wouldn't you keep the display name as part of what's serialized with the player object? Why should I constantly obtain it from the service instead?
2) As far as virtual currencies: so I would obtain the entire VC Dictionary and attach it to the player object via a member field. Then whenever I want a certain one like gem or coins, I'd just set the getter for that property to return the value of the dict. for that currency. So the setter I would have to also change the value of that currency in the dict., and then sync the entire dictionary back to the service upon every save (can you point me to the API page for this?)
3) What makes the statistics dictionary special? Why should I use that for my booleans? Do you choose that just to organize the example Player class better, or is there a deeper reason?
4) I see in Game Manager under Settings -> API Features there are options for allowing client to add & subtract currencies. If I didn't enable these (since they aren't enabled by default), how else would I make those calls from my game?
(Sorry for posting this as an answer. I tried posting this as a comment several times and it wouldn't allow me, even though it was under the 1200 char limit, before my latest edit at least)
Related: is there a single API call that will get me everything about a Player that I can call and sift through st game startup? because it seems like as it stands I would need to do a call for virtual currencies, then a call for stats, then a call for player data. if every login is multiple calls right off the bat, I may go over my PlayFab limits fairly quickly.
I'm the same vein, is there a way to make the get player data call just retrieve ALL player data key Value pairs without having to specify exact ones?
Checkout GetPlayerCombinedInfo for getting all the different types of data a user can have at once.
For getting all the Key:Value pairs from a get_Data call just pass in a null value for the Keys parameter on those requests.
Also, have a look at the InfoRequestParameters parameter in all login calls. Since you want to get the data on startup, that may be your best bet.
1) By default usernames are set to be force unique which allows searching on them in various apis. You can actually go ahead and save the display name to userdata the important thing is make sure you actually set the really display name value in the playfab api
2) So the setter here is a bit more complicated and pretty much ties into your question 4. The thing is you don't want to really capture a change to a users virtual currency as a "I am settings this value" kind of change. You want to capture and sync the actions that cause the value to change up or down. (all VC APIs are add/subtract, no set/update).
This quickly morphs into a different question, "How do I make sure my game is secure against hacking/server authoritative with it's data?"The brief answer is use ReadOnly/Internal data and keep it so virtual currencies and statistics can only be chanced from a server/admin api calls. To get fast and easy access to a server environment to run server api calls from I recommend checking out Cloud Script.
3) This depends on the number of values, but by using statistics you can segment you r users. This lets you filter your users and do targeted rewards/ads/stores/events/etc...