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 (http://code.google.com/p/andengine/source/browse/src/org/anddev/andengine/util/levelstats/LevelStatsDBConnector.java).

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(
               PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_ID, -1);
       if (playerID != -1) {
           this.mPlayerID = playerID;
       } else {
           this.mPlayerID = MathUtils.random(1000000000, Integer.MAX_VALUE);
           SimplePreferences
                   .getEditorInstance(context)
                   .putInt(PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_ID,
                           this.mPlayerID).commit();
       }
       playerName = simplePreferences.getString(
               PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_NAME, "-");
   }
}

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:
CREATE TABLE IF NOT EXISTS `gameresults` (
`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,
PRIMARY KEY (`id`),
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() {
           @Override
           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) {
       Debug.e(e);
  }
  }
  }).start();

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` ,
                               `name`
                           )
                           VALUES ('" . $_POST['player_id'] .
                           "', '" . $_POST['score'] .
                           "', '$name " .
                           "', '$currDate'" .
                           "');";
           mysql_query($sql) or die($fail . mysql_error());
           echo "";
   } else {
       echo $fail;
   }
?>

Outlook

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

Use of Wakelok and Stand-By in Android Games

When to suppress the Stand-By Mode in Games?

This depends on the kind of game you want to develop. If the player has to interact with the game constantly or if he only has a limited time to draw, the stand-by mode usually doesn’t need to be disabled.

It’s relevant to disable the stand-by mode only in video-or demonstration modes, when the player doesn’t need to interact at all for a certain time. It’s more important, how the game reacts on the stand-by mode. It’s the best solution if a game automatically switches to the pause-menu on a stand-by, where the player can easily resume the game.

How to suppress the Stand-By Mode?

You use so called “Wakelocks” to suppress the stand-by mode on a Android Phone. Here I show you a sample implementation using the game-engine e3roid. It’s very important to release the Wakelock before exiting the game.

Manifest:

You need to request the nessesary permision:

<uses-permission android:name="android.permission.WAKE_LOCK" />

Main Class:

public class StoneSetter extends E3Activity {
private PowerManager.WakeLock wl;
public E3Scene onLoadScene() {
   ...
   PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
   wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "SomeTag");
   wl.acquire();
}
 
public boolean onKeyDown(E3Scene in_scene, int keyCode, KeyEvent event) {
if((keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK)){
   exitGame();
   return true;
}else{
   return false;
}
}
 
public void exitGame(){
  wl.release();
  finish();
}
 
}

Game Development Considerations

If you want to develop a game, you have to think first about the genre to develop the game for. Should it be a puzzle game / board game or an action game? After answering this question you have a lot of follow up questions to answer.

If you choose an action game (Jump&run) you have to take care of a lot more things than in a turn based game. You always have to take care that your programming is really performant, to achieve high frame rates. In a turn-based-game it’s less important as the user usually doesn’t notice if a calculation takes a little longer. A good overview on this topic is documented in this article, which is unfortunately only available in German language: Real Time vs Turn Based Games.

My first Android-Game-Project was such an Action Game. But the performance itself wasn’t the biggest problem for me (after I found some good 2d Game Engines). The biggest problem has been the level design, which take a large amount of time. That is why I postponed this project. After the start phase the level design took up about 80% of the development time.

Sample of my first Project(Graphics mainly from http://www.reinerstilesets.de/ – a really good source for grafics and more)

Continue reading