Adding Python
Eine dynamische Skriptsprache zum Spiel hinzuzufügen erlaubt es, schnelle Änderungen der Spielelemente durchzuführen und es erlaubt der Community, das Spiel selbst zu verändern und zu erweitern. Es gibt viele Skriptsprachen, die man wählen kann, also warum Python? Verglichen mit anderen Skriptsprachen bietet Python eine vergleichsweise einfache Syntax, dynamische Typisierung, eine riesige Standardbibliothek, eine einfache Anbindung an C/C++ über boost.python und eine Menge Dokumentation und Tutorials. Dennoch hat Python ein paar Kosten. Es ist schwer, Python in einer Sandbox auszuführen, wodurch es eine Menge Ausnutzungsmöglichkeiten eröffnen und es gibt einige Komplexitäten, mit denen Programmuierer umgehen müssen (aber das ist besser, als den Endbenutzer sich mit den Komplexitäten befassen zu lassen, die Python entfernt).
Beim Hinzufügen von Python zu Source ist die Source Engine von Grund auf eine Hauptaufgabe, um die verschiedenen Komponenten zum Laufen zu bekommen. Das GoldenEye Source Team hat jedoch den Großteil dessen bereits erledigt, sodass andere Teams Python einfach innerhalb von Minuten einbinden können.
Benötigte Dateien
Du wirst den Code aus dem SVN unter http://code.google.com/p/sourcesdkpython/source/checkout auschecken und dann in dein Mod-Verzeichnis exportieren müssen.
Erste Schritte
Sobald du das Paket oben heruntergeladen hast, entpacke die Verzeichnisse im src-Ordner in den Quellcode-Wurzelordner und die Ordner im mod-Ordner in dein Mod-Wurzelordner.
In den Server-Projekteinstellungen musst du ein paar Einstellungen hinzufügen, damit es fehlerfrei kompiliert. Du wirst dies zu Debug und release hinzufügen müssen, sofern nicht anders angegeben.
Add these to: Configure properties -> c++ -> General-> Additional Include Directories
..\..\public\python .\server\py
Fühe dies hinzu zu: Configure properties -> c++ -> Preprocesser -> Preprocesser defines
Für Release und Debug: Py_NO_ENABLE_SHARED BOOST_PYTHON_STATIC_LIB BOOST_PYTHON_SOURCE BOOST_PYTHON_NO_LIB Nur für Debug: Py_DEBUG
Füge dies hinzu zu: Configure properties -> Linker -> General -> Additional Library Directories
..\..\lib\python
Füge dies hinzu zu: Configure properties -> Linker -> Input -> Additional Dependencies
Für Release Build: boost_pythoncore.lib pythoncore.lib Für Debug Build: boost_pythoncore_d.lib pythoncore_d.lib
Python Manager
Füge diese beiden Dateien zu deiner Half-Life 2 SDK Solution hinzu (in einem neuen Verzeichnis namens python):
ge_pymodules.cpp ge_pymanager.cpp ge_pymanager.h
Diese Dateien definieren die Python Manager Klasse (PMC - "Python Manager Class"). Die PMC erlaubt es dir, verschiedene Instanzen von Python für verschiedene Aufgaben laufen zu haben. Jede Instanz hat ihre eigenen Verzeichnisse, welche die globalen und lokalen Defines beinhalten, sodass 2 Instanzen sich nicht gegenseitig behindern können. PMC lädt außerdem das Setup-Skript, welches Python IO auf die Konsole umleitet und das Python-Wurzelverzeichnis einstellt, damit es weiß, woher Python-Dateien geladen werden sollen. Die PMC bindet außerdem einen Befehl ein, welches dir das Ausführen von Python-Code in der globalen Python-Instanz ermöglicht. (Unterstützung für andere Instanzen kommt bald.)
Python initialisieren
Wenn das erledigt ist, wirst du deinen Code so anpassen müssen, dass er die Init- und Shutdown-Funktionen aufruft. Füge dies in DLLInit in gameinterface.cpp vor dem return true hinzu (um Zeile 550):
PythonInit();
Und füge in der DLLShutdown-Funktion dies hinzu:
PythonShutdown();
Füge außerdem dies nach den defines am Anfang hinzu:
extern void PythonInit();
extern void PythonShutdown();
Das Letzte, was getan werden muss, ist, InitHandles aufzurufen (GoldenEye Source macht dies in den GameRules, aber es kann ebenso an jeder anderen Stelle gemacht werden). Füge dies zu deinem GameRule-Konstruktor hinzu:
#ifndef CLIENT_DLL
PythonInitHandles();
#endif
Und das zum destruktor:
#ifndef CLIENT_DLL
PythonShutdownHandles();
#endif
Und wir müssen es am anfang der Datei externalisieren:
extern void PythonInitHandles();
extern void PythonShutdownHandles();
Deine eigene Python-Instanz erstellen
Nun kommt der spaßige Teil, die eigene Python-Instanz zu erstellen. Du wirst deine eigene Klasse erstellen müssen, die von PyHandle erbt und Funktionalität für Init und Shutdown bereitstellt.
Init
Init wird aufgerufen nachdem Python initialisiert wurde und liefert somit die beste Stelle zum Laden deiner Skripte.
void MyPyHandle::Init()
{
ExecFile("test.py");
}
Shutdown
Shutdown verarbeitet das Abschalten des Python-Handles und sollte Dinge machen, wie Shutdown-Funktionen der eigenen Skripte aufzurufen.
Nützliche Funktionen
Das Python-Handle ermöglicht es dir, Code in Form von C++-Strings durch Aufruf von Exec auszuführen und erhält außerdem das Dictionary, um andere Python-Funktionen zu verwenden.
Die Instanz erzeugen
Am Anfang deiner neuen PyHandle-Klasse musst du eine Funktion definieren, die einen globalen Zugriff auf die Python-Instanz ermöglicht. Füge ungefähr sowas hinzu:
MyPyHandle* g_PyHandle = NULL;
MyPyHandle *GetPyHandle()
{
return PyHandle ;
}
Und füge einen Aufruf zum setzen der Instanz im Konstruktor deines PyHandles hinzu, wenn eine neue erstellt wird:
MyPyHandle::MyPyHandle() : PyHandle()
{
g_PyHandle = this;
Register();
}
Und erstelle letztendlich in deinem GameRules-Konstruktor einen neuen PyHandle:
CGameRules::CGameRules()
{
// anderes Zeug hier
// Starte unseren Python-Handle
if (!GetPyHandle())
new MyPyHandle();
}
C++ Klassen/Funktionen Python bekannt machen
Das ist relativ einfach und eine ausführliche anleitung kann auf der boost.python Website gefunden werden. Wenn du dein Modul definiert hast, muss du es zu ge_pymodule.cpp ungefähr so hinzufügen:
void RegisterPythonModules()
{
REG( HLUtil ); // Das ist bereits da
REG( MyModule ); // Das ist dein neues Modul (ändere den Namen, damit es dem deines Moduls entspricht)
}
Ein paar Schnittstellen des Source SDK wurden bereits zur Verfügung gestellt (du kannst sie natürlich auch erweitern), du musst sie also nur noch zu deinem Projekt hinzufügen und sie dann mit der Python-Modulfunktion registrieren.
Bemerkungen zur Kompilierung von Python Notes on von Grund auf
bp-Namespace-Fehler
Wenn du einen Fehler in ge_pymodules.cpp oder woanders erhälst, der sich auf ein nicht vorhandenes Namespace bezieht, gehe sicher, dass du boost/python.hpp einbindest und das Namespace definierst, wie in ge_pymanager.cpp:
namespace bp = boost::python;
Linker-Probleme mit Visual C++ 2010
Kompiliere die Boost-Python-Bibliothek erneut anhand der Solution in src\utils\python\boost, wobei du vielleicht den ausgabepfad anpassen musst, damit er der existierenden boost_pythoncore(_d).lib-Datei entspricht.