question

kabumere avatar image
kabumere asked

Converting PlayFab Info to Player Object

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.

apisPlayer DataAccount ManagementPlayer Inventory
1 comment
10 |1200

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

kabumere avatar image kabumere commented ·

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?

0 Likes 0 ·
Joshua Strunk avatar image
Joshua Strunk answered

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

  • PlayFabId : string
  • Username : string
  • Email : string
  • TitleDisplayName : string

VirtualCurrency: { [ currencyId: string] : number}

Inventory: ItemInstance[]

Statistics : { [ statisticId: string ] : number }

Data (all of structure {[ key:string ] : value : string})

  1. Read / Write
  2. Read
  3. Internal

---

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; } }
10 |1200

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

kabumere avatar image
kabumere answered

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)

6 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.

Joshua Strunk avatar image Joshua Strunk commented ·

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...

1 Like 1 ·
kabumere avatar image kabumere commented ·

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?

0 Likes 0 ·
Joshua Strunk avatar image Joshua Strunk kabumere commented ·

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.

1 Like 1 ·
brendan avatar image brendan ♦♦ Joshua Strunk commented ·

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 Like 1 ·
Show more comments

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.