ARTIKEL 4 - Compilieren auf anderen Plattformen/Compilern
( Teil I )
von HypoThermia
Dieser Artikel richtet sich an diejenigen Mod-Entwickler, die nicht mit MS
Visual C++ arbeiten können (oder wollen). Endlich müssen nicht mehr alle
an einem Project beteiligten Coder die gleiche Entwicklungsumgebung (IDE)
nutzen.
Ich setze voraus, dass du die Quake3 Sourcen + Tools hast.
Der Artikel fasst die Erfahrungen zusammen, die ich beim Erstellen der Q3Sourcen
mit dem Borland Compiler gemacht habe. Ich hoffe er hilft dir, die Tools
zum Laufen zu bringen,
die du bei der Arbeit mit den Q3Sourcen auf deinem Compiler brauchst.
Es wäre sinnvoll, als Grundlage für deine Compilerlösung, unter \quake3\source ein neues
Verzeichnis anzulegen.
Die Beispiele beruhen allesamt auf meinen Erfahrungen. Es gibt keinen Ersatz für reale Erfahrungen.
Code3Arena wird bald Compilerlösungen zum Erstellen der Q3Sourcen anbieten. Als Mod-Entwickler
kannst du dir dann die Plattform aussuchen, mit der du arbeiten willst, ohne den hier
beschriebenen Aufwand zu betreiben :).
OK, lehn dich zurück, zieh die Schuhe aus, und versuche nicht an Käse zu denken.
1.Hintergrund
Die Quake 3 Sourcen, veröffentlicht von id Software, erstellen einen Teil des Spielcodes
und erlauben es den eingefleischten Fans so, Quake 3 für die Community zu verbessern und auszubauen.
Da das Spiel bereits von Haus aus mehrere Plattformen unterstützt, muss natürlich auch der
Q3Source so plattformunabhängig wie möglich sein. Er wurde in portablem ANSI C geschrieben,
und wird zu einem Bytecode compiliert, der auf jedem System lauffähig ist, auf dem Quake3 läuft.
Mod-Entwickler können also ihre Spiel-Erweiterungen auf einer Plattform entwickeln, und sie werden
auf allen anderen laufen.
Ein Vorteil von ANSI C ist, dass es tonnenweise Programmierer mit C-Erfahrung
gibt. Der andere grosse Vorteil besteht darin, dass sich für die jeweilige
Arbeitsplattform Binaries erstellen lassen, die das Entwickeln und Debuggen
erleichtern.
2. Ziele
Wir können die Aufgabe in drei wesentliche Bereiche aufteilen:
1. den Bytecode für Quake3 erstellen
2. entsprechende Binarys für Tests und Debugging erstellen
3. deine Arbeit für andere Entwickler zugänglich machen
Wenn du anderen Entwicklern deine Portierung zur Verfügung stellen willst, sollte sie nur die für
die Portierung unbedingt nötigen Änderungen gegenüber den "originalen" Q3Sourcen enthalten. Bestenfalls
fügst du eigene Batchdateien oder Scripte bei, die allerdings diejenigen der Q3Sourcen nicht
überschreiben sollten. id Software könnte aktuellere Sourcen veröffentlichen, die deine Scripte wieder
überschreiben. Im Idealfall sollte ein anderer Coder nur die von dir gefundenen notwendigen Veränderungen
auf seine Q3Sourcen übertragen müssen.
Sieh dir "Veröffentlichen deines Projektes" im zweiten Teil dieses Artikels an, um zu
sehen wie das konkret aussehen könnte.
Für die ersten Beiden Aufgaben werden die Headerdateien deines Compilers von
grosser Bedeutung sein. Für den
Bytecode ist das wichtigste, dass sich deine Header wie ANSI C verhalten.
Dazu gleich mehr unter
"3. Erste Schritte...".
Das Erstellen von Binaries auf deiner Plattform setzt die Beherrschung der
Kunst voraus, portablen Code so zu modifizieren, dass er portabel bleibt.
Einfach ausgedrückt, jeder andere
Compiler sollte deine Q3Sourcen ohne Probleme erstellen können. Mehr dazu in "Binaries
compilieren"
im zweiten Teil des Artikels.
3. Erste Schritte mit Bytecode
Der Bytecode, der auf der Quake3 Virtual Machine (QVM) läuft, ist plattformunabhängig. Er wird
mit Hilfe der Header deines Compilers durch lcc.exe compiliert, ein Tool von id Software.
Jede der compilierten Dateien wird dann durch q3asm.exe erstellt und verlinkt.
Du wirst 3 verschiedene QVM Dateien erstellen:
qagame.qvm |
Enthält den Code für den Spielserver
und die Bot KI. |
cgame.qvm |
Verarbeitet die Ereignisse( events)
und die Bildschirmdarstellung auf Clientseite. |
ui.qvm |
Stellt die Benutzerschnittstelle und
Menüs für den Singleplayer Modus zur Verfügung. |
Um den Bytecode erstellen zu können, musst du die Headerdateien überreden, sich wie
plattformunabhängiges ANSI C zu verhalten.
4.Compilierung mit Hilfe von Scripts automatisieren
Teil der Q3Sourcen sind vier Batchdateien, die im DOS Modus laufen. Drei dieser Dateien kümmern
sich um das Erstellen der Bytecode Module qagame, cgame und ui. Sie heissen game.bat, cgame.bat
und ui.bat. Jede von ihnen ruft die vierte Batchdatei namens compile.bat auf, und übergibt ihr den
Namen der Sourcedatei, die für das jeweilige QVM Modul übersetzt werden muss.
Nach dem Compilieren muss sich das Script nur noch um das Erstellen und Verlinken der Dateien kümmern.
Das dazu aufgerufene q3asm.exe nutzt für jedes Modul eine entsprechende Datei: game.asm, cgame.asm und ui.asm.
Kopiere diese Dateien in dein "Compilerverzeichnis" unter Quake3\source. Da sie ihre Arbeit eigentlich
im Unterverzeichnisse vm erledigen, solltest du sie folgendermassen modifizieren:
Passe die Batchdatei auf die Scriptsprache deines Systems an, achte dabei darauf, dass nur das compile-Script
lcc.exe aufruft.
-
Ändere die relativen Pfade entsprechend der Sourcedateien.
-
Ändere die relativen Pfade von lcc.exe und q3asm.exe oder füge
sie der PATH Umgebungsvariable ( dem executable path) hinzu ( dokumentieren!).
-
Ändere die relativen Pfade zu ..\game, ..\cgame, und ..\ui
im compile-Script.
In jeder der .q3asm Dateien: ändere jeweils nur den Pfad zu cg_syscalls, ui_syscalls bzw g_syscalls,
damit die richtige .asm Datei aus den Q3Source Unterverzeichnissen cgame, ui bzw game benutzt wird.
Wenn du jetzt versuchst die Scripte auszuführen, sollten sie eine Fehlermeldung über nicht gefundene
Headerdateien ausspucken.
5. Deine Header verstehen
Die nachfolgenden Änderungen beschränken sich auf das compile-Script.
Zuerst müssen wir dem compile-Script sagen, wo sich die Header befinden. Versichere dich, das folgendes Argument
an lcc.exe übergeben wird:
-I<path to header files>
wobei "path to header files" ein konkreter Pfad auf deinem System ist.
Jetzt, wo deine Header bekannt sind, musst du dich mit Plattform- und Compilerspezifischen Problemen
befassen. Die meisten sollten durch die Übergabe von so etwas ähnlichem wie einem "#define xxxx" an
lcc.exe gelöst werden. Konkret machst du das durch hinzufügen des Arguments -Dxxxx bzw -Dxxxx=""
zum compile-Script.
Wenn deine Header mehr als ein Compiliermodell unterstützen, musst du sie austricksen,
um eine "pure"
ANSI C Definition aller Funktionen zu bekommen. Zum Beispiel könntest du die
Headerdatei suchen, in der die compilerspezifischen Informationen gespeichert
sind, diese Überbrücken, und stattdessen eigene
Definitionen an lcc.exe übergeben.
Ausserdem musst du evt. einige Compilerspezifische Werte definieren, um die Route durch die Headerdateien
zu kontrollieren. Prüfe, ob die Werte nicht schon im Q3Source verwendet werden, und falls doch, ob sie
Probleme verursachen.
Beispiel:
Borland C++ benutzt Header, die sowohl ausführbare Dateien als auch DLLs unter Win32, Win16 und
(nur ausführbare Dateien) unter DOS mit 6 verschiedenen Speichermodellen erstellen können.
Ich erzwang den Pfad für ausführbare Dateien unter Win32 durch die Definition von __FLAT__.Da ich den
Aufruf von Win32 Funktionen im Q3Source verhindern musste ( ich wollte ja nicht nur für diese
Plattform compilieren), definierte ich nicht WIN32, _WIN32 oder __WIN32__. Diese Variablen werden
sowohl von Borland als auch von Microsoft Tools definiert und benutzt.
Ausserdem definierte ich die Compilerspezifische Variable __BORLANDC__, die ebenfalls die Route
durch die Header beeinflusst.
Mit diesen Definitionen bekam ich Fehler von lcc.exe bezüglich _RLENTRY und ähnlichen
Konstanten, also musste ich die Headerdatei entfernen, die diese Definitionen
enthält:
<_defs.h>
(ich
erreichte das durch Definition von __DEFS_H, da der Header diese Variable
benutzt, um sich vor wiederholter Einbindung zu schützen)
Eine typische Borland Definition sieht so aus:
int _RTLENTRYF atoi(const char _FAR *__s);
Der Präprozessor macht durch Ersetzen der #defines folgendes daraus:
int atoi(const char *__s);
6. Halte den Code portabel
Vermeide Modifikationen an den Q3Sourcen, falls sie nicht absolut notwendig
sind. Versuche stattdessen, deine Modifikationen mit Hilfe der Kommandozeilen-Optionen
von lcc.exe (also dem
compile-Script) zu erreichen.
Prüfe also erst, ob ein -Dxxxx dein Problem lösen kann. Falls du doch in den Sourcen arbeiten musst,
mache deine Änderungen von einer Konstanten abhängig, die nur dein Compiler definiert. Versichere dich,
dass diese Konstante ebenfalls im compile-Script definiert ist.
Begrenze die notwendigen Änderungen auf Dateien, die sowieso Compilerspezifische
Komponenten enthalten (z.B. game\q_shared.h). Modifikationen sollten nur
bei sehr wenigen Headern nötig sein.
Für das Verändern von .c (source) Dateien gilt noch noch grössere Vorsicht.
Die unter "Erwartete Fehler"
aufgeführten Fehler, kannst du normalerweise ignorieren.
Wenn für deine Plattform bereits eine Route durch die Q3Source Header verfügbar ist, du aber einen anderen
Compiler verwendest, kannst du dir vielleicht etwas abschauen.
Dokumentiere deine Änderungen und überlasse es den Nutzern deiner Arbeit sie zu verbinden, so das sie den
Prozess nachvollziehen und evt. daraus lernen können. Bedenke: die Leute die von deiner Arbeit profitieren
sind selbst erfahrene C-Coder.
Beispiel #1:
Mit den Borland Headerdateien gab es einen Namenskonflikt bei der Funktion random(). Da die Q3Source Version
Vorrang haben sollte, fügte ich in game\q_shared.h über der Definition von random() folgenden Code ein:
#if (defined __BORLANDC__ && defined random)
#undef random
#endif |
Dies hilft auch beim Erstellen von Binarys, wo das gleiche Problem auftaucht.
Beispiel #2:
Die einzige andere Modifikation die bei mir nötig war, war in einer Borland
Header Datei. lcc.exe erzeugte einen Fehler beim Parsen einer #error Anweisung
(obwohl diese nicht ausgeführt wurde). Die Änderung bestand
darin, die Errormessage in Anführungszeichen zu setzen.
#error "Can't include both STDARG.H and VARARGS.H"
in
Zeile 20.
7. Erwartete Fehler
Trotz der Portabilität der Q3Sourcen, erzeugen die Sourcen Warnungen. Vielleicht erzeugen auch die
Headerdateien einige Warnungen. Schau sie dir genauer an, und entscheide ob Modifikationen in deinen
Headerdateien notwendig sind.
ui/ui_ingame.c:103
ui/ui_ingame.c:107
ui/ui_atoms.c:739
ui/ui_atoms.c:742 |
"Warning: Conversion of 'pointer to void'... ...das ist Compilerabhängig"
Für die QVM ist das kein Problem, für deinen Compiler könnte es schon eins sein.
|
Eventuell wirst du feststellen, dass sich die Grösse der erstellten QVMs deutlich von der der Quake3
QVMs unterscheidet. Die Ursache dafür ist wahrscheinlich die unterschiedliche Art, auf die Headerdateien
statische Daten für ihre Implementierung nutzen.
Beispiel:
Die Borland Header erzeugen eine weitere Warnung:
limits.h:31 Character constant taken as not signed
Paradoxerweise bezieht sich diese Warnung auf Borlands Methode, um bei chars zwischen signed und unsigned
zu unterscheiden. Sie kann getrost ignoriert werden.
8. Den Bytecode testen
Wende Tutorial#3 an, um einen "Slow Rocket Mod" zu erstellen. Vergiss nicht, die Änderungen rückgängig zu machen!
Versuche die Demos mit "timedemo 1" abzuspielen. Nicht als Performancetest, sondern um sicherzustellen,
dass jedes Mal die gleichen Frames dargestellt werden.
Spiel ein paar Runden gegen Bots oder zieh deine Kumpels ab. Du hast es dir verdient!
9.Soweit so gut, und jetzt ?
Die compilierten QVMs hast du schon. Als nächstes solltest du versuchen den Code zu Binarys zu compilieren.
Das ist Thema des zweiten Teils dieses Artikels, inklusive einiger Vorschläge, wie du deine Arbeit organisieren
kannst, wenn du sie anderen zur Verfügung stellen willst.
<< zurück/back >>