The XNA Highscores Component (distributed leaderboards, sort-of)

This is a simple game that uses the "HighscoreComponent" to manage a highscore list. It stores the list both locally, and uses Xbox Live! sessions to exchante highscore information with other players who happen to be playing the game at the same time. This way, high scores from people who are not your local players will show up, which is all kinds of cool :-)

The game itself is very silly: Hold down left shift (or the left trigger) to make a yellow bar go across the screen. Make it stop before it hits the right hand side. Then hold down right shift (or right trigger) to make a blue bar go across the screen. Make it stop before it runs out of space created by the yellow bar. The score is a factor of how far right you managed to push the yellow bar and the blue bar without overrun. Yeah, a total hit at parties!

This allows you to implement a limited kind of leaderboards. Usage is really simple: just create a GamerServicesComponent and add to your components, then create the HighscoreComponent and add to your components. As arguments, pass your game, null, and the name of your game as a brief string.

When your player finishes a game, call HighscoreComponent.Global.SetNewScore() and pass in the player index, the score, and an optional message (could be an empty string) not to exceed 64 characters. That data will be stored in the highscore table, and exchanged with other players of your game. The best N scores for each local player, and the best N scores for all networked players you've seen (which includes players they have seen, etc) are remembered on the hard disk. N is currently 50.

When you ask for highscores, you can ask for highscores for a current user, or for the global highscore list including the network downloaded data (if any). You will receive a list of objects containing the gamertag, score, date and message for each of the entries. For example, to receive scores for the gamer currently signed in as PlayerIndex.One, simply call:

Highscore[] scores = new Highscore[10]; int n = HighscoreComponent.Global.GetHighscores(PlayerIndex.One, scores);

This will return the number of scores that were actually returned, in case the local player hasn't yet played (in this case) 10 full games yet.

Some additional notes:

  • If the user refuses to choose a storage device, in case there are multiple, then there will be no highscores loaded, and whatever scores you apply will not be saved. If the user chooses a device the next time a score is added, the current scores will be merged with those already on the storage device, and properly saved.
  • It takes a few cycles of Update() after you have called SetNewScore() before it is properly saved to disk. This is because of the need to ask for a new device if the current device has been disconnected, etc.
  • The network session system will try to both connect as a peer, and if no suitable sessions are found, host a new session. Thus, session management is mostly "magic" and should make it so that if at least two peers are playing at the same time for at least 30 seconds, highscore information will be exchanged.
  • You can call ClearHighscores() to remove all current highscore data. Again, this takes a couple of iterations of Update() to actually hit the disk or memory cartridge.
  • You can call PurgeHighscoresOlderThan(Date) to remove highscores that are too old. Typically, you will want to know that some highscores have been loaded from disk before you call this (because it's an immediate operation with no "memory" for later received or loaded data). You can check whether any data has yet been loaded from disk by checking the HasReadFile boolean property.
  • If you want to suspend the operation of the highscore component temporarily, you can set its Enabled property to false. It will immediately terminate any network session it may have open. If a save is pending, that save cannot complete until you re-enable the component, though!
  • When you create the component, you have the option of passing in network session filtering parameters. This is useful if you also want to use session management on your own for multiplayer games, and want to be able to tell highscore exchange sessions from "real" game sessions. Set one of the variables to some unique value for the highscore sessions. Remember to disable the highscore component before you create your own session, though!
  • The code is not written with an eye towards conserving garbage in the garbage collector. The game code allocates several strings every frame, so there will be small hitches in frame rate at some intervals. Perhaps I'll go through and clean that up in some future version, if there's a lot of interest in this component.

This code is released Open Source under the MIT license. Copyright 2008 Jon Watte, All Rights Reserved. You may use it free of charge for any purposes, provided that Jon Watte's copyright is reproduced in your use, and that you indemnify and hold harmless Jon Watte from any claim arising out of any use (or lack of use or lack of ability of use) you make of it. This software is provided as-is, without any warranty or guarantee, including any implicit guarantee of merchantability or fitness for any particular purpose. Use at your own risk!

For more information and updates, stop by my XNA programming area at http://www.enchantedage.com/highscores

You will have to register and log in to download the code to build the game (which includes the component).

AttachmentSize
Highscores20081022.zip48 KB
highscores-1.png13.72 KB
highscores-2.png64.43 KB