Adding Python:de

From Valve Developer Community
Revision as of 08:05, 25 April 2013 by Sacaldur (talk | contribs) (linked the original page)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
English

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.)


Bemerkung:
jede Python-Instanz wird von der gleichen Python-Engine ausgeführt und sollte nur innerhalb des gleichen Threads verwendet werden (kein Source-Fehler).

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();
Bemerkung:
Der Grund, weshalb dies exernalisiert wird und keine Header eingebunden werden, ist, um einem bewusst zu machen, dass der Python Code vom Source SDK Code getrennt ist.


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.

Bemerkung:
Ein Beispiel-Python-Handle ist im Paket enthalten.

Init

Init wird aufgerufen nachdem Python initialisiert wurde und liefert somit die beste Stelle zum Laden deiner Skripte.

void MyPyHandle::Init()
{
	ExecFile("test.py");
}
Bemerkung:
Das Standard-Pythonverzeichnis ist modfiles\scripts\python und kann im Python-Manager geändert werden.

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();
}
Bemerkung:
Es ist wichtig, Register() aufzurufen, da es sonst nicht funktioniert!

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.

Zu erledigen: Mehr (siehe Originale Seite)