Erstellung einer einfachen Online High-Score-Liste für Android Spiele (1)
Da ich schon einmal die Fragestellung zur einfachen Verwaltung von Online High-Score-Listen gesehen habe, und nun auch direkt von einem freundlichen Blog-Leser dazu gefragt worden bin, habe ich mich entschlossen die Thematik mal etwas zu beleuchten.
In einer kleinen Serie möchte ich vorstellen, wie ich an das Thema der Online High-Score-Liste heran gegangen bin und wie meine Lösung aussieht. Es gibt bestimmt bessere oder professionellere Lösungen, aber meine Lösung hat sich als sehr flexibel herausgestellt.
Die Lösung basiert auf der LevelStatsDBConnector-Klasse von Andengine (http://code.google.com/p/andengine/source/browse/src/org/anddev/andengine/util/levelstats/LevelStatsDBConnector.java).
Da der Artikel sehr lang geworden wäre, habe ich ihn in mehre logische Abschnitte aufgeteilt.
In dem Beispiel gehe ich davon aus das die Engine Andengine verwendet wird. Diese wird z.B. bei der Umwandlung von dem HTTP-Request, den SharedPreferences und den Zufallszahlen verwendet. Es sollte aber kein allzu großes Problem sein diese Methoden durch eigene zu ersetzen.
Statistiken Sammeln
Damit eine Online High-Score-Liste genutzt werden kann, müssen natürlich zunächst die relevanten Daten gesammelt werden. Bei der Punktzahl ist dies natürlich naheliegende. Aber weitere Werte können ebenfalls interessant sein und können ebenfalls erfasst werden wie z.B. durchschnittliche Dauer für einen Zug, Spieldauer, Anzahl der Interaktionen, Anzahl fehlerhafter Interaktionen etc.
Eindeutige Spieler-ID erstellen
Damit man einzelne Spieler in einer Datenbank unterscheiden kann, sollten jeder Spieler eine eindeutige ID erhalten. Diese ID sollte man lokale auf dem Gerät speichern (z.B. unter Verwendung von SharedPreferences wie in meinem Beispiel), damit sie bei jedem Spielstart gleich bleibt.
Der Einfachheit nutze ich eine INT-Zufallszahl als Spielder ID. Es gibt diverse andere Möglichkeiten, die wirklich eindeutige ID’s für ein Gerät bereit stellen. Aber da der Spieler zusätzlich einen Namen einträgt sollte die Eindeutigkeit weiterhin gegeben sein – es ist eher eine 90% Lösung. Der Konstruktor der StatsDB-Klasse liest diese ID aus oder erzeugt eine entsprechende neue. auch der Spielername wird hier bereits aus den SharedPreferences ausgelesen.
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, "-");
}
}
Die Datenbank-Struktur und was dafür notwendig ist
Damit die Daten in einer online-High-Score-Liste gespeichert werden können benötigt man einen Web-Server und eine Online-Datenbank. Mein Beispiel verwendet eine PHP & MySQL-Lösung.
Die Datenbankstruktur ist sehr einfach gehalten. Es wird eine Tabelle benötigt, die mindestens folgende Felder enthält:
Feld Typ player_id bigint name varchar(20) score int date varchar(20) // hier ist natürlich auch ein Datumsfeld oder ähnliches möglich ID bigint, autoincrement
Es können natürlich weitere Felder hinzu gefügt werden, je nach Bedarf. Felder die interessant sein könnten: Spieldauer, Status (Completed, Game over, Cancelled), Level, Schwierigkeitsgrad, Version, etc.
Der Primärschlüssel ist die ID, das Feld Player_ID sollte indiziert werden.
Hier der SQL-Code zum erstellen der Tabellenstruktur:
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`) )
Die Übertragung der Statistik-Informationen in eine Web-Datenbank
Aus dem Spiel senden wir einfach bei dem entsprechenden Event (z.B. Game Over) die für das tracking notwendigen Informationen per POST-Nachricht an eine Empfänger-Webseite. Dies passiert wie folgt:
Senden von der Android-App
Hier ist das senden der Daten beispielhaft dargestellt. Das Senden eines HTTP-Requests wird in einem separaten Thread ausgeführt, da es dauern kann,bis die Antwort vom Web-Server geliefert. In diesem Beispiel sind noch keine Sicherheits-Mechanismen eingebunden, diese werden in einem späteren Artikel behandelt.
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();
Empfangen der Daten Server-Seitig
Auf dem Empfänger-Server liegt ein PHP-Script. Dieses überprüft zunächst ob die Parameter alle vorhanden sind und ob diese gültigkeit haben, sonst nutzt ein Hacker ggf. SQL-Code Injection um die Datenbank zu kompromittieren.
Anschließend wird eine SQL-Verbindung aufgebaut und die Daten in der Datenbank gespeichert.
Es kann sich noch anbieten in dieser Phase sofort noch die aktuellen Online-High-Score-Daten auszulesen, schließlich will der Spieler ja sofort sehen, wenn er in der High-Score Liste eingetragen worden ist.
"; // 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;
}
?>
Ausblick
Im nächsten Beitrag behandle ich das Thema wie die Daten aus der Web-Datenbank ausgelessen werden können.






