Seite 1 von 1

Biete halbfertigen Browsergame-Nachbau

Verfasst: Samstag 3. Januar 2009, 19:14
von sma
Vor 16 Monaten hatte ich mal zu Übungszwecken versucht, die älteste Version des Browserspiels [Stellar Crisis](http://en.wikipedia.org/wiki/Stellar_Crisis), die ich finden konnte, in Python für Django zu übersetzen. Stellar Crisis wurde ursprünglich in [Pike](http://en.wikipedia.org/wiki/Pike_%28pr ... anguage%29) für den Roxen-Webserver geschrieben, der das Spiel dann wohl komplett im Hauptspeicher ohne Datenbank-Backend ausgeführt hat.

Mein Wissen über Python war noch nicht so groß, und mein Ansatz, das Pike-Programm möglichst direkt auf Django mit Datenbank-gestützten Modellen abzubilden, hat dem schon recht unübersichtlichen Programm dann den Rest gegeben. Ich glaube, ich habe die 3785 Zeilen Pike relativ komplett in ~3000 Zeilen Python-Code übertragen und man kann sogar die ersten Screens sehen, doch spätestens bei der über 1000-Zeilen langen Funktion, die Kämpfe berechnet, glaube ich nicht, dass das alles so korrekt ist. Es geht jedenfalls nicht...

Übrigens: Spätere Versionen von Stellar Crisis haben über 11.000 Zeilen Code - immer noch in einer einzigen Datei mit Funktionen, die länger sind, als so manch anderes Softwareprojekt insgesamt. Irgendwann Ende der 90er wurde das dann wohl so unwartbar, das man versucht hat, dass ganze in PHP neu zu bauen. Keine Ahnung, wie da der Stand ist. Ich wollte daher gerne die Ur-Version, mit der alles anfing, haben.

Es wurmt mich nun, da ein weiteres angefangenes, nie beendetes Projekt zu haben und bitte an dieser Stelle um Rat und vielleicht Tat, etwas daraus zu machen.

Der Autor von SC (vielleicht bedingt durch das, was Pike kann) war offenbar in Dictionaries verliebt, denn diese sind neben Listen die einzige Datenstruktur, die er benutzt hat. Dadurch ist das Programm gespickt mit Ausdrücken wie

Code: Alles auswählen

    d[series][game]["p"][owner]["dip"][d[series][game]["s"][loc]["owner"]]
    
Die globale Variable "d" (steht vielleicht für "data") bildet einen Namen auf eine Spielvariante ("series") und in dieser einen weiteren Namen auf das Spiel ab. Unter "p" findet man die Teilnehmer ("participants" rate ich), "owner" ist dann der Name eines Teilnehmers und der hat ein Attribut "dip", was für das diplomatischen Verhältnis zu anderen Spielern steht. Da wird nun nachgeschaut wie "owner" zu dem Besitzer eines Planeten ("s" = "star" oder "system") ist, der an Position "loc" auf der Karte ist.

In Python mit Django könnte das so aussehen:

Code: Alles auswählen

    game = Series.objects.get(series).games.get(game)
    participant = game.participants.get(owner)
    star = game.stars.get(loc)
    participant.diplomatic_states.filter(other=star.owner)
    
Oder mit mehr "Zucker":

Code: Alles auswählen

    game = get_game(series, game)
    participant = game.participant_named(owner)
    participant.is_allied_with(game.star_at(loc).owner)
    
Ich habe jetzt nicht versucht, den Code entsprechend umzuschreiben, sondern es so direkt wie möglich abzubilden, was ähnlich furchtbar ist und aus jetziger Sicht IMHO ein Fehler war.

Bei Django muss man zudem explizit veränderte Modelle speichern. Pike hat dieses Problem nicht. Es wird einfach das Dictionary von Dictionaries von Dictionaries von ... verändert und ich glaube, ein Hintergrundprozess schreibt dieses in Intervallen auf Festplatte, damit nach einem Crash nicht alles weg ist. Möglicherweise ist der Roxen-Server auch single-threaded, denn andernfalls käme es zu endlos vielen Racing-Conditions, die jetzt natürlich mein Python-Code alle hat.

Ich habe versucht, an den richtigen Stellen explizit zu speichern, habe aber bestimmt viele übersehen und das scheint mir der Grund zu sein, warum das ganze nicht richtig funktioniert - abgesehen von Flüchtigkeitsfehlern beim Umschreiben.

Statt Django wäre es vielleicht besser, den mit Python ausgelieferten WSGI-Referenzserver zu benutzen, um so keine externen Abhängigkeiten zu haben und auch einen single-threaded Server, der permanent läuft und so einfach alle Daten im Hauptspeicher halten kann. Dann setzt man z.B. `shelve` zur Datenspeicherung ein, was wohl dem Ansatz von SC am nächsten kommt.

Alternativ nutzt man [Storm](https://storm.canonical.com/) oder [SQLAlchemy](http://www.sqlalchemy.org/), die zumindest automatisch die in einer Session veränderten Objekte sammeln, sodass man am Ende eines Requests nur noch `commit` aufrufen muss. Zur Implementierung des Webservers könnte man auch [Werkzeug](http://werkzeug.pocoo.org/) in Betracht ziehen.

Oder man rettet den Django-Ansatz vielleicht dadurch, dass man das einen Automatismus entwirft, veränderte Objekte zu registrieren und zu speichern. Allerdings erzeugt Django mit jeder Query immer neue Objekte (hat keine Identity-Map) und macht es hier nochmals komplizierter.

Ich fände es allerdings auch reizvoll, einen kleinen eigenen ORM auf Pythons sqlite-Support aufzusetzen, um so auch hier keine Abhängigkeiten zu haben und so z.B. gleich Python 3 als Zielplattform ins Auge zu fassen.

Wie auch immer, falls jemand Spass daran hat, Fehler in komplexem Code zu suchen... ich hätte da 3000 Zeilen pures Vergnügen im Angebot :)

Stefan