Updating Stats 2017
You update stats by sending the latest value for the Xbox Live Serivce using the
StatsManager APIs which will be discussed below.
It is up to your title to keep track of player stats, and you call
StatsManager to update these as appropriate.
StatsManager will buffer any changes and flush these to the service periodically. Your title can also manually flush.
Do not flush stats too often. Otherwise your title will be rate limited. A best practice is to flush at most once every 5 minutes.
It is possible for a player to play your title on multiple devices. In this case, you need to make certain effort to keep things in sync.
For example, if a player got 15 headshots on their Xbox at home. Later, they got 10 more headshots on their Xbox at a friend's house. You would need to send the stat value of 25 on the second device. But you would have no way of knowing this without somehow synchronizing this information.
There are a few ways you can do this:
- Store them using Connected Storage. Usually you would use Connected Storage for per-user save data. This data is kept in sync across different devices for a given user.
- Use your own web service to keep stats in sync if you already have one for performing auxillary tasks for your title.
As we mentioned above, your title is responsible to keep track of player stats and, therefore, responsbile to support offline scenarios.
We will go through an example to tie these concepts together.
A common stat in a racing game is lap time. Usually lower is better for these stats. So you would create a stat and associated leaderboard, where lower is better. In other words, this leaderboard would be sorted in ascending order.
Your title would keep track of a user's lap times during their play session. You would update the Stats Manager only if they had a lap time lower than their previous best.
You can track their previous best in one of the following ways:
- From the save file using Connected Storage.
- Your own web service.
The service will replace the stat value no matter what. So even if you were to update with a lap time that's greater than their previous best, then their previous best would be overwritten.
So please ensure in your title, that you are only sending the proper stat values based on your gameplay scenario. In some cases lower values might be better, in some other cases higher might be better, or something else entirely.
Typically your flow for using stats is:
- Initialize the
StatsManagerAPI by passing in a local user.
- As a user plays through your title, update stat values using the
- These stat updates will be periodically flushed and written to Xbox Live. You can also do this manually.
You call the
StatsManager with a local user to initialize the API with the necessary information.
See below for an example
std::shared_ptr<stats_manager> statsManager = stats_manager::get_singleton_instance(); statsManager->add_local_user(user); statsManager->do_work(); // returns stat_event_type::local_user_added
Microsoft.Xbox.Services.Statistics.Manager.StatisticManager statManager = StatisticManager.SingletonInstance; statManager.AddLocalUser(user); statEvent = statManager.DoWork();
You write stats using the
stats_manager::set_stat family of functions. There are three variants of this function for each data type:
When you call these, the stat updates are cached locally on the device. Periodically these will be flushed to Xbox Live.
You have the option of manually flushing stats via the
stats_manager::request_flush_to_service API. Please note that if you call this function too often, you will be rate limited. This does not mean that the stat will never get updated. It merely means that the update will happen when the timeout expires.
statsManager->set_stat_integer(user, L"numHeadshots", 20); statsManager->request_flush_to_service(user); // requests flush to service, performs a do_work statsManager->do_work(); // applies the stat changes, returns stat_update_complete after flush to service
statManager.SetStatisticIntegerData(user, statName, (long)statValue); statManager.RequestFlushToService(user); statManager.DoWork();
Let's say that you have a first-person shooter. During a match you might accumulate the following stats:
|Best Kills Per Round||Integer|
|Lifetime Kill/Death Ratio||Number|
As the player goes through the match, you would increment the Kills Per Round, Lifetime Kills and Lifetime Deaths locally.
At the end of the match you would do the following:
- Compare the kills they got in the round, with their previous best. If it is greater, then update
- Update their Lifetime Kills and Deaths with the new values and update
- Calculate Kills/Deaths and update
Note that for 1 and 2, you need to know their previous stat values. See the above sections for best practices on retrieving these.
Any of these stats could correspond to a leaderboard, which will be discussed in the next article.
You can manually flush stats using
stats_manager::request_flush_to_service. You might want to do this if you are want to display a leaderboard.
For example, if you had a leaderboard for
Lifetime Kills in the above example, you would want to make sure that the stat updates corresponding to this stat had been flushed to the server before displaying the leaderboard. That way the leaderboards reflects the player's latest progress.
When the title closes, remove the user from stats manager. This will flush the latest stat values to the service.
statsManager->remove_local_user(user); statsManager->do_work(); // applies the stat changes, returns local_user_removed after flush to service