Das Rad neu erfinden

2018-04-11

Für die häufigstens Probleme gibt es akzeptierte Lösungen oder fertige Tools. Dazu gehört das Packen von Sprites oder Texturen in eine große Textur oder Sprite-Sheet. Kein Grund also wieder einmal das Rad neu zu erfinden. Ausser man hält es wie Richard Feynman:

What I cannot create, I do not understand.

In meinem Fall hatte ich etwas überspezifische Anforderungen, die keines der verfügbaren Tools für mich bot. Die Informationen der Subtexturen sollten im JSON-Format vorliegen und die Möglichkeit diese über numerische IDs zu finden. FSM sei Dank, hat Nicolas Perdu genug Vorarbeit geleistet um meine Wunsch-Features einfach selber zu implementieren ohne alles drum herum neu zu machen. (Mal wieder ein Paradadebeispiel für die Vorteile von FOSS).

Schwuppdiwupp, und der Sprite Sheet Packer ist fertig! Das JSON-Format ist sehr einfach und sieht so aus:

{"TextureAtlas":
{
    "imagePath": "example",
    "SubTexture":
    [
        { "name": "img17", "x": 235, "y": 149,
            "width": 58, "height": 59,
            "rotation": 0, "id": 2822025959 },
        { "name": "img18", "x": 470, "y": 0,
            "width": 29, "height": 76,
            "rotation": 0, "id": 948678518 }
    ]
}}

Die numerischen IDs sind einfache CRC32-Summen der Dateinamen. Das ist einfach genug, ich erwarte da sobald keine Kollisionen. Man kann sich auch gleich eine C++-Header mit allen IDs generieren lassen. Einfacher geht es wirklich nicht. Durch die großartige Vorarbeit von Jukka Jylänk ist auch auf der algorithmischen Seite alles erschlagen. Das Packergebnis sieht gut aus und ich muss kein weiteres Gehirnschmalz in dieses Problem investieren.

Example sprite sheet packing

Tags: GameDev, C++

Krypto für Spielstände

2014-09-17

Es ist immer wieder schön, wenn ich mich mit Crypto beschäftigen kann. Nicht weil ich ein Experte auf diesem Gebiet bin, sondern weil es mich einfach unabhängig von meiner Qualifikation einfach sehr interessiert. Ich bin kein Kryptograph. Bei dieser Gelegenheit ging es darum den Spielstand (Savegame) eines Spiels „sicher“ zu speichern. Dazu gehört als erstes definiert, was man als einen sicheren Spielstand versteht.

  • Geheim – der Inhalt des Spielstandes darf nicht lesbar sein
  • Korrekt – der Inhalt des Spielstandes darf nicht manipulierbar sein

In den den meisten Fällen muss ein Spielstand nicht geheim sein. Es gibt keine Vertraulichkeit darüber, welche Gegenstände im Inventar sind, wie hoch der Highscore ist oder wieviel Lebensenergie der Spieler gerade hat. Darüber hinaus ist ein von Menschen lesbarer Spielstand zu bevorzugen. Es erleichtert bei der Entwicklung einiges wie Fehlersuche oder das Testen. Aber korrekt muss ein Spielstand immer sein! Einfach mal mehr Gold oder Lebensenergie in einen Spielstand zu schreiben ist in aller Regel nicht schwer. In der Regel schadet es keinem, wenn Spieler sich auf diesem Wege das Spiel leichter machen.

Zusammen gefasst wird klar, dass der Spielstand niemals auf Seite des Spielers gespeichert werden sollte, wenn man einen sicheren Spielstand benötigt. Dazu zählen besonders Daten für Mehrspielerspiele oder High Scores für Ranglisten. Daraus folgt, dass „alles in die Cloud“ oder zumindest auf einen Server gespeichert werden muss. Das ist einfach nicht immer praktikabel und Always Online ist für viele Spiele einfach nicht sinnvoll.

Hohe Hürden

Was kann man also tun, wenn man akzeptiert hat, dass man eigentlich nicht gewinnen kann? Man muss es einem mogelnden Spieler so schwer wie möglich zu machen. Der naive Ansatz wäre einfach Binärdaten zu schreiben, da die meisten Menschen diese nicht lesen können. Allerdings hat das schon vor 20 Jahren keine 13-Jährige mit Hexeditor aufgehalten. Zusätzlich verliert man die Vorteile der menschenlesbaren Spielstände.

In meinem konkreten Fall habe ich mich für schnödes XML entschieden. Die De-/Serialisierung ist in aller Regel bereits erschlagen, der Aufwand ist gering. Zum Schutz vor Manipulationen erzeuge ich einen HMAC mit dem der Spielstand signiert wird. Auch das muss nicht selber implementieren, wieder Zeit gespart. Damit wird jeder Manipulationsversuch erkannt. Darüber hinaus kann man die Nachricht mit einer Nutzerkennung oder eine Geräte-ID versehen. Der Spielstand ist so auch an einen Nutzer oder ein Gerät gebunden. Einfaches Tauschen von gültigen Spielständen wird damit unterbunden. Problematisch ist natürlich, dass das Geheimnis zum Signieren im Programm vorliegt. Ein motivierter Angreifer wird diesen finden.

Ob dieses Vorgehen genügt um Ranglisten vor unmöglichen Rekorden schützen wird die Zeit zeigen. Irgendwie freue ich mich auf ein mögliches Wettrüsten. Obwohl in letzter Konsequenz nur das Speichern außerhalb der Reichweite der Spieler erfolgversprechend ist. Dieses Vorgehen hält aber auch ganz andere Fallstricke bereit.

Tags: Krypto, GameDev