TSE Python module
by Prairie Games · in Torque Game Engine Advanced · 05/10/2006 (1:42 am) · 4 replies
I'm in the process of refining our Python support. Below are the basics for getting TSE into a python module, getting/setting fields, and calling script methods. It's quite short and sweet.
I'll soon add exporting Python functions to the console system and evaluating generated TorqueScript from the Python side. That's pretty much the commonly used functionality from our previous system.
I've also been playing around with Stackless Python. It's pretty darn neat.
-Josh Ritter
Prairie Games, Inc
I'll soon add exporting Python functions to the console system and evaluating generated TorqueScript from the Python side. That's pretty much the commonly used functionality from our previous system.
I've also been playing around with Stackless Python. It's pretty darn neat.
-Josh Ritter
Prairie Games, Inc
//Python Support
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "platformWin32/platformWin32.h"
#include "console/simBase.h"
#include "console/compiler.h"
#include "console/consoleInternal.h"
int TSE_Initialize();
int TSE_Tick();
int TSE_Shutdown();
extern "C" {
#ifdef _DEBUG
#undef _DEBUG
#include <Python.h>
#define _DEBUG
#else
#include <Python.h>
#endif
static PyObject* gTSEException = NULL;
//TSEObject
typedef struct {
PyObject_HEAD
SimObject* simObject;
} PyTSEObject;
//PyTSEObject Allocator (used when wrapping/finding an existing TSEObject)
static PyObject* PyTSEObject_new(PyTypeObject *typeobject,PyObject *pargs, PyObject *kwargs)
{
PyObject* arg = NULL;
SimObject* simObject = NULL;
if (PyTuple_Size(pargs)!=1)
{
PyErr_SetString(gTSEException, "TSEObject(name|id)");
return NULL;
}
arg = PyTuple_GetItem(pargs,0);
if (PyString_Check(arg))
{
simObject = Sim::findObject(PyString_AsString(arg));
if (!simObject)
{
PyErr_Format(gTSEException, "NEW: Unable to find a TSEObject with name '%s'",PyString_AsString(arg));
return NULL;
}
}
else if (PyInt_Check(arg))
{
simObject = Sim::findObject(PyInt_AsLong(arg));
if (!simObject)
{
PyErr_Format(gTSEException, "NEW: Unable to find a TSEObject with id: %i",PyInt_AsLong(arg));
return NULL;
}
}
else
{
PyErr_SetString(gTSEException, "TSEObject(name|id)");
return NULL;
}
PyTSEObject* nobj = PyObject_New(PyTSEObject, typeobject); //new reference
nobj->simObject = simObject;
Py_INCREF(nobj);
return (PyObject*)nobj;
}
//PyTSEObject Field Accessors
//Python to TSE function calling
static StringTableEntry gFunctionString;
static Namespace* gFunctionNameSpace;
static Namespace::Entry *gFunctionEntry;
static PyObject* PyTSEObject_call(PyTSEObject *self, PyObject *args, PyObject *kw)
{
const char* argv[32];
S32 argc = 2;
argv[0]=gFunctionString;
argv[1]=self->simObject->getIdString();
for (int i =0;i<PyTuple_Size(args) && i<32;i++)
{
if (gFunctionEntry->mMaxArgs>0 && argc>gFunctionEntry->mMaxArgs)
{
PyErr_Format(gTSEException, "TSEObject of class %s function %s max arguments exceeded",self->simObject->getClassName(),gFunctionString);
return NULL;
}
PyObject* o = PyTuple_GetItem(args,i);
if (PyString_Check(o))
argv[i+2]=PyString_AsString(o);
else
argv[i+2]=PyString_AsString(PyObject_Str(o));
}
if (argc<gFunctionEntry->mMinArgs)
{
PyErr_Format(gTSEException, "TSEObject of class %s function %s not enough arguments",self->simObject->getClassName(),gFunctionString);
return NULL;
}
SimObject *save = gEvalState.thisObject;
gEvalState.thisObject = self->simObject;
const char *ret = gFunctionEntry->execute(argc, argv, &gEvalState);
gEvalState.thisObject = save;
return PyString_FromString(ret);
}
#2
05/10/2006 (1:43 am)
Wow, very cool, this should be a resource
#3
This will also work in TGE 1.3 and 1.4
-JR
05/10/2006 (1:52 am)
I'll see about making a resource. This will also work in TGE 1.3 and 1.4
-JR
#4
Example Usage
-JR
05/10/2006 (12:06 pm)
I added exporting functions and other goodies. Here's a blog post with some example usage:Example Usage
-JR
Torque Owner Prairie Games
Prairie Games, Inc.
static PyObject* PyTSEObject_getattr(PyTSEObject *self, char *attr) { StringTableEntry fieldName = StringTable->insert(attr); const AbstractClassRep::Field* field = self->simObject->getClassRep()->findField(fieldName); if (field) { //XXX: Handle Arrays return PyString_FromString(self->simObject->getDataField(fieldName,NULL)); } //not a field, could be a function gFunctionNameSpace=self->simObject->getClassRep()->getNameSpace(); AssertISV(gFunctionNameSpace,"SimObject with no namespace"); gFunctionString = fieldName; gFunctionEntry = gFunctionNameSpace->lookup(gFunctionString); if (!gFunctionEntry) { PyErr_Format(gTSEException, "TSEObject of class %s has no field or function %s",self->simObject->getClassName(),gFunctionString); return NULL; } //XXX: increase ref count on self here? return (PyObject *)self; } static int PyTSEObject_setattr(PyTSEObject* self, char* attr, PyObject* value) { StringTableEntry fieldName = StringTable->insert(attr); const AbstractClassRep::Field* field = self->simObject->getClassRep()->findField(fieldName); if (!field) { PyErr_Format(gTSEException, "TSEObject of class %s has no field %s",self->simObject->getClassName(),fieldName); return NULL; } if (PyString_Check(value)) { self->simObject->setDataField(fieldName,NULL,PyString_AsString(value)); return 0; } self->simObject->setDataField(fieldName,NULL,PyString_AsString(PyObject_Str(value))); return 0; } static PyTypeObject PyTSEObject_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "pytse.TSEObject", /*tp_name*/ sizeof(PyTSEObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) PyTSEObject_getattr, /*tp_getattr*/ (setattrfunc) PyTSEObject_setattr, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ (ternaryfunc) PyTSEObject_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "TSEObject", /* tp_doc */ }; //pytse Module static PyObject* pytse_init(PyObject* self,PyObject* args) { TSE_Initialize(); Py_INCREF(Py_None); return Py_None; } static PyObject* pytse_tick(PyObject* self,PyObject* args) { if (TSE_Tick()) { //still going Py_INCREF(Py_True); return Py_True; } Py_INCREF(Py_False); return Py_False; } static PyObject* pytse_shutdown(PyObject* self,PyObject* args) { TSE_Shutdown(); Py_INCREF(Py_None); return Py_None; } static PyMethodDef pytse_methods[] = { {"initialize", pytse_init, METH_VARARGS, NULL}, {"tick", pytse_tick, METH_VARARGS, NULL}, {"shutdown", pytse_shutdown, METH_VARARGS, NULL}, {NULL,NULL,0,NULL} /* Sentinel */ }; extern "C" __declspec(dllexport) void initpytse(void) { //initialize the python module PyObject *m; m = Py_InitModule("pytse", pytse_methods); //initialize the TSEObject type PyTSEObject_Type.tp_new = PyTSEObject_new; if (PyType_Ready(&PyTSEObject_Type) < 0) AssertISV(0,"There was an error reading the PyTSEObject_Type"); Py_INCREF(&PyTSEObject_Type); PyModule_AddObject(m, "TSEObject", (PyObject *)&PyTSEObject_Type); gTSEException = PyErr_NewException("pytse.exception", NULL, NULL); Py_INCREF(gTSEException); PyModule_AddObject(m, "TSEException", gTSEException); } } //extern "C" //TSE extern void createFontInit(); extern void createFontShutdown(); extern bool LinkConsoleFunctions; //-------------------------------------- static int TSE_Initialize() { Vector<char *> argv; char moduleName[256]; LPSTR lpszCmdLine =GetCommandLineA(); GetModuleFileNameA(NULL, moduleName, sizeof(moduleName)); argv.push_back(moduleName); //ensure that console functions link LinkConsoleFunctions = true; for (const char* word,*ptr = lpszCmdLine; *ptr; ) { // Eat white space for (; dIsspace(*ptr) && *ptr; ptr++) ; // Pick out the next word for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++) ; // Add the word to the argument list. if (*word) { int len = ptr - word; char *arg = (char *) dMalloc(len + 1); dStrncpy(arg, word, len); arg[len] = 0; argv.push_back(arg); } } winState.appInstance = GetModuleHandle(NULL); //S32 retVal = run(argv.size(), (const char **) argv.address()); createFontInit(); S32 ret = Game->initialize(argv.size(), (const char **) argv.address()); for(U32 j = 1; j < argv.size(); j++) dFree(argv[j]); return 1; } static int TSE_Tick() { return Game->tick(); } static int TSE_Shutdown() { int ret = Game->shutdown(); createFontShutdown(); return ret; }