Developing a simple online high-score ranking system for Android games (1)

I’ve seen the question about how to maintain online high-score lists a couple of times, and now even a blog-reader asked me how I handled this topic. This was the impulse to start a short tutorial series in my blog on how I handled the online high-score-lists in my game and giving some further input on this topic. There might be better and more professional approaches available, but my solutions is quite flexible.
The solution is based on the LevelStatsDBConnector-Class of Andengine (

As the article would have become extremely long I’ve decided to split the tutorial into a couple (3 are planned by now) of logical posts. In my example I assume you use the Game-Engine Andengine. In the sample this engine is only used for some simple methods that make topics like converting of HTTP-Requests, using SharedPreferences and Random numbers easier. But it shouldn’t be a big deal to replace these methods with your own methods.

Collecting Statistical Data

To be able to establish an online highscore list, you first have to collect the relevant data. It’s obvious that you need the score of the player, but further information can also be very interesting and you can think about storing them for example: average time for a move, playtime, count of interactions, count of incorrect interactions, game over or game solved, …

Having an unique Player-ID

To distinguish between multiple players, each player should have an individual unique ID.  This ID has to be stored locally on the device (e.g. by using SharedPreferences, as shown in the example later), so you can read the ID in every game-session.
To make things easy, I simply use an random integer as player-id. There are a lot of other options on getting and unique ID – for example by using some Device specific data (IMEI). But as the player will also provide a name for the high-score-list, it will still be relatively uniqui – it’s more a 90% solution. The constructor of the StatsDB Class reads the ID or creates a new one if none is existend. The player name is also read by using the Shared Preferences.

public class StatsDBConnector  {
   private final int mPlayerID;
   private Context context;

   public StatsDBConnector(final Context pContext) {
       context = pContext;
       final SharedPreferences simplePreferences = SimplePreferences.getInstance(pContext);
       final int playerID = simplePreferences.getInt(
       if (playerID != -1) {
           this.mPlayerID = playerID;
       } else {
           this.mPlayerID = MathUtils.random(1000000000, Integer.MAX_VALUE);
       playerName = simplePreferences.getString(

Database structur and what you need to store the data online

To be able to store your game high-scores online you’ll need an Web-server and an online database. My example uses a web server with PHP and a MySQL Database. The structure of the database  is extremely easy. It’s just one tabel that uses at least the following fields:

Feld        Typ
player_id    bigint
name        varchar(20)
score        int
date        varchar(20) // You could use a date-field instead
ID        bigint, autoincrement

You can add further fields, depending on your needs. fields that could be interessting could be: playtime, state (completed, game over, cancelled), level, difficulty level, version of the game, …
The primary key is an simpel auto-increment ID and the Player_Id should be indexed to imporve some queries you will build later.

Here you find the SQL Code to build the table-structure in mySQL:
`player_id` bigint(20) NOT NULL,
`score` int(11) NOT NULL,
`date` varchar(20) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
KEY `level_id` (`level_id`,`player_id`)

Sending the game statistics to the Web Database

When a certain event happens we send the necessary tracking information by using a HTTP POST message to the receiver web-page. The sending will be done as follows:

Sending from the Adroid point of view

I show here an example on how to send the data to the web-server. The sending of the HTTP-Request is done in an separate thread, as you don’t know how long it takes for receive the answer – and we don’t want the game to freeze. In this sample there are still security measures missing. security measures will be treated in a later article.

new Thread(new Runnable() {
           public void run() {
               try {
final HttpClient httpClient = new DefaultHttpClient();
final HttpPost httpPost = new HttpPost("http://servername/order/insert.php");
final List nameValuePairs = new ArrayList(4);
nameValuePairs.add(new BasicNameValuePair("player_id",String.valueOf(StatsDBConnector.this.mPlayerID)));
nameValuePairs.add(new BasicNameValuePair("score",String.valueOf(StatsDBConnector.this.mPlayerID)));
nameValuePairs.add(new BasicNameValuePair("name",String.valueOf(StatsDBConnector.this.mPlayerID)));

httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
final int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
  final String response = StreamUtils.readFully(httpResponse.getEntity().getContent());
} else {
  Debug.e("http: send not possilbe");
} catch (final IOException e) {

Receiving the Data on the Server

On the receiving server has to be a PHP-script. This will first check if the required POST  parameters are available and if they are valid, else a hacker could use the parameters to try to do SQL code injection to compromise the server.
The next step is to build a SQL connection to the database and insert the data from the POST. In this phase it can also be a good idea to directly read the highscore lists, as the player wants to see his scores directly in the high-score lists.

"; // message to be displayed if something went wrong

// function to Check if the name is a valid string - only alphanumierc and space, dash, underline are allowed
// A good regular expression would be the better way to do this.
   function validateName($inName,$id){  
       $aValid = array(' ', '-',"_",",");
       if(!ctype_alnum(str_replace($aValid, '', $inName))) {
           $inName="player-" . ($id % 10000 );
       return $inName;

   if (
           isset($_POST['player_id']) &&
           isset($_POST['score']) &&
           isset($_POST['name']) &&
           is_numeric($_POST['player_id']) &&
           is_numeric($_POST['score'])){ // Check if all required varialbles are set and if the numeric ones are numeric

           mysql_connect($host, $us, $pw) or die($fail . mysql_error());
           mysql_select_db($db) or die($fail . mysql_error());

           $currDate = date("Y.m.d H:i");

           $name = validateName($_POST['name'],$_POST['player_id']);
           $sql = "INSERT INTO `gameresults` (
                               `player_id` ,
                               `score` ,
                               `date` ,
                           VALUES ('" . $_POST['player_id'] .
                           "', '" . $_POST['score'] .
                           "', '$name " .
                           "', '$currDate'" .
           mysql_query($sql) or die($fail . mysql_error());
           echo "";
   } else {
       echo $fail;


In the next article you’ll read about how to read high-score data from the Web-Database.