Tuesday, March 27, 2012

Using Amazon SimpleDB for Runtime App-Parameter Editing

When you develop an app, you probably have a few parameters that you need to adjust and tweak to find the desired value. The usual approach would be to change the parameter in the source code, recompile and deploy the app to your phone to test it. If you have a lot of parameters to tweak, you probably add a developer GUI  to your app that enables you to change the parameters at runtime.
In this blog post I present an easy to integrate alternative. Instead of reading the values from your source code or an in game GUI, the parameter values are stored and retrieved in a cloud database. The technology is build onto my former blog post about using Amazon SimpleDB, hence for this we use SimpleDB again. The idea of this approach is like this:
Store all parameters you want to edit in an own class, e.g. AppParameter. When running your app in edit mode you will retrieve the values for AppParameter from the database. On your PC you run a .NET application that allows you to edit the database parameters. Reread the values inside your app and test the changed parameters. No need to recompile or even restart you app and edit the parameters from any PC.
To make the integration easy I present a solution that uses C# reflections to automatically determine the fields of the AppParameter class. Saving and reading the parameter class to and from the SimpleDB uses generics. That means you can use the code for any class you define. Also the PC-Editor automatically creates the GUI for the class parameters. This is a picture of the Editor using some arbitrary game parameters:
The parameter class used here:
public class AppParamter
{
    public float JumpForce;
    public float Gravity;
    public float Height;
    public float Weight;
    public Vector2 StartPosition;
    public Vector2 LevelOffset;
    public int NumOfLives;
    public int MaxItems;
    public int MinItems;
}
If you want to use this with your app you only have to change the AppParameter class to contain the parameters you need. However, I currently only support the data format float, Vector2 and int. If you need other formats you need to extend the example.
The Editor provides buttons to read and write the values from and to the Amazon SimpleDB. With the Clear DB button you can delete the complete entry for this parameter class. This is recommendable every time you  change the structure of the parameter class. Copy Values creates a code snippet and copies it to the clipboard that sets the current values of your class. Use this once you finalized your parameters to set the values inside your app without the database. The code snippet created for this example is:
public void SetValues()
{
    JumpForce = 2.3f;
    Gravity = 7f;
    Height = 12f;
    Weight = 55f;
    StartPosition = new Vector2(12f, 25f);
    LevelOffset = new Vector2(-3f, -2f);
    NumOfLives = 7;
    MaxItems = 5;
    MinItems = 1;
}

Implementation

The core class of this project is the ParameterProcessor. It is realized as a singleton and provides the interface to the database. Furthermore it provides functions to create strings from values and the other way round. You will find a lot of similarities to my previous SimpleDB blog post.
The class is designed to be used from the PC-Editor as well as from the phone app. Therefore I did not use the Amazon AWS SDK for .NET for the SimpleDB access from Windows, but used the SDK designed for the Windows Phone. For that I needed to create Windows Library Project and add the SDK source files. Compiling and running it from Windows worked smooth with only one exception. In SimpleDBResponsce.cs line 39, the IsRequestSuccessful property for set is declared protected. This caused the XmlSerializer to throw an Exception. Removing the protected modifier solved the problem. Now let us take a look at the code. In the beginning the singleton is defined and in the constructor the amazon client is initialized. Don't forget to enter your SimpleDB keys there.
    public class ParameterProcessor
    {
        #region singleton
        // create singleton
        static readonly ParameterProcessor instance = new ParameterProcessor();
        /// <summary>
        /// Get instance of singleton.
        /// </summary>
        public static ParameterProcessor Instance
        {
            get
            {
                return instance;
            }
        }
        /// <summary>
        /// Explicit static constructor to tell C# compiler
        /// not to mark type as beforefieldinit
        /// </summary>
        static ParameterProcessor()
        {
        }
        #endregion

        // db
        AmazonSimpleDB m_sdb;

        /// <summary>
        /// Constructor
        /// </summary>
        ParameterProcessor()
        {
            // intitialize db
            // amazon cloud access
            m_sdb = AWSClientFactory.CreateAmazonSimpleDBClient("YourPublicKey", "YourPrivatKey");
        }
The function SaveToDB uses generics to pass the parameter class and instance of which values are stored in the database. The second function parameter is the name of the domain in SimpleDB. The domain needs to be created before. The response handler, which is called after the parameters have been stored in the database, currently does nothing. Using C# reflections the fields of the parameter class are easily iterated through an FieldInfo array provided by the function GetFields() from the Type class. The database then simply stores the field name as an attribute with the field value converted to a string. Additionally I save a time stamp that could be used to check if data has been updated without retrieving all attribute. This could make sense if the data you store is very huge. The complete class values are entered as one item into SimpleDB where the parameter class name is used as key value for the item. By that you could store multiple different parameter classes within one SimpleDB domain.
/// <summary>
/// Takes a class and enters its fields to an Amazon SimpleDB
/// </summary>
/// <typeparam name="T">Serializable Class to be saved into the database</typeparam>
/// <param name="data">Class instance that is saved into the database</param>
/// <param name="tableName">Name of domain in SimpleDB</param>
public void SaveToDB<T>(T data, string tableName)
{
    SimpleDBResponseEventHandler<object, ResponseEventArgs> handler = null;
    handler = delegate(object senderAmazon, ResponseEventArgs args)
    {
        //Unhook from event.
        m_sdb.OnSimpleDBResponse -= handler;
        PutAttributesResponse response = args.Response as PutAttributesResponse;
        if (null != response)
        {
        }
        else
        {
        }
    };

    m_sdb.OnSimpleDBResponse += handler;

    string structName = data.GetType().Name;
    PutAttributesRequest putAttributesRequest = new PutAttributesRequest { DomainName = tableName, ItemName = structName };
    List<ReplaceableAttribute> attributesOne = putAttributesRequest.Attribute;

    Type type = data.GetType();
    System.Reflection.FieldInfo[] fields = type.GetFields();
    foreach (System.Reflection.FieldInfo field in fields)
    {
        string valueString = FieldToString(field, data);
        attributesOne.Add(new ReplaceableAttribute().WithName(field.Name).WithValue(valueString).WithReplace(true));
    }
    attributesOne.Add(new ReplaceableAttribute().WithName("TimeStamp").WithValue(DateTime.Now.Ticks.ToString()).WithReplace(true));

    m_sdb.PutAttributes(putAttributesRequest);
}
The ReadFromDB function also uses generics and reads the values from the database into the passed parameter class instance. Additionally you can pass an Action delegate, that is called after the data is received. Remember that the database calls are asynchronous, so if you for example need to refresh your GUI after the new values are received, you can use this delegate.
The select request retrieves the SimpleDB item with the key value (itemName) of the parameter class name. The request handler then iterates through the retrieved attributes and using reflections sets the values into the fields of the parameter class instance.
/// <summary>
/// Reads values from a SimpleDB and writes it into a class instance.
/// </summary>
/// <typeparam name="T">Serializable Class that is read from the database</typeparam>
/// <param name="data">Class instance where the database values are written into</param>
/// <param name="tableName">Name of domain in SimpleDB</param>
/// <param name="readFinished">A delegate that is called when reading the values has finished. Set null if not needed.</param>
public void ReadFromDB<T>(T data, string tableName, Action readFinished)
{
    // create response handler and delegate
    SimpleDBResponseEventHandler<object, ResponseEventArgs> handler = null;

    handler = delegate(object sender, ResponseEventArgs args)
    {
        //Unhook from event.
        m_sdb.OnSimpleDBResponse -= handler;
        SelectResponse response = args.Response as SelectResponse;

        if (null != response)
        {
            Type type = data.GetType();
            System.Reflection.FieldInfo[] fields = type.GetFields();

            SelectResult selectResult = response.SelectResult;
            if (null != selectResult)
            {
                foreach (Item item in selectResult.Item)
                {
                    // actually should just be one item
                    foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attribute)
                    {
                        var field = fields.FirstOrDefault(f => f.Name == attribute.Name);
                        if (field != null)
                        {
                            StringToFieldValue(field, data, attribute.Value);
                        }
                    }
                }
            }
            if (readFinished != null)
            {
                readFinished.Invoke();
            }
        }
    };
    m_sdb.OnSimpleDBResponse += handler;

    string structName = data.GetType().Name;
    // create request
    string sql = "SELECT * FROM " + tableName + " WHERE itemName()='" + structName + "'";
    m_sdb.Select(new SelectRequest { SelectExpression = sql, ConsistentRead = true });
}
SimpleDB can only store string values, therefore the parameter class field have to be converted to a string. This is done by the FieldToString function. The current implementation explicitly only supports float, int and Vector2. You can add more types if you need them here.
/// <summary>
/// Using reflection to get a value string of a class field.
/// </summary>
/// <param name="field">The field of the class to get the value from</param>
/// <param name="data">Class instance with values</param>
/// <returns></returns>
public String FieldToString(System.Reflection.FieldInfo field, object data)
{
    string valueString;
    if (field.FieldType == typeof(float))
    {
        valueString = ((float)field.GetValue(data)).ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
    }
    else if (field.FieldType == typeof(Vector2))
    {
        Vector2 vec = (Vector2)field.GetValue(data);

        System.Globalization.NumberFormatInfo nfi
                = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
        valueString = vec.X.ToString(nfi) + ", "
                    + vec.Y.ToString(nfi);
    }
    else
    {
        // just use the default ToString for all other field types
        // extend it if you need special handling like the types before
        valueString = field.GetValue(data).ToString();
    }
    return valueString;
}
The complement function to the above takes a string and writes the value into the field of the passed instance. Again only float, int and Vector2 is supported here.
/// <summary>
/// Using reflection to set a class field from a value string.
/// Currently only float, Vector2 and int is supported.
/// Extend for you needs.
/// </summary>
/// <param name="field">The field of the class for which the value will be set.</param>
/// <param name="data">Class instance where the value will be stored into</param>
/// <param name="valueString">A string containing a value for the field</param>
public void StringToFieldValue(System.Reflection.FieldInfo field, object data, string valueString)
{
    if (field.FieldType == typeof(float))
    {
        field.SetValue(data, float.Parse(valueString, System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
    }
    else if (field.FieldType == typeof(Vector2))
    {
        System.Globalization.NumberFormatInfo nfi
            = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
        String[] values = valueString.Split(',');
        Vector2 point = new Vector2();
        try
        {
            point.X = float.Parse(values[0], nfi);
            point.Y = float.Parse(values[1], nfi);
        }
        catch
        {
            // oh no
        }
        field.SetValue(data, point);
    }
    if (field.FieldType == typeof(int))
    {
        field.SetValue(data, Int32.Parse(valueString, System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
    }
    else
    {
        // unsupported type
    }
}
The Windows DB Parameter Editor uses the above class to read and write to and from the database. I will not go much into the details of the editor source code here. Worth mentioning is the dynamic creation of the parameter GUI. Therefore exists a base class ParameterControl, which is derived from UserControl. This base class also has a static function CreateParameterControl, which serves as a factory for the actual derived parameter controls. According to the type of the passed FieldInfo different controls can be generated. Anyhow, in this example I only implemented the TextBoxParameterControl, where you enter the values into a simple text box. Possible extensions could for example be a slider control for floats,  a checkbox control for a bool parameter or a color picker control for a color field. Inside the main form (Form1.cs) the controls are then created like this:
Type type = classData.GetType();
System.Reflection.FieldInfo[] fields = type.GetFields();
int offset = 15;
foreach (System.Reflection.FieldInfo field in fields)
{
    ParameterControl control = ParameterControl.CreateParameterControl(field, classData);
    control.Top = offset;
    control.Left = 5;
    offset += control.Height;
    this.parameterGroupBox.Controls.Add(control);
}
Using the ParameterProcessor class you can finally integrate the database access into your application. Just reread the parameter class values at your convenience. You could add an update button, or reread every time you restart a level or even poll at a certain time interval.

Download and Conclusion

The complete source code of the DB Parameter Editor can be downloaded from my homepage:
DBParameterEditor.zip
It is developed using Microsoft Visual Studio 2010 Express, but it should not be a problem to use it in MonoDevelop. I also included the AWSSDKWP7 (SDK for SimpleDB) as Windows binary, so that you can directly start testing.

In this post I presented an easy way to tweak app parameters from a PC using a cloud database. This scenario can be extended in multiple directions. For example you could extend the Editor to become a more flexible game level editor. By that your game and level designers can modify levels on the running application. You could also extend it to be usable with a release version of a game. By that you can make game play adjustments without the need of an update.

Thursday, March 15, 2012

Save and Read an Encrypted Data File

Your mobile game or app most probably has some data you want to store locally on the phone. Also there might be good reason why you don't want this saved data to be easily accessible and changeable from outside of your app. For the iPhone for example you can simply use iExplorer to read and change any files of an app - no jailbreak needed. If you develop a game you might want to avoid that a user can easily cheat points and achievements. If your app has in-app purchasable items you might even get financial loss if the user can easily cheat.
Luckily the .Net Framework comes with a lot of classes that make file encryption easy to implement. If you want to secure your data from manipulation you generally have two ways of achieving it:
  • Leave the data readable but add an authentication hash key. 
  • Encrypt the complete data file.
The first method is for example used by RESTful web services. In my previous post I talked about Amazon SimpleDB which uses a hash key for authentication of the database requests. Take a look inside the library source code if you are interested in a working cross-platform implementation example.
The second method encrypts the complete data file. People sneaking into your data files will not be even able to read it and might not be tempted to try to alter the values.
In this post I will give an example for the second method. I will give an implementation that works well with Windows Phone, MonoTouch and Mono for Android and can be easily added to any app.

Application Flow
In this implementation the data that should be stored is located inside a struct. Using XML serialization the struct is converted to XML which is then encrypted and finally stored as an isolated storage file.
struct -> XmlSerializer -> XmlWriter -> CryptoStream -> IsolatedStorageFileStream
When reading the encrypted file the flow is the other way round:
IsolatedStorageFileStream -> CryptoStream -> XmlSerializer -> struct

Implementation
Start by adding the following using statements to your source file:
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Security;
using System.Security.Cryptography;
using System.IO.IsolatedStorage;
In my example I use the following data structure, which is some simple game data.
public struct SaveGameStruct
{
    public string Name;
    public int HiScore;
    public int NumOfCoins;
    public DateTime Date;
    public string Guid;
}
Next store a password and a salt as member variable string:
string m_password = "7Sj)fjfdHf734Jjd";
string m_salt = "hHeh=j84";
The password is not the actual password that is used to encode your data file. Instead it is used to create a key that then is used for encryption. The key generator uses pseudo random numbers, hence the salt. Don't forget to change the password and salt values in your source code.
The function to save an encrypted file now looks like this:
public void DoSaveGame()
{
    //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator
    //Salt must be at least 8 bytes long
    //Use an iteration count of at least 1000
    Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(m_password, Encoding.UTF8.GetBytes(m_salt), 1000);

    //Create AES algorithm
    AesManaged aes = new AesManaged();
    //Key derived from byte array with 32 pseudo-random key bytes
    aes.Key = rfc2898.GetBytes(32);
    //IV derived from byte array with 16 pseudo-random key bytes
    aes.IV = rfc2898.GetBytes(16);
            
    string SAVEFILENAME = "savegame.xml";

    IsolatedStorageFileStream storageStream = m_savegameStorage.OpenFile(SAVEFILENAME, FileMode.Create);
    XmlWriterSettings writerSettings =
    new XmlWriterSettings
    {
        Indent = true,
        IndentChars = "\t"
    };

    XmlSerializer serializer = new XmlSerializer(typeof(SaveGameStruct));

    CryptoStream cryptoStream = new CryptoStream(storageStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

    using (XmlWriter xmlWriter = XmlWriter.Create(cryptoStream, writerSettings))
    {
        // m_saveGame is a member variable of the type SaveGameStruct which holds the data to be saved
        serializer.Serialize(xmlWriter, m_saveGame);
    }
    cryptoStream.FlushFinalBlock();
    cryptoStream.Close();
    storageStream.Close();
}
As you can see I use AesManaged, because this is an encryption class that I found to work with WP7, MonoTouch and Mono for Windows. That is all that is necessary to store a structure inside an encrypted file. The function to read the encrypted file is similar simple:
void LoadSaveGame()
{
    //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator
    //Salt must be at least 8 bytes long
    //Use an iteration count of at least 1000
    Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(m_password, Encoding.UTF8.GetBytes(m_salt), 1000);

    //Create AES algorithm
    AesManaged aes = new AesManaged();
    //Key derived from byte array with 32 pseudo-random key bytes
    aes.Key = rfc2898.GetBytes(32);
    //IV derived from byte array with 16 pseudo-random key bytes
    aes.IV = rfc2898.GetBytes(16);

    string SAVEFILENAME = "savegame.xml";
    if (m_savegameStorage.FileExists(SAVEFILENAME))
    {
        IsolatedStorageFileStream fs = null;
        try
        {
            fs = m_savegameStorage.OpenFile(SAVEFILENAME, System.IO.FileMode.Open);
        }
        catch (IsolatedStorageException)
        {
            // The file couldn't be opened, even though it's there.
            // You can use this knowledge to display an error message
            // for the user (beyond the scope of this example).
        }

        if (fs != null)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(SaveGameStruct));
            CryptoStream cryptoStream = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read);
            m_saveGame = (SaveGameStruct)serializer.Deserialize(cryptoStream);
        }
    }
    else
    {
        // if file is not found it is probably the first time the app launched - handle it properly
    }
}
As you can see it is pretty much similar to the file save function, just the streams are the other way round.
And this already completes the code for saving and reading an encrypted data file. Please note, that I didn't handle exceptions at the stream reading and writing parts. So you still have to add a proper exception handling here.
My solution was heavily inspired (and partly copy and pasted) from this awesome blog post.
The encryption solution described here is not perfectly save against being hacked, as the password is saved in the source code and could be disassembled. If you have to encrypt critical data, like passwords, bank data or confidential company data, you should use a more save encryption method. But probably cross platform implementation will be not as easily be achieved then. You can read more about this topic and a solution for Windows Phone here.

Friday, March 9, 2012

Cross-Platform Global High-Score using Amazon SimpleDB

In this post I am going to describe my solution for a simple global high score that works with WP7, iOS and Android. I wasn't really following the development of web technologies for more than 10 years. So when looking for a global high score solution for my new game I needed to catch up with the recent technologies and then had to decide which one to use. I don't know if what I came up with is the best solution, but it is powerful yet simple and easy.
The preconditions for me for the global high score where:
  • It should be easy and fast to implement and maintain.
  • It should use standard web technologies.
  • It should be scaleable.
  • It should use the standard web http protocol.
  • It should be secure,
  • and it should be as cross platform as possible.
When it came to a standard interface using http I soon was sure that web services are the way to go. From there I decided that a RESTful (Representational state transfer) web service using XML should be most suited for .NET applications.
The next decision was whether to use my own virtual root server and setup the appropriate interface to a data base or use a cloud service. Even though there are a lot of tools available that support you to develop server side web services, in the end it looked like it would really brake the precondition of easy and fast implementation. Also scaling might get a problem if the game becomes very popular. I found that Amazon and Microsoft both offer a cloud service to a simple nosql database that already comes with a complete RESTful web service interface:
I actually first wanted to try the Microsoft Azure Service, but just when I tried to open an account, the management service was down and kept being down for the whole afternoon. Not a good omen for a cloud service, so I decided to go for Amazon SimpleDB instead.
Microsoft(!) offers a SDK for Windows Phone (though still beta) for the Amazon cloud services and of course Amazon offers SDKs for iOS and Android. But the cool part is, that the Windows Phone SDK comes with source code and can therefore be easily used with MonoTouch and Mono for Android as well! Using the SDK there is no need to write low level client side RESTful web services yourself, instead you have an easy to use interface to the Amazon SimpleDB data base.

Setting up the Amazon SDK
The source code for the AWS-SDK for Windows Phone is available from a GIT-Hub repository. So first check out the source code to a local directory. It comes with a complete set of samples for different Amazon cloud services. Inside the directory AWSWP7SDK you find the actual sdk. For WP7 you just have to include the project file AWSSDKWP7.csproj to your game solution and add the project as reference to your game.
For MonoTouch and Mono for Android you have to create a new project file for each. Just create a new MonoTouch and Mono for Android library and add all the SDK files. Then you need to make small changes in two files:
  • Amazon.Runtime\AWSCredentials.cs
  • Amazon.Runtime\ConfigurationManager.cs
In both files you should make a #if WINDOWS_PHONE ... #endif around all using statement to the System.Windows domain and sub domains. Inside the ConfigurationManager.cs you also should to this to the body of the LoadConfigFile() function, because we cannot read Windows config files with Mono. To initialize the database we therefore have to set the database key and security key in code instead of the config file, which is the the better way to do anyhow.
Now the SDK should compile with MonoTouch and Mono for Android and you can add the projects to your game solutions.

Getting started with Amazon SimpleDB
Before you can start developing you need to sign up for Amazon AWS here. There is a good setup guide from Amazon here. Unfortunately Amazon does not include SimpleDB within their web based management console. Therefore it is a good idea to setup a management software like the Scratchpad from the setup guide.

Simple Global High Score implementation
Amazon itself has an article for a global high score using the iOS and Android SDK on their web page. The sample I provide here is not very different from that. 
First you should create a domain for your game in the database using a management tool like the Scratchpad. Then start implementing by creating a SimpleDB client object:
// add at beginning of your file
// Amazon db
using Amazon;
using Amazon.SimpleDB;
using Amazon.SimpleDB.Util;
using Amazon.SimpleDB.Model;

// make this a member variable
AmazonSimpleDB m_sdb;

// and create the client inside the constructor
m_sdb = AWSClientFactory.CreateAmazonSimpleDBClient("your_access_key", "your_secrete_access_key");
The SimpleDB is a nosql database and therefore a table does not have a fixed structure. Anyhow, when you store and read high score data from the database you still have to decide on which attributes a high score entry inside your domain table should have. The table I use has the following attributes:

  • Category - this is always "HighScore" for high score entries
  • Player - name of the player to display in high score list
  • Score - score of the entry
  • OS - I also store "Windows Phone", "iOS" or "Android" for statistics.
Using these attributes the function to store a high score entry now looks like this:
void WriteGlobalHighscore()
{
    SimpleDBResponseEventHandler<object, ResponseEventArgs> handler = null;
    handler = delegate(object senderAmazon, ResponseEventArgs args)
    {
        //Unhook from event.
        m_sdb.OnSimpleDBResponse -= handler;
        PutAttributesResponse response = args.Response as PutAttributesResponse;
        if (null != response)
        {
           // we could do something when we get a success response from the server
        }
    };

    m_sdb.OnSimpleDBResponse += handler;
    string uniqueID = "UNIQUE_ID";
    string playerName = "PLAYER_NAME";
    string domainName = "YOUR_DOMAIN_NAME";
    string operationSystem = "Windows Phone"; // change for other os
    PutAttributesRequest putAttributesRequest = new PutAttributesRequest { DomainName = domainName, ItemName = uniqueID };
    List<ReplaceableAttribute> attributesOne = putAttributesRequest.Attribute;

    attributesOne.Add(new ReplaceableAttribute().WithName("Category").WithValue("HighScore").WithReplace(true));
    attributesOne.Add(new ReplaceableAttribute().WithName("Player").WithValue(playerName).WithReplace(true));
    // zero pad highscore string - necessary for sorting in select request because all value types are strings
    string number = AmazonSimpleDBUtil.EncodeZeroPadding(m_saveGame.HiScore, 10);
    attributesOne.Add(new ReplaceableAttribute().WithName("Score").WithValue(number).WithReplace(true));
    attributesOne.Add(new ReplaceableAttribute().WithName("OS").WithValue(operationSystem).WithReplace(true));

    m_sdb.PutAttributes(putAttributesRequest);
}
As you can see, the call to the database is asynchronous. When the operation is completed the response handler is called. Out of laziness the handler is directly declared as delegate inside this function.
The variables uniqueID, playerName and domainName have to be assigned with proper values. The uniqueID is the key value of the database table. I follow the example from Amazon and use an ID that is unique for each phone. I do this by creating a GUID the first time the game is started and store this GUID in a file. To get the player name you have to implement a dialog to let the player enter a name. The name should also be stored inside a file, so that you don't have to ask the player each time again.

You should now use the Scratchpad to verify if your entry really is stored at your SimpleDB. To do that you use the Select function and enter the Select Expression "select * from <your_domain_name>".

If you confirmed your successful storage of your high score you are ready to implement the function to request the entry from your game:
void RequestGlobalHighscore()
{
    // create response handler and delegate
    SimpleDBResponseEventHandler<object, ResponseEventArgs> handler = null;

    handler = delegate(object sender, ResponseEventArgs args)
    {
        int globlaHighscore;
        string playerName;
        string operationSystem;
        //Unhook from event.
        m_sdb.OnSimpleDBResponse -= handler;
        SelectResponse response = args.Response as SelectResponse;

        if (null != response)
        {
            SelectResult selectResult = response.SelectResult;
            if (null != selectResult)
            {
                foreach (Item item in selectResult.Item)
                {
                    // actually should just be one item
                    foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attribute)
                    {
                        switch (attribute.Name)
                        {
                            case "Score":
                                globlaHighscore = Int32.Parse(attribute.Value);
                                break;
                            case "Player":
                                playerName = attribute.Value;
                                break;
                            case "OS":
                                operationSystem = attribute.Value;
                                break;
                        }
                    }
                }
            }
            // now store the attributes to some member variables
            // ....
        }
    };
    m_sdb.OnSimpleDBResponse += handler;

    // create request
    string domainName = "YOUR_DOMAIN_NAME";
    string sql = "SELECT * FROM " + domainName + " WHERE Category='HighScore' INTERSECTION Score IS NOT null ORDER BY Score DESC LIMIT 1";
    m_sdb.Select(new SelectRequest { SelectExpression = sql, ConsistentRead = true });
}
This database request again is asynchronous and the handler, that is called when the answer arrived, is directly defined as delegate inside this function. In this example the result attributes are just stored inside local variables. Of course you need to copy them to some global structure or member variables.

This ends the post about my solution for a simple global high score implementation that works with WP7, iOS and Android. I think it is very elegant as the actual game code is shared along all platforms without any special modifications for a platform necessary. Also the SDK for the Amazon AWS cloud services is very easy to use. Following this example adding a global high score to your game can be done in just a few hours.