//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== // // Purpose: // //============================================================================= #ifdef PLATFORM #define __PLATFORM PLATFORM #undef PLATFORM #endif #ifdef _DEBUG #undef _DEBUG // don't want a debug python header! #include "Python.h" // include python before any standard headers #define _DEBUG #else #include "Python.h" #endif // _DEBUG #ifdef __PLATFORM #undef PLATFORM #define PLATFORM __PLATFORM #undef __PLATFORM #endif #include #include #include #include #define _HAS_EXCEPTIONS 0 #include #include "platform.h" #include "tier1/utlmap.h" #include "datamap.h" #include "tier1/functors.h" #include "tier1/utlvector.h" #include "tier1/utlhash.h" #include "tier1/utlbuffer.h" #include "tier1/fmtstr.h" #include "tier1/convar.h" #include "mathlib/vector.h" #include "vstdlib/random.h" #include "vscript/ivscript.h" // #include // DEBUG: memory overwrites #include "memdbgon.h" /* Implementation of the IVSCRIPT interface for Python 2.5 ------------------------------------------------------- The IVSCRIPT interface bridges the Python virtual machine (python25.dll), and a set of server-side systems and objects, such as CBaseEntity. Although the ivscript interface is not changed for python, the python implementation works quite differently from the Squirrel implementation from a programming standpoint. Squirrel ivscript associates instance data with an entity's code file, and each entity has its own copy of the code: ----------------------------------------------------- The current implementation of ivscript that interfaces with the Squirrel virtual machine assumes that every map that spawns may cause one or more script files to load, compile and execute. In addition, every entity spawned within a map may also cause one or more script files to load, compile and execute. An entity's script code and data is fully isolated from other entities. Within an entity's code file, script variables such as 'self' and 'player'are auto- defined, and they refer to the entity that invokes the code, the player etc. This scheme creates an execution model with per-entity scope, and a global scope common to all entities. It also means that every entity keeps a unique, compiled copy of its code in memory. When a map is re/loaded, all code is flushed, and the Squirrel interpreter shuts down and restarts. No code or data persists beyond a level load or restart, and all code associated with a new map must be newly loaded and compiled. Python ivscript associates instance data with the entity itself, and all entities share the same compiled code: ------------------------------------------------------- Python has a global scope, which contains a single compiled instance of each loaded code module. When a code module is loaded, it is compiled and executed exactly once. (Note that 'execution' of a loaded module creates module-global variables, and creates functions and classes). Any variables defined within a module are global to functions/classes defined in the module, but not to other modules. In other words, code defined within the module shares only the module's data scope. However, code can call into another module, and must simply refer to any 'foreign' module data through explicit use of the module name. Within a module, a class definition also creates a scope, as do method definitions with classes. Since all module-defined data is shared by all code within the module, we must associate instance data with the entity instance, instead of the module (as is done in the Squirrel implementation). This means that to preserve data associated with an entity, we pass the entity pointer as 'self' to all module functions that need to access instance data as the first parameter. In python, we have very simple ways to get and set data on an instance, including defining new data at runtime. Unlike Squirrel ivscript, the Python interpreter is not shut down when a new map is loaded or when the game is restarted. This means that more compiled code is shared between maps as new modules are imported. (NOTE: It also means that by the end of a game, all unique script code modules could be simultaneously loaded in memory. Compiled code is relatively small for full-featured modules- perhaps 10-20k. 20 modules would be a lot of script code for a game - so perhaps 200-400k total. To manage large amounts of in-memory code, we may need a mechanism for manually discarding modules from memory that we know to be unique to a particulary map or entity instance.) Hammer entity interface: ----------------------- Rule 1: A python module should contain functionality corresponding to a particular type of entity. For instance, you might have a CameraMan.py module, a SpaceMarine.py module and a Medic.py module, each assigned as the ActivateScript for a camera, spacemarine and medic entity types you've placed in hammer. The 'ActivateScript' field of an entity may contain only one python module name, such as 'SpaceMarine.py'. This module will be loaded and run the first time an entity is spawned with this module listed as its ActivateScript. This module may contain definitions of various Valve callbacks. Each callback will be passed the entity instance as the first parameter, such as: ScriptThinkFunction( entity ) - called every tenth of a second during entity think OnDeath( entity ) - called when entity dies etc. These are identical to the regular vscript callbacks (see wiki), except that under the python system, they all take 'entity' as the first parameter. */ static int GetNewProxyId( void ); static void SetProxyBinding( int proxyId, ScriptFunctionBinding_t *pBinding ); static PyCFunction GetProxyFunction( int proxyId ); static ScriptFunctionBinding_t *GetProxyBinding( int proxyId ); static void InitProxyTable( void ); inline PyObject *CreatePyVector( Vector *pVector ); inline bool IsPyVector( PyObject *pobj ); inline bool VMInitFinalized( void ); //------------------------------------------------------------------------------ // python interpreter singleton //------------------------------------------------------------------------------ static void *g_pVm = NULL; // object tags, largely used for memory validity checking on pointer casts #define TYPETAG_INSTANCE 71717171 #define TYPETAG_SCOPE 81818181 #define TYPETAG_VECTOR 91919191 // NOTE: increase MAX_VALVE_FUNCTIONS_EXPORTED and the DPX(n) & SPX(n) entries at the end of this file if more than 260 // functions/methods are exported from valve dlls to vscript. #define MAX_VALVE_FUNCTIONS_EXPORTED 260 // NOTE: increase this if we export more than N classes to python. #define MAX_VALVE_CLASSES_EXPORTED 260 //------------------------------------------------------------------------------ // Module structs //------------------------------------------------------------------------------ // a single python method definition followed by a null sentinel typedef struct { PyMethodDef defs[2]; } pymethoddef_t; // valve server object instance data typedef struct { void *pInstance; // instance of the valve object ScriptClassDesc_t *pClassDesc; // binding descriptors for methods PyObject *pPyName; // name } InstanceContext_t; // python object with data for valve server object instances typedef struct { PyObject_HEAD; /* instance data */ PyObject *pDict; // mapped to __dict__ of python instance object int typeTag; InstanceContext_t instanceContext; } scriptClassInstance_t; typedef struct { PyCFunction pfn; ScriptFunctionBinding_t *pBinding; // binding to valve function } proxybinding_t; // global array mapping from proxy function id to function binding data static proxybinding_t g_proxies[MAX_VALVE_FUNCTIONS_EXPORTED]; //------------------------------------------------------------------------------ // inline debug helpers //------------------------------------------------------------------------------ // define this to always sanity check the object's ref counts // NOTE: failing this can imply a reference count bug (i.e. too many frees) // or possible memory corruption bug. #define DEBUG_PY 1 inline void AssertIsPyObject( HSCRIPT hscript, int minRefCount = 0 ) { #ifdef DEBUG_PY Assert( ((PyObject*)hscript == Py_None) || ((PyObject *)hscript)->ob_refcnt < 5000 && ((PyObject *)hscript)->ob_refcnt >= minRefCount); #endif // DEBUG_PY } // object must be a python object and an instance object inline void AssertIsInstance( HSCRIPT hscript ) { #ifdef DEBUG_PY AssertIsPyObject( hscript, 0 ); Assert ( ((scriptClassInstance_t *)hscript)->typeTag == TYPETAG_INSTANCE ); #endif // DEBUG_PY } //------------------------------------------------------------------------- // Vector class //------------------------------------------------------------------------- // python vector instance with data for valve object instance typedef struct { PyObject_HEAD; /* instance data goes here*/ int typeTag; Vector *pVector; } PyVectorInstance_t; inline void AssertIsVector( HSCRIPT hscope ) { Assert( ((PyVectorInstance_t *)hscope)->typeTag == TYPETAG_VECTOR ); } static int DEBUG_VECCOUNT = 0; // count the vector allocations vs frees (test for mem leaks) static int DEBUG_VARIANTCOUNT = 0; // count the variant allocations vs frees (test for mem leaks) static int DEBUG_FUNCCOUNT = 0; // count the function handle allocs vs frees (test for mem leaks) //------------------------------------------------------------- // called from python directly during object destruction. //------------------------------------------------------------- static void VectorRelease( PyObject *pSelf ) { AssertIsVector( (HSCRIPT)pSelf ); // free the game vector if ( ((PyVectorInstance_t *)pSelf)->pVector ) { delete ((PyVectorInstance_t *)pSelf)->pVector; DEBUG_VECCOUNT--; } // free the python object pSelf->ob_type->tp_free( pSelf ); } //------------------------------------------------------------- // called from python directly during object construction. // same as class __init__ function - init the member data for valve instance // Allocates a NEW Vector. The new Vector object will be deleted // when the python object is deleted via VectorRelease. //------------------------------------------------------------- static int VectorConstructNew( PyObject *pSelf, PyObject *pArgs, PyObject *pkwds ) { float x = 0.0; float y = 0.0; float z = 0.0; if ( pArgs ) { PyArg_ParseTuple( pArgs, "|fff", &x, &y, &z ); } Vector *pVector = new Vector(x, y, z); DEBUG_VECCOUNT++; Assert( DEBUG_VECCOUNT < 1000 ); // if this fails, we're likely leaking new vectors each frame. ((PyVectorInstance_t *)pSelf)->pVector = pVector; ((PyVectorInstance_t *)pSelf)->typeTag = TYPETAG_VECTOR; return 0; } //-------------------------------------------------------------------- // creates a new python vector object which references the given Vector. // (i.e. does NOT create a new Vector object). The referenced vector will // be deleted when the python object is deleted via VectorRelease. //-------------------------------------------------------------------- void VectorBuildCopy( PyObject *pSelf, Vector *pVector) { ((PyVectorInstance_t *)pSelf)->pVector = pVector; ((PyVectorInstance_t *)pSelf)->typeTag = TYPETAG_VECTOR; } //------------------------------------------------------------- // called from python directly on object attribute x,y,z access //------------------------------------------------------------- static PyObject * VectorGet( PyObject *pSelf, PyObject *pname ) { AssertIsVector( (HSCRIPT)pSelf ); if (!PyString_Check( pname )) return NULL; Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } const char *pszKey = PyString_AsString( pname ); if ( pszKey && *pszKey && !*(pszKey + 1) ) { int index = *pszKey - 'x'; if ( index >=0 && index <= 2) { float fret = (*pVector)[index]; return PyFloat_FromDouble( (double)fret ); } } return PyObject_GenericGetAttr( pSelf, pname ); } //------------------------------------------------------------- // called from python directly on object attribute x,y,z access //------------------------------------------------------------- static int VectorSet( PyObject *pSelf, PyObject *pname, PyObject *pval ) { AssertIsVector( (HSCRIPT)pSelf ); if (!PyString_Check( pname )) return -1; Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return -1; } const char *pszKey = PyString_AsString( pname ); if ( pszKey && *pszKey && !*(pszKey + 1) ) { int index = *pszKey - 'x'; if ( index >=0 && index <= 2) { (*pVector)[index] = (float)PyFloat_AsDouble( pval ); return 0; } } return -1; // no dictionary on vector object! return PyObject_GenericSetAttr( pSelf, pname, pval); } // repr function for vector static PyObject * VectorToString( PyObject *pSelf ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { return PyString_FromString(""); } return PyString_FromString( (static_cast(CFmtStr("", pVector->x, pVector->y, pVector->z))) ); } // repr function for instance static PyObject * InstanceToString( PyObject *pSelf ) { AssertIsPyObject( (HSCRIPT)pSelf ); return PyString_FromFormat("<%s at %p>", pSelf->ob_type->tp_name, (void*)pSelf ); // UNDONE: // StackHandler sa(hVM); // InstanceContext_t *pContext = (InstanceContext_t *)sa.GetInstanceUp(1,0); // char szBuf[64]; // // if ( pContext && pContext->pInstance && pContext->pClassDesc->pHelper && pContext->pClassDesc->pHelper->ToString( pContext->pInstance, szBuf, ARRAYSIZE(szBuf) ) ) // { // sa.Return( szBuf ); // } // else // { // HPYOBJECT hInstance = sa.GetObjectHandle( 1 ); // sq_pushstring( hVM, CFmtStr( "(instance : 0x%p)", (void*)_rawval(hInstance) ), -1 ); // } // return 1; } static PyObject * VectorToKeyValueString( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } return PyString_FromString( (static_cast(CFmtStr("%f %f %f", pVector->x, pVector->y, pVector->z))) ); } static PyObject * VectorAdd( PyObject *pSelf, PyObject *pOther ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; Vector *pVectorOther = ((PyVectorInstance_t *)pOther)->pVector; if ( !pVectorOther || !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } if ( !IsPyVector( pOther ) ) { PyErr_SetString(PyExc_ValueError, "can't add vector to non vector type"); return NULL; } // create new PyTypeVector object - explicitly calls VectorConstructNew PyObject *pretObj = CreatePyVector( NULL ); *( ((PyVectorInstance_t *)pretObj)->pVector ) = *pVector + *pVectorOther; return pretObj; } static PyObject * VectorSubtract( PyObject *pSelf, PyObject *pOther ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; Vector *pVectorOther = ((PyVectorInstance_t *)pOther)->pVector; if ( !pVectorOther || !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } if ( !IsPyVector( pOther ) ) { PyErr_SetString(PyExc_ValueError, "can't sub non vector type from vector"); return NULL; } // create new PyTypeVector object PyObject *pretObj = CreatePyVector( NULL ); *( ((PyVectorInstance_t *)pretObj)->pVector ) = *pVector - *pVectorOther; return pretObj; } static PyObject * VectorScale( PyObject *pSelf, PyObject *pScale ) { PyObject *pPyVec; PyObject *pPyScale; if ( ((PyVectorInstance_t *)pSelf)->typeTag == TYPETAG_VECTOR ) { pPyVec = pSelf; pPyScale = pScale; } else { pPyVec = pScale; pPyScale = pSelf; } Vector *pVector = ((PyVectorInstance_t *)pPyVec)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } if ( !PyFloat_Check( pPyScale ) ) { PyErr_SetString(PyExc_ValueError, "can't scale vector by non float type"); return NULL; } float scale = (float)PyFloat_AsDouble( pPyScale ); PyObject *pretObj = CreatePyVector( NULL ); *( ((PyVectorInstance_t *)pretObj)->pVector ) = *pVector * scale; return pretObj; } static int VectorCoerce(PyObject **pv, PyObject **pw) { if ( PyFloat_Check(*pw) ) return 0; // NOTE: we don't actually coerce - vector multiply handles a float scalar // and all other math functions explicitly check 2nd arg type. else return 1; // can't coerce anything else } static PyObject * VectorLength( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flLength = pVector->Length(); return PyFloat_FromDouble( (double)flLength ); } static PyObject * VectorLengthSqr( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flLength = pVector->LengthSqr(); return PyFloat_FromDouble( (double)flLength ); } static PyObject * VectorLength2D( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flLength = pVector->Length2D(); return PyFloat_FromDouble( (double)flLength ); } static PyObject * VectorLength2DSqr( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flLength = pVector->Length2DSqr(); return PyFloat_FromDouble( (double)flLength ); } static PyObject * VectorCross( PyObject *pSelf, PyObject *pOther ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; Vector *pVectorOther = ((PyVectorInstance_t *)pOther)->pVector; if ( !pVectorOther || !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } // create new PyTypeVector object PyObject *pretObj = CreatePyVector( NULL ); *( ((PyVectorInstance_t *)pretObj)->pVector ) = (*pVector).Cross( *pVectorOther ); return pretObj; } static PyObject * VectorDot( PyObject *pSelf, PyObject *pOther ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; Vector *pVectorOther = ((PyVectorInstance_t *)pOther)->pVector; if ( !pVectorOther || !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flResult = (*pVector).Dot( *pVectorOther ); return PyFloat_FromDouble( (double)flResult ); } static PyObject * VectorNorm( PyObject *pSelf, PyObject *pArgs ) { AssertIsVector( (HSCRIPT)pSelf ); Vector *pVector = ((PyVectorInstance_t *)pSelf)->pVector; if ( !pVector ) { PyErr_SetString(PyExc_ValueError, "null vector"); return NULL; } float flLength = pVector->NormalizeInPlace(); return PyFloat_FromDouble( (double)flLength ); } // python vector class methods static PyMethodDef g_VectorFuncs[] = { { "ToKVString", VectorToKeyValueString, METH_NOARGS, "Get x,y,z as string of space-separated floats." }, { "Length", VectorLength, METH_NOARGS, "Get the vector's magnitude." }, { "LengthSqr", VectorLengthSqr, METH_NOARGS, "Get the vector's magnitude squared." }, { "Length2D", VectorLength2D, METH_NOARGS, "2d x,y length." }, { "Length2DSqr", VectorLength2DSqr, METH_NOARGS, "Square of x,y length." }, { "Dot", VectorDot, METH_O, "Dot product of vectors." }, { "Cross", VectorCross, METH_O, "Cross product of vectors."}, { "Norm", VectorNorm, METH_NOARGS, "Normalize vector in place." }, { NULL, NULL, 0, NULL } }; static PyNumberMethods g_VectorAsNumber = { (binaryfunc)VectorAdd, /* nb_add */ (binaryfunc)VectorSubtract, /* nb_subtract */ (binaryfunc)VectorScale, /* nb_multiply */ 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ 0, /* nb_nonzero */ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ 0, // (coercion)VectorCoerce, /*nb_coerce*/ 0, /*nb_int*/ 0, /*nb_long*/ 0, /*nb_float*/ 0, /*nb_oct*/ 0, /*nb_hex*/ 0, /*nb_inplace_add*/ 0, /*nb_inplace_subtract*/ 0, /*nb_inplace_multiply*/ 0, /*nb_inplace_divide*/ 0, /*nb_inplace_remainder*/ 0, /*nb_inplace_power*/ 0, /*nb_inplace_lshift*/ 0, /*nb_inplace_rshift*/ 0, /*nb_inplace_and*/ 0, /*nb_inplace_xor*/ 0, /*nb_inplace_or*/ 0, /* nb_floor_divide */ 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ }; // python vector class template static PyTypeObject PyTypeVector = { PyObject_HEAD_INIT(NULL) /* type type */ // set up by PyType_Ready() call later. 0, /*ob_size*/ "Vector", /*tp_name*/ sizeof(PyVectorInstance_t),/*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)VectorRelease, /*tp_dealloc*/ // consider 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc )VectorToString, /*tp_repr*/ // consider &g_VectorAsNumber, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ (getattrofunc)VectorGet, /*tp_getattro*/ (setattrofunc)VectorSet, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ // don't coerce, all number functions check types 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ g_VectorFuncs, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ // base class type object 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)VectorConstructNew, /* tp_init */ // consider PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ // consider // BUG: doesn't call tp_init? PyObject_Del, /* tp_free */ }; //----------------------------------------------------------------------------------- // create a new python vector object from the PyTypeVector type // if pVector is null, allocs a new vector object, otherwise just references pVector //----------------------------------------------------------------------------------- inline PyObject *CreatePyVector( Vector *pVector ) { PyObject *pretObj = PyType_GenericNew( &PyTypeVector, NULL, NULL ); // calls tp_alloc(type,0) -> calls PyType_GenericAlloc -> calls PyObject_MALLOC if ( pVector ) { VectorBuildCopy( pretObj, pVector ); // use the provided vector to build the python object } else { VectorConstructNew( pretObj, NULL, NULL ); // allocate a new vector object within the python object } return pretObj; } inline bool IsPyVector( PyObject *pobj ) { return ( pobj->ob_type == &PyTypeVector ); } // ----------------------------------------------------------- // register custom vector class into the 'valve' module scope // ----------------------------------------------------------- bool RegisterVector( PyObject *pmodule ) { // finalize the new python vector type if (PyType_Ready( &PyTypeVector ) < 0) return false; // add the new class type to the 'valve' module Py_INCREF( &PyTypeVector ); PyModule_AddObject( pmodule, "Vector", (PyObject *)&PyTypeVector ); return true; } // ------------------------------------------------------- // Scope wrapper class. Python scope represents // a python module's dictionary, or an instance object's dictionary. // ------------------------------------------------------- class CPyScope { public: CPyScope( ) { m_pPyModule = NULL; // a python module object m_pPySelf = NULL; // an instance object m_pTempDict = PyDict_New(); // holding location for data until 'self' is set in scope m_typeTag = TYPETAG_SCOPE; } //------------------------------------------------------- // release all python objects on delete //------------------------------------------------------- ~CPyScope() { Py_XDECREF( m_pTempDict ); Py_XDECREF( m_pPyModule ); Py_XDECREF( m_pPySelf ); } void SetInstanceObject( PyObject *pSelf ) { m_pPySelf = (scriptClassInstance_t *)pSelf; Py_XINCREF( m_pPySelf ); } void SetModuleObject( PyObject *pModule ) { m_pPyModule = pModule; Py_XINCREF( m_pPyModule ); } PyObject *GetInstanceObject () { return (PyObject *)m_pPySelf; // return borrowed ref } PyObject * GetInstanceDict() { if ( !m_pPySelf ) return NULL; return m_pPySelf->pDict; // return borrowed ref } PyObject * GetModuleDict() { if ( !m_pPyModule ) return NULL; return PyModule_GetDict( m_pPyModule ); // return borrowed ref } PyObject *GetModule() { return m_pPyModule; } void TransferTempDictToInstance() { // move contents of temp dict into instance dict. // don't overwrite values in instance dict. Assert ( m_pPySelf ); PyDict_Merge( m_pPySelf->pDict, m_pTempDict, false ); } void SetTempDictValue( const char *pszKey, PyObject *pyobj ) { // set python key:value in temp dict PyDict_SetItemString( m_pTempDict, pszKey, pyobj ); } scriptClassInstance_t *m_pPySelf; // pointer to invoking entity instance - entity data lives here PyObject *m_pPyModule; // pointer to module - entity code lives here PyObject *m_pTempDict; int m_typeTag; }; inline void AssertIsScope( HSCRIPT hScope ) { // validate hscope handle as a CPyScope object Assert ( !hScope || ((CPyScope *)hScope)->m_typeTag == TYPETAG_SCOPE ); } //-------------------------------------------------------------------------------- // utility callback: display a string at the console // python sys.stdio and sys.stderr are redirected here //-------------------------------------------------------------------------------- static PyObject *vprint(PyObject *self, PyObject *args) { const char *psz; int ret = PyArg_ParseTuple(args, "s", &psz); if ( ret ) //DevMsg( (const tchar *)psz); Msg( (const tchar *)psz ); else DevMsg("vpython.cpp, vprint error: bad argument?"); return Py_BuildValue("i", 0); }; //-------------------------------------------------------------------------------- // Reload the named module & fixes up the internal module pointer saved in the hScope. // params: (PyObject *pSelf, PyObject *moduleName, PyObject *hScope ) // ignores first param 'self'. //-------------------------------------------------------------------------------- static PyObject *ReplaceClosures( PyObject *self, PyObject *args ) { PyObject *pSelf; // this is called through ExecuteFunction, which adds the self param const char *pszModuleName; PyObject *hScope; int ret = PyArg_ParseTuple(args, "OsO", &pSelf, &pszModuleName, &hScope); if ( !ret ) { DevMsg("vpython.cpp, ReplaceClosures argument error!"); return Py_BuildValue("i", 0); } // get the previously imported module handle PyObject *pOldModule = PyImport_AddModule( pszModuleName ); if ( !pOldModule ) { DevMsg("vpython.cpp, ReplaceClosures error: module was never previously loaded!" ); return Py_BuildValue("i", 0); } PyObject *pNewModule = PyImport_ReloadModule( pOldModule ); if ( !pNewModule ) { DevMsg("vpython.cpp, ReplaceClosures error: module failed to reload!" ); return Py_BuildValue("i", 0); } // fixup refs to old module object in hScope if ( hScope ) { if ( pOldModule != pNewModule ) { Assert( pOldModule == ((CPyScope *)hScope)->GetModule() ); Py_XDECREF( pOldModule ); // release the ref count that the hScope is holding on the old module ((CPyScope *)hScope)->SetModuleObject( pNewModule ); } } return Py_BuildValue("i", 0); } //--------------------------------------------------------------------------------- // given euler angles Pitch, Yaw, Roll, get forward vector //--------------------------------------------------------------------------------- static PyObject *VectorFromAngles( PyObject *self, PyObject *args ) { float fpitch, fyaw, froll; int ret = PyArg_ParseTuple(args, "|fff", &fpitch, &fyaw, &froll ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to VectorFromAngles, expected 3 floats!"); return Py_BuildValue("i",0); } // create new PyTypeVector object - explicitly calls VectorConstructNew PyObject *pretObj = CreatePyVector( NULL ); QAngle angles(fpitch, fyaw, froll); // QAngle(pitch, yaw, roll); Vector forward; AngleVectors( angles, &forward ); *( ((PyVectorInstance_t *)pretObj)->pVector ) = forward; // copy return pretObj; } //--------------------------------------------------------------------------------- // given forward vector, get euler angles Pitch, Yaw, Roll. Roll is always 0. //--------------------------------------------------------------------------------- static PyObject *AnglesFromVector( PyObject *self, PyObject *args ) { PyObject *pyVector; int ret = PyArg_ParseTuple(args, "|O",&pyVector ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to AnglesFromVector, expected 1 vector"); return Py_BuildValue("i",0); } QAngle angles; Vector forward = *((PyVectorInstance_t *)pyVector)->pVector; // copy VectorAngles( forward, angles); // returns pitch (x), yaw (y), roll (z) PyObject *pretObj1 = PyFloat_FromDouble(angles[PITCH]); PyObject *pretObj2 = PyFloat_FromDouble(angles[YAW]); PyObject *pretObj3 = PyFloat_FromDouble(angles[ROLL]); PyObject *pretObj = PyTuple_Pack(3, pretObj1, pretObj2, pretObj3); Py_XDECREF(pretObj1); Py_XDECREF(pretObj2); Py_XDECREF(pretObj3); return pretObj; } // ---------------------------------------------------------------------------- // given euler angles Pitch, Yaw, Roll, return tuple of vforward, vright, vup // ---------------------------------------------------------------------------- static PyObject *VectorsFromAngles( PyObject *self, PyObject *args ) { float fpitch, fyaw, froll; int ret = PyArg_ParseTuple(args, "|fff", &fpitch, &fyaw, &froll ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to VectorFromAngles, expected 3 floats!"); return Py_BuildValue("i",0); } // create new PyTypeVector object - explicitly calls VectorConstructNew PyObject *pretObj1 = CreatePyVector( NULL ); PyObject *pretObj2 = CreatePyVector( NULL ); PyObject *pretObj3 = CreatePyVector( NULL ); QAngle angles(fpitch, fyaw, froll); // QAngle(pitch, yaw, roll); Vector forward; Vector right; Vector up; AngleVectors( angles, &forward, &right, &up ); *( ((PyVectorInstance_t *)pretObj1)->pVector ) = forward; // copy *( ((PyVectorInstance_t *)pretObj2)->pVector ) = right; // copy *( ((PyVectorInstance_t *)pretObj3)->pVector ) = up; // copy PyObject *pretObj = PyTuple_Pack(3, pretObj1, pretObj2, pretObj3); Py_XDECREF(pretObj1); Py_XDECREF(pretObj2); Py_XDECREF(pretObj3); return pretObj; } //----------------------------------------------------------------------------- // Interpolate a Catmull-Rom spline. // t is a [0,1] value and interpolates a curve between p2 and p3. // takes p1,p2,p3,p4 vectors and float t, returns output vector. //----------------------------------------------------------------------------- static PyObject *CatmullRomSpline( PyObject *self, PyObject *args ) { // UNDONE: use interpolatortypes.cpp types instead of a single interpolator here PyObject *pyv1,*pyv2,*pyv3,*pyv4; Vector p1,p2,p3,p4; float t; int ret = PyArg_ParseTuple(args, "|OOOOf", &pyv1, &pyv2, &pyv3, &pyv4, &t ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to Catmull_Rom_Spline, expected 4 vectors and 1 float!"); return Py_BuildValue("i",0); } p1 = *((PyVectorInstance_t *)pyv1)->pVector; p2 = *((PyVectorInstance_t *)pyv2)->pVector; p3 = *((PyVectorInstance_t *)pyv3)->pVector; p4 = *((PyVectorInstance_t *)pyv4)->pVector; // create new PyTypeVector object - explicitly calls VectorConstructNew PyObject *pretObj = CreatePyVector( NULL ); Vector output(0,0,0); Catmull_Rom_Spline(p1,p2,p3,p4,t,output); *( ((PyVectorInstance_t *)pretObj)->pVector ) = output; // copy return pretObj; } //----------------------------------------------------------------------------- // Purpose: basic hermite spline. t = 0 returns p1, t = 1 returns p2, // d1 and d2 are used to entry and exit slope of curve // Input : p1,p2,d1,d2, t, //----------------------------------------------------------------------------- static PyObject *HermiteSpline( PyObject *self, PyObject *args ) { // UNDONE: use interpolatortypes.cpp types instead of a single interpolator here PyObject *pyv1,*pyv2,*pyv3,*pyv4; Vector p1,p2,d1,d2; float t; int ret = PyArg_ParseTuple(args, "|OOOOf", &pyv1, &pyv2, &pyv3, &pyv4, &t ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to HermiteSpline, expected 4 vectors and 1 float!"); return Py_BuildValue("i",0); } p1 = *((PyVectorInstance_t *)pyv1)->pVector; p2 = *((PyVectorInstance_t *)pyv2)->pVector; d1 = *((PyVectorInstance_t *)pyv3)->pVector; d2 = *((PyVectorInstance_t *)pyv4)->pVector; // create new PyTypeVector object - explicitly calls VectorConstructNew PyObject *pretObj = CreatePyVector( NULL ); Vector output(0,0,0); Hermite_Spline(p1,p2,d1,d2,t,output); *( ((PyVectorInstance_t *)pretObj)->pVector ) = output; // copy return pretObj; } static PyObject *HermiteSplineFloat( PyObject *self, PyObject *args ) { float p1,p2,d1,d2; float t; int ret = PyArg_ParseTuple(args, "|fffff", &p1,&p2,&d1,&d2, &t ); if ( !ret ) { DevMsg("vpython.cpp: invalid params to HermiteSplineFloat, expected 5 floats!"); return 0; } float output = Hermite_Spline(p1,p2,d1,d2,t); return Py_BuildValue("f", output); } //--------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------- static PyObject *ExactTime( PyObject *self, PyObject *args ) { return PyFloat_FromDouble( Plat_FloatTime() ); } static PyMethodDef valvemethods[] = { {"vprint",(PyCFunction) vprint, METH_VARARGS, "Display a string on the Valve console."}, {"__ReplaceClosures",(PyCFunction) ReplaceClosures, METH_VARARGS, "Reload a module."}, {"VectorFromAngles",(PyCFunction) VectorFromAngles, METH_VARARGS, "Convert from Roll, Pitch, Yaw to Vector"}, {"VectorsFromAngles",(PyCFunction) VectorsFromAngles, METH_VARARGS, "Convert from Roll, Pitch, Yaw to forward, right, up Vectors"}, {"AnglesFromVector",(PyCFunction) AnglesFromVector, METH_VARARGS, "Convert Vector to Roll, Pitch, Yaw angles (Yaw always 0)"}, {"ExactTime",(PyCFunction) ExactTime, METH_VARARGS, "Get accurate sub-frame time in seconds"}, {"CatmullRomSpline",(PyCFunction) CatmullRomSpline, METH_VARARGS, "Interpolate along a Catmull Rom Spline. Takes p1,p2,p3,p4 vectors and t float fraction."}, {"HermiteSpline", (PyCFunction) HermiteSpline, METH_VARARGS, "Interpolate along a Hermite Spline. Takes p1,p2,d1,d2 vectors and t float fraction."}, {"HermiteSplineFloat",(PyCFunction) HermiteSplineFloat, METH_VARARGS, "Interpolate along a Hermite Spline. Takes f1,f2,d1,d2 floats, and t float fraction."}, {NULL, NULL, 0, NULL} // sentinel }; //------------------------------------------------------------------------------ // UNUSED: Purpose: remove old module from sys.modules and decrement ref to module object. // may provide either module object, or module name //------------------------------------------------------------------------------ void UnloadModule( PyObject *pmodule, const char *pszModuleName ) { return; // BUG: the name of the last module to run becomes '__main__' - must not unload main! const char *pszName = NULL; PyObject *pSysModules = PyImport_GetModuleDict( ); // borrowed ref if ( pmodule ) { pszName = PyModule_GetFilename( pmodule ); pszName = PyModule_GetName( pmodule ); } else { Assert ( pszModuleName ); pszName = pszModuleName; } if ( !pszName ) return; // scope module has no name PyObject *pPyKey = PyString_FromString( pszName ); if ( PyDict_Contains( pSysModules, pPyKey) ) { // remove module from dict PyObject *pOldModule = PyDict_GetItemString( pSysModules, pszName ); PyDict_DelItemString( pSysModules, pszName ); Py_XDECREF( pOldModule ); } Py_XDECREF( pPyKey ); } //------------------------------------------------------------------ // SINGLETON python interpreter - do not instantiate more than one // (this module keeps a static global pointer to the interpreter instance.) //------------------------------------------------------------------ class CPythonVM : public IScriptVM { public: CPythonVM( ) : m_iUniqueIdSerialNumber( 0 ) #ifndef VPYTHON_TEST , developer( "developer" ) #else , developer( "developer", "1" ) #endif { m_bInitialized = false; // becomes true on all subsequent attempts to kill/restart the interpreter m_pRootScope = NULL; m_pValveScope = NULL; m_iMethodDef = 0; m_iClassDef = 0; m_debugObjCount = 0; } //------------------------------------------------------------- // Init the interpreter class - NOP if interp already active //------------------------------------------------------------- bool Init() { // g_pMemAlloc->CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_CHECK_CRT_DF ); if ( VMInitFinalized() ) return true; // Python makes use of mathlib, so we have to init it: MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); // init static proxy table InitProxyTable(); // start python interpreter installed on users machine. // i.e. python25.dll should be in c:\windows\system32 or on dll search path. Py_Initialize(); // this is a NOP if already initialized if ( !Py_IsInitialized() ) { DevWarning("CPythonVM.Init(): python interperter failed to initialize!"); return false; } const char *pszVersion = Py_GetVersion(); char buffer[64]; V_strncpy(buffer,pszVersion, 6); buffer[5] = '\0'; if ( V_strncmp(pszVersion, buffer, 5) ) { // we link with python25.lib from version 2.5.1 DevWarning("Python25.dll version mismatch: version %s loaded. Should be 2.5.1!\n", buffer); } // modify python's module search path in sys.path to point to current working directory + /scripts/vscripts int ret; ret = PyRun_SimpleString( "import sys\n" "import os\n" "a,b = os.path.split(os.getcwd())\n" "c,gamename = os.path.split(a)\n" "p = os.path.join (os.getcwd(),gamename,'scripts','vscripts')\n" "sys.path.append( p )\n" ); Assert ( ret == 0 ); // make sure we could set up the python sys.path to the vscripts directory // save off the vscript directory - ex: U:\projects\sob\game\sob\scripts\vscripts char szScriptPathTemp[MAX_PATH]; char szScriptPathTemp2[MAX_PATH]; char szDirectoryTemp[MAX_PATH]; char szGamename[MAX_PATH]; // get current working directory V_GetCurrentDirectory( szDirectoryTemp, sizeof( szDirectoryTemp ) ); // U:\projects\sob\game // get game name from current working directory V_ExtractFilePath( szDirectoryTemp, szGamename, sizeof(szGamename) ); // U:\projects\sob const char *pszGamename = V_UnqualifiedFileName( szGamename ); V_ComposeFileName( szDirectoryTemp, pszGamename, szScriptPathTemp, sizeof( szScriptPathTemp ) ); V_ComposeFileName( szScriptPathTemp, "scripts", szScriptPathTemp2, sizeof( szScriptPathTemp2 ) ); V_ComposeFileName( szScriptPathTemp2, "vscripts", m_szScriptPath, sizeof( m_szScriptPath ) ); // create a new global scope 'valve' module - all valve classes, functions, instances are registered in this module. // NOTE: 'valve' must be imported by python modules that wish to access these c functions. PyObject *pPyValve; pPyValve = PyImport_AddModule("valve"); PyModule_AddStringConstant(pPyValve, "__file__", ""); PyObject *pmod = Py_InitModule("valve", valvemethods); Assert ( pmod == pPyValve ); // Redirect python stdout & stderr to Valve console ret = PyRun_SimpleString( "import valve\n" "import sys\n" "class StdoutCatcher:\n" "\tdef write(self, str):\n" "\t\tvalve.vprint(str)\n" "class StderrCatcher:\n" "\tdef write(self, str):\n" "\t\tvalve.vprint(str)\n" "sys.stdout = StdoutCatcher()\n" "sys.stderr = StderrCatcher()\n" ); Assert ( ret == 0 ); // create new valve module scope m_pValveScope = new CPyScope( ); m_pValveScope->SetModuleObject( pPyValve ); // get the main module object PyObject* pPyMain = PyImport_AddModule("__main__"); // create new root scope from main's dictionary m_pRootScope = new CPyScope( ); m_pRootScope->SetModuleObject( pPyMain ); // load and run the init.py module in the global scope PyObject *pPyModule; PyObject *pystr = PyString_FromString( "init" ); pPyModule = PyImport_Import( pystr ); Py_XDECREF( pystr ); PyPrintError(); if ( !pPyModule ) { DevWarning("CPythonVM.Init(): unable to load main module init.py - module not on PYTHONPATH (sys.path)?"); return false; } m_TypeMap.Init( 256 ); // must be power of 2 m_ClassMap.Init( 256 ); // must be power of 2 RegisterVector( m_pValveScope->GetModule() ); PyPrintError(); return true; } //------------------------------------------------------------- // Called every frame with frame time //------------------------------------------------------------- bool Frame( float simTime ) { // UNDONE: garbage collect periodically? // UNDONE: invoke a per-frame global function? ex: to run generators #ifdef DEBUG_PY if (1) { // validate every instance object we've allocated for (int i=0; i < m_debugObjCount; i++) { if ( m_debugObjects[i] ) AssertIsPyObject( (HSCRIPT) (m_debugObjects[i]) ); } } #endif // DEBUG_PY return false; } //------------------------------------------------------------- // Called on level load //------------------------------------------------------------- void Shutdown() { bool bGameExit = false; // UNDONE: need flag passed in - only kill interpreter data on game exit, not restart or level load if ( bGameExit ) { if(1) Py_Finalize(); // interpreter shutdown - reliable only ONCE per game session. // shut down the vm. called on restart, or game exit if (1) { ReleaseScope( (HSCRIPT)m_pRootScope ); ReleaseScope( (HSCRIPT)m_pValveScope ); m_TypeMap.Purge(); m_ClassMap.Purge(); } } } //------------------------------------------------------------- // //------------------------------------------------------------- ScriptLanguage_t GetLanguage() { return SL_PYTHON; } //------------------------------------------------------------- // //------------------------------------------------------------- virtual const char *GetLanguageName() { return "Python"; } //------------------------------------------------------------- // //------------------------------------------------------------- virtual void AddSearchPath( const char *pszSearchPath ) { } //------------------------------------------------------------- // //------------------------------------------------------------- bool ConnectDebugger() { // for debugging from visual studio into python, running pydev in eclipse, // first launch the pydev debug server in eclipse, then // run python code containing the following line: // // import pydevd; pydevd.settrace() // for debuggin in Wing IDE, place 'import wingdbstub.py' in // the code module you wish to debug. return false; } //------------------------------------------------------------- // //------------------------------------------------------------- void DisconnectDebugger() { } //------------------------------------------------------------- // run script text in the root scope //------------------------------------------------------------- ScriptStatus_t Run( const char *pszScript, bool bWait = true ) { Assert( bWait ); PyObject* pyret = PyRun_String( pszScript, Py_file_input, m_pRootScope->GetModuleDict(), m_pRootScope->GetModuleDict() ) ; PyPrintError(); if ( pyret == NULL ) return SCRIPT_ERROR; Py_XDECREF(pyret); return SCRIPT_DONE; } //------------------------------------------------------------- // run the compiled script in the given scope //------------------------------------------------------------- ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) { return CPythonVM::ExecuteFunction( hScript, NULL, 0, NULL, hScope, bWait ); } //------------------------------------------------------------- // run the compiled script in the root scope //------------------------------------------------------------- ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) { Assert( bWait ); return CPythonVM::Run( hScript, (HSCRIPT)NULL, bWait ); } //------------------------------------------------------------- // python auto-compiles modules, so just return a python string containing the module name, // or a python function object // // pyszScript - script file contents - module content - ignored if pszId is given // pszId - module name with extension - modules live in scripts/vscripts // //------------------------------------------------------------- HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) { if ( pszId ) { // pszId is the module name with extension - required for python Assert(pszId != NULL); PyObject *pName; // strip module extension char buffer[1024]; V_StripExtension( pszId, buffer, sizeof(buffer) ); pName = PyString_FromString(buffer); // new ref return (HSCRIPT)pName; // return the name of the module to later import (and run). compilation is automatic. } else { // code string fixup: /* Replace any occurances of "\r\n?" in the input string with "\n". This converts DOS and Mac line endings to Unix line endings. Also append a trailing "\n" to be compatible with PyParser_SimpleParseFile(). Returns a new reference. */ if ( !pszScript || !*pszScript ) { DevWarning ( "Vscript: no script text passed to CompileScript - ignoring compilation!"); return NULL; } char *buf; { char *q; const char *p = pszScript; if (!p) return NULL; /* one char extra for trailing \n and one for terminating \0 */ buf = (char *)PyMem_Malloc( strlen(pszScript) + 2); if (buf == NULL) { PyErr_SetString(PyExc_MemoryError, "Python Source Compile - no memory to allocate conversion buffer!"); return NULL; } /* replace "\r\n?" by "\n" */ for (q = buf; *p != '\0'; p++) { if (*p == '\r') { *q++ = '\n'; if (*(p + 1) == '\n') p++; } else *q++ = *p; } *q++ = '\n'; /* add trailing \n */ *q = '\0'; } // create a new python code object - we own the reference which we must release later // NOTE: must pass full path to source file to enable debugging in external IDE (such as Wing IDE Professional). char szFullPath[MAX_PATH]; if ( pszId ) { V_ComposeFileName( m_szScriptPath, pszId, szFullPath, sizeof(szFullPath) ); } PyObject *pyCodeObject = Py_CompileString(buf, (pszId) ? szFullPath : "unnamed", Py_file_input); // PyObject *pyCodeObject = Py_CompileString(buf, "U:\\projects\\sob\\game\\sob\\scripts\\vscripts\\mapspawn.py", Py_file_input); PyPrintError(); PyMem_Free(buf); return (HSCRIPT)pyCodeObject; // return a code object - NULL on compile error } } //------------------------------------------------------------- // release code object, string object (module) or instance object //------------------------------------------------------------- void ReleaseScript( HSCRIPT hScript ) { Assert( hScript ); AssertIsPyObject( hScript ); if ( !PyString_Check( (PyObject *)hScript ) && !PyCode_Check( (PyObject *)hScript ) ) { AssertIsInstance( hScript ); } Py_XDECREF((PyObject *)hScript); } //------------------------------------------------------------- // create an empty scope object - use SetModule and SetInstance // to create actual references to dictionaries. //------------------------------------------------------------- HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) { CPyScope *pPyScope = new CPyScope(); if ( hParent ) { DevMsg( "Warning, Python script language ignoring hParent parameter for CreateScope!" ); } return (HSCRIPT)pPyScope; } //------------------------------------------------------------- // delete the CPyScope object //------------------------------------------------------------- void ReleaseScope( HSCRIPT hScript ) { AssertIsScope( hScript ); if ( hScript ) delete (CPyScope *)hScript; } //------------------------------------------------------------- // return a python PyObject* given a named variable in the given scope's instance object. // python object is a new reference if bAddRef is true, otherwise borrowed reference. //------------------------------------------------------------- PyObject* LookupObject( const char *pszObject, HSCRIPT hScope = NULL, bool bAddRef = true ) { if ( hScope == INVALID_HSCRIPT ) return NULL; PyObject *pGlobals = InstanceDictFromScope( hScope ); // Return the object from globals dict using key. Return NULL if the key is not present. PyObject *pyobj; pyobj = PyDict_GetItemString( pGlobals, pszObject); // returns borrowed reference - we don't own this obj unless we increment the reference. PyPrintError(); if ( pyobj == NULL) // key not in dict return NULL; if ( bAddRef ) Py_XINCREF(pyobj); return pyobj; } //------------------------------------------------------------- // return a python function object - lookup function in MODULE scope dict - // NOTE: this will only work correctly AFTER CompileScript and // Run or ExecuteFunction is called on the script which holds the function. // NOTE: caller must call ReleaseFunction when finished with function! //------------------------------------------------------------- HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) { // UNDONE: CBaseEntity calls this for EVERY think cycle - // UNDONE: rewrite CBaseEntity to remember the think function handle! if ( hScope == INVALID_HSCRIPT ) return NULL; PyObject *pGlobals; if ( hScope ) { pGlobals = ((CPyScope *)hScope)->GetModuleDict(); } else { pGlobals = m_pValveScope->GetModuleDict(); // lookup function in valve scope } if ( !pGlobals ) { DevWarning("Vscript, vpython.cpp: LookupFunction - must first compile and run the script before you can lookup a function!"); return NULL; } PyObject *pFunc = PyDict_GetItemString(pGlobals, pszFunction); // borrowed reference // set up function object with scope PyPrintError(); if (pFunc != NULL && PyCallable_Check(pFunc) ) { Py_INCREF( pFunc ); // inc ref so obj can be released later DEBUG_FUNCCOUNT++; Assert(DEBUG_FUNCCOUNT < 1000); // if this fails, server is likely not freeing function handles (leaking each frame) return (HSCRIPT )pFunc; } else { return NULL; } } //------------------------------------------------------------- // decrement our reference to the function handle //------------------------------------------------------------- void ReleaseFunction( HSCRIPT hScript ) { AssertIsPyObject( hScript ); Py_XDECREF((PyObject *)hScript); DEBUG_FUNCCOUNT--; //ReleaseScriptObject( hScript ); } //------------------------------------------------------------- // given a handle to a python function, a compiled code object, // or a module name, execute the object. If given a module name, // also sets the module object in the hScope for subsequent // execution of functions in module scope. Caller must free // variant args & any newly returned variant. //------------------------------------------------------------- ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope = NULL, bool bWait = true ) { if ( hScope == INVALID_HSCRIPT || !hFunction) { // DevWarning( "Invalid scope handed to script VM\n" ); return SCRIPT_ERROR; } AssertIsScope( hScope ); AssertIsPyObject( hFunction ); Assert ( bWait ); PyObject *pGlobals = ModuleDictFromScope( hScope ); // get type of hfunction - may be string (module), function, or compiled code object if ( PyString_Check( (PyObject *)hFunction) ) { // Import a module and set the hScope module variable. // NOTE: this is required before lookup/executeFunction calls into the module. // hFunction is a module name - may be returned by CompileScript // equivalent to python "import modulename" // note: python auto compiles the module if it is out of date, and saves the // binary image to disk for faster load times (no compile) on future import calls. ScriptStatus_t result = SCRIPT_DONE; PyObject *pModule; // char *pszModuleName = PyString_AsString( (PyObject *)hFunction ); // internal pointer // import and run module - always runs in global scope // PyObject *pystr = PyString_FromString( pszModuleName ); pModule = PyImport_Import( (PyObject *)hFunction ); // Py_XDECREF( pystr ); // set the module object in the scope. if ( hScope ) ( (CPyScope *)hScope )->SetModuleObject( pModule ); Py_XDECREF(pModule); PyPrintError(); if ( !pModule ) result = SCRIPT_ERROR; return result; } else if ( PyFunction_Check( (PyObject *)hFunction) || PyCallable_Check((PyObject *)hFunction) ) { // Run a function in a module: // hFunction is a python function object. // NOTE: hScope is ignored - it was associated with the function object during function lookup (function's module) // NOTE: first argument is always 'self' of the calling entity, even if NULL PyObject *pPyArgs = PyTuple_New(nArgs+1); PyObject *pValue; int i; // first argument is always 'self' ,even if NULL PyObject *pSelf = NULL; if ( hScope ) { pSelf = ((CPyScope *)hScope)->GetInstanceObject(); } if ( !pSelf ) pSelf = Py_None; Py_XINCREF( pSelf ); PyTuple_SetItem(pPyArgs, 0, pSelf); // steals ref to pSelf for (i = 0; i < nArgs; ++i) { pValue = ConvertToPyObject( pArgs[i], true ); // new ref // pValue reference stolen here: tuple owns the objects now PyTuple_SetItem(pPyArgs, i+1, pValue); } PyObject *pPyReturn = PyObject_CallObject((PyObject *)hFunction, pPyArgs); Py_DECREF(pPyArgs); // release tuple and contents PyPrintError(); if ( pPyReturn == NULL ) { // call failed return SCRIPT_ERROR; } bool bFreeobj = false; if ( pReturn ) { bFreeobj = ConvertToVariant( pPyReturn, pReturn ); // caller must free this } Py_XDECREF(pPyReturn); return SCRIPT_DONE; } else { // Run compiled code in a module: // assume hFunction is a compiled code object PyObject *pValue; PyObject *rgpyArgs[31]; PyObject *pPyReturn; Assert ( nArgs < 32 ); // first argument is always 'self' ,even if NULL PyObject *pSelf = ((CPyScope *)hScope)->GetInstanceObject(); if ( !pSelf ) pSelf = Py_None; Py_XINCREF( pSelf ); rgpyArgs[0] = pSelf; // arg steals ref to pSelf int i; for (i = 0; i < nArgs; i++) { pValue = ConvertToPyObject( pArgs[i], true ); // create new python objects with new refs rgpyArgs[i+1] = pValue; } if (0) { char *pszdebug = PyString_AsString( ((PyCodeObject *)hFunction)->co_filename ); char buffer[1024]; V_StripExtension( pszdebug, buffer, sizeof(buffer) ); //Py_XDECREF( ((PyCodeObject *)hFunction)->co_filename ); //((PyCodeObject *)hFunction)->co_filename = PyString_FromString( buffer ); PyObject *pystr = PyString_FromString( buffer ); PyObject *pmodule = PyImport_Import( pystr ); Py_XDECREF( pystr ); // BUG: this executes in the module's scope, which is not per-instance. // PyObject *pmodule = PyImport_ExecCodeModule( buffer, (PyObject *)hFunction ); Py_XDECREF( pmodule ); pPyReturn = Py_None; Py_INCREF( pPyReturn ); } else { // NOTE: This function will actually // run the code within the given scope dictionary - must be a module-level dictionary. pPyReturn = PyEval_EvalCodeEx((PyCodeObject *)hFunction, pGlobals, //PyObject *globals, pGlobals, //PyObject *locals, rgpyArgs, nArgs, //PyObject **args, int argc, NULL, 0, //PyObject **kwds, int kwdc, NULL, 0, //PyObject **defs, int defc, NULL); //PyObject *closure } // release arg objects for (i = 0; i < nArgs; i++) Py_XDECREF(rgpyArgs[i]); if ( pPyReturn == NULL ) { // call failed PyPrintError(); return SCRIPT_ERROR; } bool bFreeobj = false; if ( pReturn ) { bFreeobj = ConvertToVariant( pPyReturn, pReturn ); // caller must free this } Py_XDECREF(pPyReturn); return SCRIPT_DONE; } // invalid hFunction return SCRIPT_ERROR; } //------------------------------------------------------------- // register a new function so python code can call it //------------------------------------------------------------- void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) { // if ( VMInitFinalized() ) // return; RegisterFunctionGuts( pScriptFunction ); // NOTE: DEFINE_SCRIPTFUNC and ScripRegisterFunction macros eventually call RegisterFunction. // Templates automatically create the ScriptFunctionBinding_t. // Following is a summary of the Template expansion for ScriptRegisterFunction(Named) which // builds the ScriptFunctionBinding_t in place and then calls ResisterFunction with it. // This scheme effectively uses C++ compile-time templates to implement function introspection: /* ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateSceneEntity, "CreateSceneEntity", "Create a scene entity to play the specified scene." ); #define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); #define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); #define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); // this is a complex macro which, at compile time, // inlines the code to fill out the remaing fields in the // pDesc structure with the appropriate arg types and return types // for the given function. (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); // uses functors to create the binding function (pScriptFunction)->m_pFunction = (void *)&func; pVM->RegisterFunction( &binding ); } */ } //------------------------------------------------------------- // create a new python type object encapsulating the class - // NOTE: must subsequently call PyType_Ready to finalize the class //------------------------------------------------------------- PyTypeObject *CreateClass( ScriptClassDesc_t *pDesc ) { // python class template static PyTypeObject scriptClassType = { PyObject_HEAD_INIT(NULL) /* type type */ // set up by PyType_Ready() call later 0, /*ob_size*/ 0, /*tp_name*/ sizeof(scriptClassInstance_t), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ // consider 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)InstanceToString, /*tp_repr*/ // consider 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ PyObject_GenericGetAttr, /*tp_getattro*/ PyObject_GenericSetAttr, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, // | Py_TPFLAGS_BASETYPE, /*tp_flags*/ // allow subclassing 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ // base class type object 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(scriptClassInstance_t, pDict), // tp_dictoffset - used by generic getattr,setattr 0, /* tp_init */ // consider PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ // consider PyObject_Del, /* tp_free */ }; // build a new scriptClassType for each 'CreateClass' call PyTypeObject *pnewtype = (PyTypeObject *) PyMem_Malloc( sizeof(PyTypeObject) ); //new PyTypeObject; if ( !pnewtype ) { // interperter out of memory Assert( false ); return NULL; } // track it so we can free it later Assert (m_iClassDef < MAX_VALVE_CLASSES_EXPORTED ); m_rgpClassDefs[m_iClassDef++] = pnewtype; // allow mapping between PyTypeObject (accessed from pSelf->ob_type ptr) to ScriptClassDesc_t pDesc. // init the pnewtype with the static template V_memcpy( pnewtype, &scriptClassType, sizeof(PyTypeObject) ); pnewtype->tp_doc = pDesc->m_pszDescription; pnewtype->tp_name = pDesc->m_pszScriptName; // BUG: prepend "valve." or pickling is impossible // if base class given, make sure base class type already defined in root scope // then hook it up to our type if ( pDesc->m_pBaseDesc ) { PyObject *pdict = m_pValveScope->GetModuleDict(); if ( PyDict_GetItemString( pdict, pDesc->m_pBaseDesc->m_pszScriptName ) == NULL ) { Assert( false ); // base class should have been pre registered in 'valve' module return NULL; } // lookup the corresponding type object PyTypeObject *pbasetype = PyTypeFromDesc( pDesc->m_pBaseDesc ); if ( !pbasetype ) { Assert ( false ); return NULL; } pnewtype->tp_base = pbasetype; } // set up constructor and destructor // DEALLOCATION CHAIN: Py_XDECREF -> tp_dealloc (free any local allocations) -> tp_free (free python object allocation) // ALLOCATION CHAIN: tp_new, tp_alloc (create empty python object) -> tp_init (set up local data) pnewtype->tp_new = PyType_GenericNew; // create new uninitialized object (just calls tp_alloc) // BUG: doesn't call tp_init! pnewtype->tp_init = (initproc)CPythonVM::InitInstance; // same as __init__ for class - init the context associated with the object pnewtype->tp_alloc = PyType_GenericAlloc; // alloc space for a python object pnewtype->tp_free = PyObject_Del; // delete the python object pnewtype->tp_dealloc = (destructor)CPythonVM::FreeInstance; // called when ref count drops to 0 - release any memory held by object // (ie: call destructor on underlaying valve object if object was allocated from python ) // UNDONE: implement these additional callbacks //sq_pushstring( m_hVM, "_tostring", -1 ); //sq_newclosure( m_hVM, &InstanceToString, 0 ); //sq_createslot( m_hVM, -3 ); //sq_pushstring( m_hVM, "IsValid", -1 ); //sq_newclosure( m_hVM, &InstanceIsValid, 0 ); //sq_createslot( m_hVM, -3 ); // register all methods - create tp_methods table of pnewtype int count = pDesc->m_FunctionBindings.Count(); if ( count ) { // create an array large enough for all method defs + null semaphore PyMethodDef *pmethods = (PyMethodDef *) PyMem_Malloc( sizeof(PyMethodDef) * (count + 1) ); // new PyMethodDef[count+1]; PyMethodDef *pm; if ( !pmethods ) { // interperter out of memory Assert ( false ); return NULL; } Assert (m_iMethodDef < MAX_VALVE_FUNCTIONS_EXPORTED ); m_rgpMethodDefs[m_iMethodDef++] = pmethods; // TEST1:m_pMethodDefs.AddToTail( pmethods ); int i; for ( i = 0; i < count; i++ ) { ScriptFunctionBinding_t *pScriptFunction = &(pDesc->m_FunctionBindings[i]); pm = &(pmethods[i]); // fill python method def pm->ml_name = pScriptFunction->m_desc.m_pszScriptName; pm->ml_flags = METH_VARARGS; pm->ml_doc = pScriptFunction->m_desc.m_pszDescription; int proxyId = GetNewProxyId(); SetProxyBinding( proxyId, pScriptFunction ); // the function/method callback chain - python calls Translate_XXX -> calls TranslateCall -> calls binding function -> actual function. pm->ml_meth = GetProxyFunction( proxyId ); } // set null semaphore at end of methods pm = &(pmethods[i]); pm->ml_name = NULL; pm->ml_meth = NULL; pm->ml_flags = 0; pm->ml_doc = NULL; pnewtype->tp_methods = pmethods; } return pnewtype; } //------------------------------------------------------------- // create new python type object for this class and include // ScriptClassDesc_t in the user type data. //------------------------------------------------------------- bool RegisterClass( ScriptClassDesc_t *pClassDesc ) { // if ( VMInitFinalized() ) // return true; PyObject *valveModule = m_pValveScope->GetModule(); PyObject *pdict = m_pValveScope->GetModuleDict(); if ( PyDict_GetItemString( pdict, pClassDesc->m_pszScriptName ) != NULL ) return true; // already registered COMPILE_TIME_ASSERT( sizeof(pClassDesc) == sizeof(int) ); if ( PyTypeFromDesc( pClassDesc ) ) { return true; } // register base class first if ( pClassDesc->m_pBaseDesc ) { CPythonVM::RegisterClass( pClassDesc->m_pBaseDesc ); } PyTypeObject *pnewtype = CreateClass( pClassDesc ); if ( pnewtype == NULL ) return false; // finalize the new python type if (PyType_Ready( pnewtype ) < 0) return false; // add the new class type to the 'valve' module Py_INCREF( pnewtype ); PyModule_AddObject( valveModule, pClassDesc->m_pszScriptName, (PyObject *)pnewtype ); m_TypeMap.Insert( (int)pClassDesc, pnewtype ); // mapping from pClassDesc to PyTypeObject needed for RegisterInstance m_ClassMap.Insert( (int)pnewtype, pClassDesc ); // mapping from PyTypeObject to pClassDesc needed for InitInstance constructor callback if ( PyDict_GetItemString( pdict, pClassDesc->m_pszScriptName ) == NULL ) { Assert ( false ); return false; // class wasn't added to valve module! } PyPrintError(); return true; } //------------------------------------------------------------- // auto-register instance class, and return script instance obj: // such as a cbaseentity instance, Entities iterator list etc //------------------------------------------------------------- HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) { // auto-create the instance's class if not already created if ( !CPythonVM::RegisterClass( pDesc ) ) { return NULL; } // create a new python instance - this winds up calling InitInstance to set up the instanceContext // x = CEntity( pInstance ) # python code PyObject *pcallable = PyDict_GetItemString( m_pValveScope->GetModuleDict(), pDesc->m_pszScriptName ); // borrowed ref scriptClassInstance_t *ppyobj = NULL; if ( pcallable && PyCallable_Check( pcallable ) ) { // create new script object ppyobj = (scriptClassInstance_t *)PyObject_CallObject( pcallable, NULL); // new ref ppyobj->typeTag = TYPETAG_INSTANCE; // make sure type name matches if ( ppyobj->ob_type->tp_name != pDesc->m_pszScriptName ) { Assert ( false ); return NULL; } // fill in the instance context for the new object ppyobj->instanceContext.pInstance = pInstance; ppyobj->instanceContext.pPyName = NULL; ppyobj->instanceContext.pClassDesc = pDesc; ppyobj->pDict = PyDict_New(); } PyPrintError(); return (HSCRIPT)ppyobj; } //------------------------------------------------------------- // set a unique string in the instance object. //------------------------------------------------------------- void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) { AssertIsInstance( hInstance ); // make sure this is an object type we have defined if ( pszId && pDescFromPyObj( (PyObject *)hInstance ) ) { ((scriptClassInstance_t *)hInstance)->instanceContext.pPyName = PyString_FromString( pszId ); } } //------------------------------------------------------------- // set instance pointer to valve server object to null // and release the python object //------------------------------------------------------------- void RemoveInstance( HSCRIPT hInstance ) { AssertIsInstance( hInstance ); // make sure this is an object type we have defined if ( pDescFromPyObj( (PyObject *)hInstance ) ) { ReleaseScriptObject( hInstance ); debugRemoveTrackedObject( (PyObject *) hInstance ); } } //------------------------------------------------------------- // Return server-side object from python object //------------------------------------------------------------- void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType ) { AssertIsInstance( hInstance ); // make sure this is an object type we have defined if ( pDescFromPyObj( (PyObject *)hInstance ) ) { InstanceContext_t *pContext = &( ((scriptClassInstance_t *)hInstance)->instanceContext ); if ( !pExpectedType || pContext->pClassDesc == pExpectedType || IsClassDerivedFrom( pContext->pClassDesc, pExpectedType ) ) return pContext->pInstance; } return NULL; } //------------------------------------------------------------- // return true if derived class derives from base class. //------------------------------------------------------------- bool IsClassDerivedFrom( const ScriptClassDesc_t *pDerivedClass, const ScriptClassDesc_t *pBaseClass ) { const ScriptClassDesc_t* pType = pDerivedClass->m_pBaseDesc; while ( pType ) { if ( pType == pBaseClass ) return true; pType = pType->m_pBaseDesc; } return false; } bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) { Assert( V_strlen(pszRoot) + 32 <= nBufSize ); Q_snprintf( pBuf, nBufSize, "%x%I64x%s", RandomInt(0, 0xfff), m_iUniqueIdSerialNumber++, pszRoot ); // random to limit key compare when serial number gets large return true; } //------------------------------------------------------------- // return true if key has a value in scope's instance object. // CONSIDER: if no instance object, try module object, if no module // object, try global scope. //------------------------------------------------------------- virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) { if ( hScope == INVALID_HSCRIPT ) return false; AssertIsScope( hScope ); PyObject *pGlobals = InstanceDictFromScope( hScope ); if ( !pGlobals ) // pGlobals = ModuleDictFromScope( hScope ); return false; Assert ( pGlobals ); if ( PyObject_HasAttrString( pGlobals, pszKey ) ) return true; return false; } bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) { return SetValueInternal( hScope, pszKey, NULL, pszValue ); } bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) { return SetValueInternal( hScope, pszKey, value, NULL ); } //------------------------------------------------------------- // set key:value pair in given scope's instance object dict. // if hScope is NULL, set data in global scope. caller still owns // object if is an Hscript (i.e. must free it). // // !!!NOTE!!!: data is not actually flushed into the instance object // until a 'self' instance value is set in the scope. At that // time, all previously set data will be visible in python // on the instance object. In python code, the instance is named 'self' // and is passed as the first param to all function calls. //------------------------------------------------------------- bool SetValueInternal( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value, const char *pszValue ) { if ( hScope == INVALID_HSCRIPT ) return false; AssertIsScope( hScope ); CPyScope *pScope = (CPyScope *)hScope; PyObject *pyobj; if ( pszValue ) { pyobj = PyString_FromString( pszValue ); // new reference } else { // trap special 'self' instance - save it in the CPyScope object if ( value.m_type == FIELD_HSCRIPT ) { // instance object... if ( !V_strcmp(pszKey, "self") ) { // save instance in scope PyObject *pyobj = ConvertToPyObject( value, true ); // new ref pScope->SetInstanceObject( pyobj ); // inc ref to object Py_XDECREF( pyobj ); // for hscript objects, caller owns the hscript within the variant, not us // copy scope temp dict into instance dict. pScope->TransferTempDictToInstance(); return true; } } // set a new copy of the value in instance's dict pyobj = ConvertToPyObject( value, true ); if ( value.m_type == FIELD_HSCRIPT) { Py_XDECREF( pyobj ); // for hscript objects, caller owns the hscript within the variant, not us debugTrackObject( pyobj ); } } PyObject *pGlobals; if ( !hScope ) { // set data in valve module scope pGlobals = m_pValveScope->GetModuleDict(); } else { // place all other data in scope's instance object dict pGlobals = InstanceDictFromScope( hScope ); } if ( !pGlobals ) { // instance object 'self' not yet set up in scope - add to temporary dict pScope->SetTempDictValue( pszKey, pyobj ); return true; } else { int ret = PyDict_SetItemString( pGlobals, pszKey, pyobj); // dict does not own the objects in it - but will decref old obj when overwritten. if ( ret == -1 ) return false; return true; } } //------------------------------------------------------------------------------ // Purpose: create a new python dictionary object and return wrapped in a // scriptVariant HScript object. //---------------------------------------------------------------------------- void CreateTable( ScriptVariant_t &Table ) { PyObject *pdict = PyDict_New(); ConvertToVariant( pdict, &Table ); return; } //------------------------------------------------------------------------------ // Purpose: returns the number of elements in the scope's instance object dict // Input : hScope - the table // Output : returns the number of elements in the table //------------------------------------------------------------------------------ int GetNumTableEntries( HSCRIPT hScope ) { if ( hScope == INVALID_HSCRIPT ) return 0; AssertIsScope( hScope ); PyObject *pGlobals = InstanceDictFromScope( hScope ); int ret = (int) PyDict_Size( pGlobals ); return ret; } //------------------------------------------------------------------------------ // Purpose: Gets a key / value pair from the instance dictionary // Input : hScope - the instance dictionary // nInterator - the current location inside of the table. NOTE this is nota linear representation // Output : returns the next iterator spot, otherwise -1 if error or end of table // pKey - the key entry // pValue - the value entry //------------------------------------------------------------------------------ int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) { if ( hScope == INVALID_HSCRIPT ) return -1; PyObject *pGlobals = InstanceDictFromScope( hScope ); Py_ssize_t nNextIterator = (Py_ssize_t)nIterator; PyObject *pPyKey, *pPyValue; int ret; ret = PyDict_Next( pGlobals, &nNextIterator, &pPyKey, &pPyValue ); if ( !ret ) { // iteration complete return -1; } ConvertToVariant( pPyKey, pKey ); ConvertToVariant( pPyValue, pValue ); return (int)nNextIterator; } //------------------------------------------------------------- // lookup a key value in the scope's instance dictionary //------------------------------------------------------------- bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) { PyObject *result = LookupObject( pszKey, hScope, false ); if ( ConvertToVariant( result, pValue ) && (result != Py_None) ) { return true; } return false; } //------------------------------------------------------------- // remove key from the given hscope - search instance scope, // then module scope. //------------------------------------------------------------- bool ClearValue( HSCRIPT hScope, const char *pszKey ) { if ( hScope == INVALID_HSCRIPT ) return false; PyObject *pInstGlobals = NULL; PyObject *pModuleGlobals = NULL; int res; if ( !hScope ) { // clear data in valve module scope pModuleGlobals = m_pValveScope->GetModuleDict(); } else { pInstGlobals = ((CPyScope *)hScope)->GetInstanceDict(); pModuleGlobals = ((CPyScope *)hScope)->GetModuleDict(); } PyObject *pystr = PyString_FromString( pszKey ); if ( pInstGlobals && PyDict_Contains( pInstGlobals, pystr ) ) { res = PyDict_DelItemString( pInstGlobals, pszKey); Py_XDECREF( pystr); if ( res == -1 ) return false; return true; } if ( pModuleGlobals && PyDict_Contains( pModuleGlobals, pystr ) ) { res = PyDict_DelItemString( pModuleGlobals, pszKey); Py_XDECREF( pystr); if ( res == -1 ) return false; return true; } return false; } //------------------------------------------------------------- // release resources saved in script variant //------------------------------------------------------------- void ReleaseValue( ScriptVariant_t &value ) { if ( value.m_flags & SV_FREE ) DEBUG_VARIANTCOUNT--; if ( value.m_type == FIELD_HSCRIPT ) { // drop our ref count to the python object Py_XDECREF((PyObject *)value.m_hScript); } else { value.Free(); } value.m_type = FIELD_VOID; } bool RaiseException( const char *pszExceptionText ) { PyErr_SetString(PyExc_Exception, pszExceptionText); return true; } virtual void DumpState() { // UNDONE: /*struct CIterator : public CSQStateIterator { CIterator( HPYTHONVM hVM ) { indent = 0; m_hVM = hVM; m_bKey = false; } void Indent() { for ( int i = 0; i < indent; i++) { Msg( " " ); } } virtual void PsuedoKey( const char *pszPsuedoKey ) { Indent(); Msg( "%s: ", pszPsuedoKey ); m_bKey = true; } virtual void Key( SQObjectPtr &key ) { Indent(); SQObjectPtr res; m_hVM->ToString( key, res ); Msg( "%s: ", res._unVal.pString->_val ); m_bKey = true; } virtual void Value( SQObjectPtr &value ) { if ( !m_bKey ) { Indent(); } m_bKey = false; SQObjectPtr res; m_hVM->ToString( value, res ); if ( ISREFCOUNTED(value._type) ) Msg( "%s [%d]\n", res._unVal.pString->_val, value._unVal.pRefCounted->_uiRef ); else Msg( "%s\n", res._unVal.pString->_val ); } virtual bool BeginContained() { if ( m_bKey ) { Msg( "\n" ); } m_bKey = false; Indent(); Msg( "{\n" ); indent++; return true; } virtual void EndContained() { indent--; Indent(); Msg( "}\n" ); } int indent; HPYTHONVM m_hVM; bool m_bKey; }; CIterator iter( m_hVM ); m_hVM->_sharedstate->Iterate( m_hVM, &iter );*/ } //------------------------------------------------------------- // //------------------------------------------------------------- void WriteState( CUtlBuffer *pBuffer) { // UNDONE: //#ifdef VPYTHON_DEBUG_SERIALIZATION // Msg( "BEGIN WRITE\n" ); //#endif // m_pBuffer = pBuffer; // sq_collectgarbage( m_hVM ); // // m_pBuffer->PutInt( SAVEVERSION ); // m_pBuffer->PutInt64( m_iUniqueIdSerialNumber ); // WriteVM( m_hVM ); // // m_pBuffer = NULL; // // SQCollectable *t = m_hVM->_sharedstate->_gc_chain; // while(t) // { // t->UnMark(); // t = t->_next; // } // // m_PtrMap.Purge(); } //------------------------------------------------------------- // //------------------------------------------------------------- void ReadState( CUtlBuffer *pBuffer ) { // UNDONE: //#ifdef VPYTHON_DEBUG_SERIALIZATION //#ifdef VPYTHON_DEBUG_SERIALIZATION_HEAPCHK // g_pMemAlloc->CrtCheckMemory(); // int flags = g_pMemAlloc->CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); // g_pMemAlloc->CrtSetDbgFlag( flags | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_CHECK_CRT_DF ); //#endif // Msg( "BEGIN READ\n" ); //#endif // // if ( pBuffer->GetInt() != SAVEVERSION ) // { // DevMsg( "Incompatible script version\n" ); // return; // } // sq_collectgarbage( m_hVM ); // m_hVM->_sharedstate->_gc_disableDepth++; // m_pBuffer = pBuffer; // int64 uniqueIdSerialNumber = m_pBuffer->GetInt64(); // m_iUniqueIdSerialNumber = max( m_iUniqueIdSerialNumber, uniqueIdSerialNumber ); // Verify( pBuffer->GetInt() == OT_THREAD ); // m_PtrMap.Insert( pBuffer->GetPtr(), m_hVM ); // ReadVM( m_hVM ); // m_pBuffer = NULL; // m_PtrMap.Purge(); // m_hVM->_sharedstate->_gc_disableDepth--; // sq_collectgarbage( m_hVM ); // //#ifdef VPYTHON_DEBUG_SERIALIZATION_HEAPCHK // g_pMemAlloc->CrtSetDbgFlag( flags ); //#endif } //------------------------------------------------------------- // //------------------------------------------------------------- void RemoveOrphanInstances() { } //------------------------------------------------------------- // //------------------------------------------------------------- virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) { } //------------------------------------------------------------- // //------------------------------------------------------------- virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) { } //--------------------------------------------------------------------------------- // The main call dispatcher - dispatches to C++ functions called from python. // Get script binding object, translate args and dispatch to actual c function call // NOTE: this must be static! (ie - it is called without a this ptr from the proxy functions) //--------------------------------------------------------------------------------- static PyObject *TranslateCall( ScriptFunctionBinding_t *pVMScriptFunction, scriptClassInstance_t *pSelf, PyObject *pArgs) { int nActualParams = (int) PyTuple_Size( pArgs ); int nFormalParams = pVMScriptFunction->m_desc.m_Parameters.Count(); CUtlVectorFixed params; ScriptVariant_t returnValue; params.SetSize( nFormalParams ); // convert python params to vector of scriptVariant_t params as req'd by binding function int i = 0; PyObject *pyobj; if ( nActualParams ) { int iLimit = MIN( nActualParams, nFormalParams ); ScriptDataType_t *pCurParamType = pVMScriptFunction->m_desc.m_Parameters.Base(); for ( i = 0; i < iLimit; i++, pCurParamType++ ) { pyobj = PyTuple_GetItem( pArgs, (Py_ssize_t)i ); switch ( *pCurParamType ) { case FIELD_FLOAT: { if ( !PyFloat_Check( pyobj) ) { PyErr_SetString(PyExc_ValueError, "expected float argument"); return NULL; } params[i] = PyFloat_AsDouble( pyobj ); } break; case FIELD_CSTRING: { if ( !PyString_Check( pyobj ) ) { PyErr_SetString(PyExc_ValueError, "expected string argument"); return NULL; } params[i] = PyString_AsString( pyobj ); // DO NOT FREE THIS Assert( !(params[i].m_flags &= SV_FREE ) ); } break; case FIELD_VECTOR: { if ( pyobj->ob_type != &PyTypeVector ) { if ( !PyString_Check( pyobj ) ) { PyErr_SetString(PyExc_ValueError, "expected vector argument"); return NULL; } } Vector *pVector = ((PyVectorInstance_t *)pyobj)->pVector; // get pointer if ( pVector ) { params[i] = pVector; // DO NOT FREE THIS Assert( !(params[i].m_flags &= SV_FREE ) ); break; } } break; case FIELD_INTEGER: { if ( !PyInt_Check( pyobj ) ) { PyErr_SetString(PyExc_ValueError, "expected integer argument"); return NULL; } params[i] = PyInt_AsLong( pyobj ); } break; case FIELD_BOOLEAN: { if ( pyobj == Py_False ) { params[i] = false; } else if ( pyobj == Py_True ) { params[i] = true; } else { PyErr_SetString(PyExc_ValueError, "expected boolean argument"); return NULL; } } break; case FIELD_CHARACTER: { if ( !PyString_Check( pyobj ) ) { PyErr_SetString(PyExc_ValueError, "expected string argument"); return NULL; } const char *psz = PyString_AsString( pyobj ); params[i] = *psz; } case FIELD_HSCRIPT: { if ( pyobj == Py_None ) { params[i] = (HSCRIPT)NULL; } else { if ( ((scriptClassInstance_t *)pyobj)->typeTag != TYPETAG_INSTANCE ) { PyErr_SetString(PyExc_ValueError, "expected HSCRIPT instance object argument"); return NULL; } params[i] = (HSCRIPT)pyobj; // (HSCRIPT)PyCObject_AsVoidPtr( pyobj ); } break; } default: break; } } } #ifdef _DEBUG for ( ; i < nFormalParams; i++ ) { Assert( params[i].IsNull() ); } #endif // get object instance pointer from pSelf if this is a method call on object InstanceContext_t *pContext; void *pObject; if ( pVMScriptFunction->m_flags & SF_MEMBER_FUNC ) { pContext = &( pSelf->instanceContext ); if ( !pContext ) { PyErr_SetString(PyExc_ValueError, "Accessed null instance"); return NULL; } pObject = pContext->pInstance; if ( !pObject ) { PyErr_SetString(PyExc_ValueError, "Accessed null instance"); return NULL; } if ( pContext->pClassDesc->pHelper ) { pObject = pContext->pClassDesc->pHelper->GetProxied( pObject ); } if ( !pObject ) { PyErr_SetString(PyExc_ValueError, "Accessed null instance"); return NULL; } } else { pObject = NULL; } // call the binding function, which will make the actual C function call (*pVMScriptFunction->m_pfnBinding)( pVMScriptFunction->m_pFunction, pObject, params.Base(), params.Count(), ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) ? &returnValue : NULL ); PyObject *pret = NULL; // use the returned scriptvariant to create a new python object if ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) { // this is the ONE case where we must actually embed a reference to the returned Vector, // instead of creating a copy of the returned Vector. // this is because the binding function call above auto-creates a new Vector for the return value. (see vscript_templates.h line 278 etc) pret = ((CPythonVM *)g_pVm)->ConvertToPyObject( returnValue, false ); // create new ref } // NOTE: returning NULL and setting error state above should throw the python error... if ( pret == NULL ) Py_RETURN_NONE; return pret; } private: //---------------- // inline Helpers //---------------- //------------------------------------------------------------------------------ // Purpose: print most recent python error to console //------------------------------------------------------------------------------ inline bool PyPrintError() { if ( PyErr_Occurred() ) { PyErr_Print(); return true; } return false; } //------------------------------------------------------------------------------ // Purpose: given hscope, return scope's instance object's dictionary. // returns NULL if no instance object associated with scope. //------------------------------------------------------------------------------ inline PyObject *InstanceDictFromScope( HSCRIPT hScope ) { if ( !hScope ) return NULL; AssertIsScope( hScope ); Assert ( hScope != INVALID_HSCRIPT ); return ((CPyScope *)hScope)->GetInstanceDict(); } //---------------------------------------------------------- // given hscope, return scope's module-level dictionary //---------------------------------------------------------- inline PyObject *ModuleDictFromScope( HSCRIPT hScope ) { if ( hScope ) { // get module's dict AssertIsScope( hScope ); Assert ( hScope != INVALID_HSCRIPT ); return ((CPyScope *)hScope)->GetModuleDict(); } else { // get global scope dict return m_pRootScope->GetModuleDict(); } } //--------------------------------------------------------------- // given class descriptor, get our pre-defined python type object. // return NULL if not found. //--------------------------------------------------------------- static inline PyTypeObject *PyTypeFromDesc( ScriptClassDesc_t *pDesc ) { if ( !pDesc ) return NULL; UtlHashFastHandle_t h = ((CPythonVM *)g_pVm)->m_TypeMap.Find( (int)pDesc ); if ( h == ((CPythonVM *)g_pVm)->m_TypeMap.InvalidHandle() ) { return NULL; } return (PyTypeObject *)(((CPythonVM *)g_pVm)->m_TypeMap.Element ( h )); } //--------------------------------------------------------------- // given python object, get class descriptor associated with object's type. // return NULL if object is not a server-side object. //--------------------------------------------------------------- static inline ScriptClassDesc_t *pDescFromPyObj( PyObject *pobj ) { if ( ! pobj ) return NULL; AssertIsPyObject( (HSCRIPT)pobj ); PyTypeObject *ptype = pobj->ob_type; UtlHashFastHandle_t h = ((CPythonVM *)g_pVm)->m_ClassMap.Find( (int)ptype ); if ( h == ((CPythonVM *)g_pVm)->m_ClassMap.InvalidHandle() ) { return NULL; } return (ScriptClassDesc_t *)(((CPythonVM *)g_pVm)->m_ClassMap.Element ( h )); } ////--------------------------------------------------------- //// Callbacks ////--------------------------------------------------------- //static void PrintFunc(HPYTHONVM m_hVM,const SQChar* s,...) //{ // Msg( CFmtStr( &s ) ); //} // //------------------------------------------------------------- // // // //------------------------------------------------------------- // static SQInteger ReleaseHook( SQUserPointer p, SQInteger size ) // { // InstanceContext_t *pInstanceContext = (InstanceContext_t *)p; // pInstanceContext->pClassDesc->m_pfnDestruct( pInstanceContext->pInstance ); // delete pInstanceContext; // return 0; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // static SQInteger ExternalInstanceReleaseHook( SQUserPointer p, SQInteger size ) // { // InstanceContext_t *pInstanceContext = (InstanceContext_t *)p; // delete pInstanceContext; // return 0; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // static SQInteger GetFunctionSignature( HPYTHONVM hVM ) // { // StackHandler sa(hVM); // if ( sa.GetParamCount() != 3 ) // { // return 0; // } // // HPYOBJECT hFunction = sa.GetObjectHandle( 2 ); // if ( !sq_isclosure( hFunction ) ) // { // return 0; // } // // std::string result; // const char *pszName = sa.GetString( 3 ); // SQClosure *pClosure = hFunction._unVal.pClosure; // SQFunctionProto *pProto = pClosure->_function._unVal.pFunctionProto; // // result += "function "; // if ( pszName && *pszName ) // { // result += pszName; // } // else if ( sq_isstring( pProto->_name ) ) // { // result += pProto->_name._unVal.pString->_val; // } // else // { // result += ""; // } // result += "("; // // for ( int i = 1; i < pProto->_nparameters; i++ ) // { // if ( i != 1 ) // result += ", "; // if ( sq_isstring( pProto->_parameters[i] ) ) // { // result += pProto->_parameters[i]._unVal.pString->_val; // } // else // { // result += "arg"; // } // } // result += ")"; // // sa.Return( result.c_str() ); // // return 1; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // static SQInteger GetDeveloper( HPYTHONVM hVM ) // { // StackHandler sa(hVM); // sa.Return( ((CPythonVM *)hVM->_sharedstate->m_pOwnerData)->developer.GetInt() ); // return 1; // } // //------------------------------------------------------------- // called from python directly during object construction. // same as class __init__ function - init the member data for server object instance // If the context has a constructor, call it to create the server object instance. // args and keywords params currently ignored. //------------------------------------------------------------- static int InitInstance( scriptClassInstance_t *pSelf, PyObject *args, PyObject *kwds ) { InstanceContext_t *pInstanceContext = &( pSelf->instanceContext ); pInstanceContext->pInstance = NULL; pInstanceContext->pClassDesc = NULL; pInstanceContext->pPyName = NULL; pSelf->pDict = PyDict_New(); //((scriptClassInstance_t *)pSelf)->typeTag = TYPETAG_INSTANCE; ScriptClassDesc_t *pDesc = pDescFromPyObj( (PyObject *)pSelf ); if ( pDesc ) { pInstanceContext->pClassDesc = pDesc; if ( pDesc->m_pfnConstruct ) { pInstanceContext->pInstance = pDesc->m_pfnConstruct(); } } else { return -1; } return 0; } //---------------------------------------------------------------------- // called from python directly during object destruction (tp_dealloc) // call destructor on the server object instance, then release the python object //---------------------------------------------------------------------- static void FreeInstance( scriptClassInstance_t *pSelf ) { AssertIsInstance( (HSCRIPT)pSelf ); InstanceContext_t *pcontext = &( pSelf->instanceContext ); ScriptClassDesc_t *pDesc = pDescFromPyObj( (PyObject *)pSelf ); if ( pDesc ) { if ( pDesc->m_pfnDestruct ) { pDesc->m_pfnDestruct( pcontext->pInstance ); } } pcontext->pInstance = NULL; Py_XDECREF( pcontext->pPyName ); Py_XDECREF( pSelf->pDict ); // will decref all objs held in dict pSelf->ob_type->tp_free( (PyObject *)pSelf ); } //------------------------------------------------------------- // UNDONE: script execution throttle - equivalent in python? //static int QueryContinue( HPYTHONVM hVM ) //{ // CPythonVM *pVM = ((CPythonVM *)hVM->_sharedstate->m_pOwnerData); // if ( !pVM->m_hDbg ) // { // if ( pVM->m_TimeStartExecute != 0.0f && Plat_FloatTime() - pVM->m_TimeStartExecute > 0.03f ) // { // DevMsg( "Script running too long, terminating\n" ); // // @TODO: Mark the offending closure so that it won't be executed again [5/13/2008 tom] // return SQ_QUERY_BREAK; // } // } // return SQ_QUERY_CONTINUE; //} //------------------------------------------------------------- // //------------------------------------------------------------- static PyObject *InstanceIsValid( PyObject *pSelf, PyObject *pArgs ) { // UNDONE: //InstanceContext_t *pContext = (InstanceContext_t *)pSelf; // //if ( pContext && pContext->pInstance ) // Py_RETURN_TRUE; //else // Py_RETURN_FALSE; } //------------------------------------------------------------- // allow C++ function to be called from python - // function parameter binding is described by // ScriptFunctionBinding_t //------------------------------------------------------------- void RegisterFunctionGuts( ScriptFunctionBinding_t *pScriptFunction ) { PyObject *pdict = m_pValveScope->GetModuleDict(); if ( PyDict_GetItemString( pdict, pScriptFunction->m_desc.m_pszScriptName ) != NULL ) return; // already registered // alloc space for small (2 element) array of PyMethodDefs - elements must not move in memory. PyMethodDef *pmethod = new PyMethodDef[2]; // save pointers so we can free 'em all later Assert (m_iMethodDef < MAX_VALVE_FUNCTIONS_EXPORTED ); m_rgpMethodDefs[m_iMethodDef++] = pmethod; // create NULL semaphore at tail of list PyMethodDef *pm = &(pmethod[1]); pm->ml_name = NULL; pm->ml_meth = NULL; pm->ml_flags = 0; pm->ml_doc = NULL; // fill the python method def pm = &(pmethod[0]); pm->ml_name = pScriptFunction->m_desc.m_pszScriptName; pm->ml_flags = METH_VARARGS; pm->ml_doc = pScriptFunction->m_desc.m_pszDescription; int proxyId = GetNewProxyId(); SetProxyBinding( proxyId, pScriptFunction ); // the function callback chain - python calls Translate_XXX -> calls TranslateCall -> calls binding function -> actual function. pm->ml_meth = GetProxyFunction( proxyId ); Py_InitModule3("valve", pmethod, "Import module for access to all exported Valve methods."); // set up parameter checking char szTypeMask[64]; if ( pScriptFunction->m_desc.m_Parameters.Count() > ARRAYSIZE(szTypeMask) - 1 ) { AssertMsg1( 0, "Too many arguments for script function %s\n", pScriptFunction->m_desc.m_pszFunction ); return; } // UNDONE: implement help - function param type documenting in python //szTypeMask[0] = '.'; //char *pCurrent = &szTypeMask[1]; //for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++, pCurrent++ ) //{ // switch ( pScriptFunction->m_desc.m_Parameters[i] ) // { // case FIELD_CSTRING: // *pCurrent = 's'; // break; // case FIELD_FLOAT: // case FIELD_INTEGER: // *pCurrent = 'n'; // break; // case FIELD_BOOLEAN: // *pCurrent = 'b'; // break; // case FIELD_VECTOR: // *pCurrent = 'x'; // break; // case FIELD_HSCRIPT: // *pCurrent = '.'; // break; // case FIELD_CHARACTER: // default: // *pCurrent = FIELD_VOID; // AssertMsg( 0 , "Not supported" ); // break; // } //} //Assert( pCurrent - szTypeMask < ARRAYSIZE(szTypeMask) - 1 ); //*pCurrent = 0; //sq_pushstring( m_hVM, pScriptFunction->m_desc.m_pszScriptName, -1 ); //ScriptFunctionBinding_t **pVMScriptFunction = (ScriptFunctionBinding_t **)sq_newuserdata(m_hVM, sizeof(ScriptFunctionBinding_t *)); //*pVMScriptFunction = pScriptFunction; //sq_newclosure( m_hVM, &TranslateCall, 1 ); //HPYOBJECT hFunction; //sq_getstackobj( m_hVM, -1, &hFunction ); //sq_setnativeclosurename(m_hVM, -1, pScriptFunction->m_desc.m_pszScriptName ); //sq_setparamscheck( m_hVM, pScriptFunction->m_desc.m_Parameters.Count() + 1, szTypeMask ); //sq_createslot( m_hVM, -3 ); //if ( developer.GetInt() ) //{ // const char *pszHide = SCRIPT_HIDE; // if ( !pScriptFunction->m_desc.m_pszDescription || *pScriptFunction->m_desc.m_pszDescription != *pszHide ) // { // std::string name; // std::string signature; // if ( pClassDesc ) // { // name += pClassDesc->m_pszScriptName; // name += "::"; // } // name += pScriptFunction->m_desc.m_pszScriptName; // signature += FieldTypeToString( pScriptFunction->m_desc.m_ReturnType ); // signature += ' '; // signature += name; // signature += '('; // for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++ ) // { // if ( i != 0 ) // { // signature += ", "; // } // signature+= FieldTypeToString( pScriptFunction->m_desc.m_Parameters[i] ); // } // signature += ')'; // sq_pushobject( m_hVM, LookupObject( "RegisterFunctionDocumentation", NULL, false ) ); // sq_pushroottable( m_hVM ); // sq_pushobject( m_hVM, hFunction ); // sq_pushstring( m_hVM, name.c_str(), name.length() ); // sq_pushstring( m_hVM, signature.c_str(), signature.length() ); // sq_pushstring( m_hVM, pScriptFunction->m_desc.m_pszDescription, -1 ); // sq_call( m_hVM, 5, false, /*false*/ true ); // sq_pop( m_hVM, 1 ); // } //} return; } //------------------------------------------------------------- // drop our ref count on the script object //------------------------------------------------------------- void ReleaseScriptObject( HSCRIPT hScript ) { AssertIsPyObject( hScript ); Py_XDECREF( (PyObject *)hScript ); } //------------------------------------------------------------- // create a new python object from the script variant // UNLESS variant is an HSCRIPT - in this case, just returns // the embedded PyObject * with incremented ref count. // if bAllocNewVector is true, create a new C++ Vector, // otherwise, embed a reference to the variant's Vector, within new py object. // NOTE: all references will be freed when the py object is freed! //------------------------------------------------------------- PyObject *ConvertToPyObject( const ScriptVariant_t &value, bool bAllocNewVector ) { switch ( value.m_type ) { case FIELD_VOID: Py_RETURN_NONE; case FIELD_FLOAT: return PyFloat_FromDouble( (double)value.m_float ); case FIELD_CSTRING: if ( value.IsNull() ) Py_RETURN_NONE; return PyString_FromStringAndSize( value, (Py_ssize_t) strlen( value.m_pszString )); case FIELD_VECTOR: { PyObject *pretObj; if ( !bAllocNewVector ) { // Vector was alloc'd by caller, and in this (rare) case we are expected to free it. // create a python vector object that references the variant's vector object // NOTE: the variant's vector object will be deleted when the python object is deleted. DEBUG_VECCOUNT++; pretObj = CreatePyVector( (Vector *)value.m_pVector ); } else { // create new python vector object and copy from scriptvariant data. must be freed by caller pretObj = CreatePyVector( NULL ); *(((PyVectorInstance_t*)pretObj)->pVector) = *((Vector *)value.m_pVector); // copy operator } return pretObj; } case FIELD_INTEGER: return PyInt_FromLong( value.m_int ); case FIELD_BOOLEAN: return PyBool_FromLong( value.m_bool ); case FIELD_CHARACTER: { char sz[2]; sz[0] = value.m_char; sz[1] = 0; return PyString_FromStringAndSize( sz, (Py_ssize_t)1 ); } case FIELD_HSCRIPT: { if ( value.m_hScript ) { PyObject *pyobj = (PyObject *)value.m_hScript; //PyCObject_FromVoidPtr((void *)value.m_hScript, NULL); Py_XINCREF( pyobj ); return pyobj; } else { Py_RETURN_NONE; } } } Py_RETURN_NONE; } //------------------------------------------------------------- // fill variant struct with appropriate value from python object // NOTE: does not decref the python object. //------------------------------------------------------------- bool ConvertToVariant( PyObject* object, ScriptVariant_t *pReturn ) { AssertIsPyObject( (HSCRIPT)object ); if ( object == Py_None ) { pReturn->m_type = FIELD_VOID; } else if ( PyLong_CheckExact( object ) ) { *pReturn = (int)PyLong_AsLong( object ); // UNDONE: need error checking for overflow - will return NULL } else if ( PyInt_CheckExact( object ) ) { *pReturn = (int)PyInt_AS_LONG( object ); // No error checking is performed, since we started with int } else if ( PyFloat_CheckExact( object ) ) { *pReturn = (float)PyFloat_AS_DOUBLE( object ); // no error checking since we started with float } else if ( PyBool_Check( object ) ) { if ( object == Py_True ) *pReturn = true; else *pReturn = false; } else if ( PyString_Check( object ) ) { // create a new string in the variant char *buffer; Py_ssize_t length; PyString_AsStringAndSize( object, &buffer, &length); int size = (int)length + 1; pReturn->m_type = FIELD_CSTRING; pReturn->m_pszString = new char[size]; V_memcpy( (void *)pReturn->m_pszString, buffer, size ); pReturn->m_flags |= SV_FREE; DEBUG_VARIANTCOUNT++; Assert( DEBUG_VARIANTCOUNT < 1000 ); // if this fails, server is likely not freeing return values from python fn calls each frame } else if ( IsPyVector( object ) ) { // create a new vector in the variant that copies the object's vector data Vector *pVector = ((PyVectorInstance_t *)object)->pVector; pReturn->m_type = FIELD_VECTOR; pReturn->m_pVector = new Vector( *((Vector *)pVector) ); pReturn->m_flags |= SV_FREE; DEBUG_VARIANTCOUNT++; Assert( DEBUG_VARIANTCOUNT < 1000 ); // if this fails, server is likely not freeing return values from python fn calls each frame } else { // save the actual object pointer pReturn->m_type = FIELD_HSCRIPT; pReturn->m_hScript =(HSCRIPT)object; // PyCObject_AsVoidPtr( object ); return false; // don't free object } return true; // ok to free python object } // // //------------------------------------------------------------------------- // // UNDONE: Serialization for save/restore // //------------------------------------------------------------------------- // enum // { // SAVEVERSION = 2 // }; // // void WriteObject( const SQObjectPtr &object ) // { // switch ( object._type ) // { // case OT_NULL: // m_pBuffer->PutInt( OT_NULL ); // break; // case OT_INTEGER: // m_pBuffer->PutInt( OT_INTEGER ); // m_pBuffer->PutInt( object._unVal.nInteger ); // break; // case OT_FLOAT: // m_pBuffer->PutInt( OT_FLOAT ); // m_pBuffer->PutFloat( object._unVal.fFloat); // break; // case OT_BOOL: // m_pBuffer->PutInt( OT_BOOL ); // m_pBuffer->PutInt( object._unVal.nInteger ); // break; // case OT_STRING: // m_pBuffer->PutInt( OT_STRING ); // m_pBuffer->PutInt( object._unVal.pString->_len ); // m_pBuffer->PutString( object._unVal.pString->_val ); // break; // case OT_TABLE: WriteTable( object._unVal.pTable ); break; // case OT_ARRAY: WriteArray( object._unVal.pArray ); break; // case OT_USERDATA: WriteUserData( object._unVal.pUserData ); break; // case OT_CLOSURE: WriteClosure( object._unVal.pClosure ); break; // case OT_NATIVECLOSURE: WriteNativeClosure( object._unVal.pNativeClosure ); break; // case OT_GENERATOR: WriteGenerator( object._unVal.pGenerator ); break; // case OT_USERPOINTER: WriteUserPointer( object._unVal.pUserPointer ); break; // case OT_THREAD: WriteVM( object._unVal.pThread ); break; // case OT_FUNCPROTO: WriteFuncProto( object._unVal.pFunctionProto ); break; // case OT_CLASS: WriteClass( object._unVal.pClass ); break; // case OT_INSTANCE: WriteInstance( object._unVal.pInstance ); break; // case OT_WEAKREF: WriteWeakRef( object._unVal.pWeakRef ); break; // default: Assert( 0 ); break; // } // //#ifdef VPYTHON_DEBUG_SERIALIZATION // SQObjectPtr res; // m_hVM->ToString( object, res ); // Msg( "%d: %s\n", m_pBuffer->TellPut(), res._unVal.pString->_val ); //#endif // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteVM( PYVM *pVM ) // { // unsigned i; // // m_pBuffer->PutInt( OT_THREAD ); // m_pBuffer->PutPtr( pVM ); // // if ( pVM->_uiRef & MARK_FLAG ) // return; // pVM->_uiRef |= MARK_FLAG; // // WriteObject( pVM->_roottable ); // m_pBuffer->PutInt( pVM->_top ); // m_pBuffer->PutInt( pVM->_stackbase ); // m_pBuffer->PutUnsignedInt( pVM->_stack.size() ); // for( i = 0; i < pVM->_stack.size(); i++ ) // { // WriteObject( pVM->_stack[i] ); // } // m_pBuffer->PutUnsignedInt( pVM->_vargsstack.size() ); // for( i = 0; i < pVM->_vargsstack.size(); i++ ) // { // WriteObject( pVM->_vargsstack[i] ); // } // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteArray( SQArray *pArray ) // { // m_pBuffer->PutInt( OT_ARRAY ); // m_pBuffer->PutPtr( pArray ); // // if ( pArray->_uiRef & MARK_FLAG ) // return; // pArray->_uiRef |= MARK_FLAG; // // int len = pArray->_values.size(); // m_pBuffer->PutInt( len ); // for ( int i = 0; i < len; i++ ) // WriteObject( pArray->_values[i] ); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteTable( SQTable *pTable ) // { // m_pBuffer->PutInt( OT_TABLE ); // m_pBuffer->PutPtr( pTable ); // // if ( pTable->_uiRef & MARK_FLAG ) // return; // pTable->_uiRef |= MARK_FLAG; // // m_pBuffer->PutInt( pTable->_delegate != NULL ); // if ( pTable->_delegate ) // { // WriteObject( pTable->_delegate ); // } // // int len = pTable->_numofnodes; // m_pBuffer->PutInt( len ); // for(int i = 0; i < len; i++) // { // WriteObject( pTable->_nodes[i].key ); // WriteObject( pTable->_nodes[i].val ); // } // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteClass( SQClass *pClass ) // { // m_pBuffer->PutInt( OT_CLASS ); // m_pBuffer->PutPtr( pClass ); // // if ( !pClass || ( pClass->_uiRef & MARK_FLAG ) ) // return; // pClass->_uiRef |= MARK_FLAG; // // bool bIsNative = ( pClass->_typetag != NULL ); // unsigned i; // if ( !bIsNative ) // { // for( i = 0; i < pClass->_methods.size(); i++) // { // if ( sq_isnativeclosure( pClass->_methods[i].val ) ) // { // bIsNative = true; // break; // } // } // } // m_pBuffer->PutInt( bIsNative ); // if ( !bIsNative ) // { // m_pBuffer->PutInt( pClass->_base != NULL ); // if ( pClass->_base ) // { // WriteObject( pClass->_base ); // } // // WriteObject( pClass->_members ); // WriteObject( pClass->_attributes ); // m_pBuffer->PutInt( pClass->_defaultvalues.size() ); // for( i = 0; i< pClass->_defaultvalues.size(); i++) // { // WriteObject(pClass->_defaultvalues[i].val); // WriteObject(pClass->_defaultvalues[i].attrs); // } // m_pBuffer->PutInt( pClass->_methods.size() ); // for( i = 0; i < pClass->_methods.size(); i++) // { // WriteObject(pClass->_methods[i].val); // WriteObject(pClass->_methods[i].attrs); // } // m_pBuffer->PutInt( pClass->_metamethods.size() ); // for( i = 0; i < pClass->_metamethods.size(); i++) // { // WriteObject(pClass->_metamethods[i]); // } // } // else // { // if ( pClass->_typetag ) // { // if ( pClass->_typetag == TYPETAG_VECTOR ) // { // m_pBuffer->PutString( "Vector" ); // } // else // { // ScriptClassDesc_t *pDesc = (ScriptClassDesc_t *)pClass->_typetag; // m_pBuffer->PutString( pDesc->m_pszScriptName ); // } // } // else // { // // Have to grovel for the name // SQObjectPtr key; // if ( FindKeyForObject( m_hVM->_roottable, pClass, key ) ) // { // m_pBuffer->PutString( key._unVal.pString->_val ); // } // else // { // Assert( 0 ); // m_pBuffer->PutString( "" ); // } // } // } // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteInstance( SQInstance *pInstance ) // { // m_pBuffer->PutInt( OT_INSTANCE ); // m_pBuffer->PutPtr( pInstance ); // // if ( pInstance->_uiRef & MARK_FLAG ) // return; // pInstance->_uiRef |= MARK_FLAG; // // WriteObject( pInstance->_class ); // // unsigned nvalues = pInstance->_class->_defaultvalues.size(); // m_pBuffer->PutInt( nvalues ); // for ( unsigned i =0; i< nvalues; i++ ) // { // WriteObject( pInstance->_values[i] ); // } // // m_pBuffer->PutPtr( pInstance->_class->_typetag ); // // if ( pInstance->_class->_typetag ) // { // if ( pInstance->_class->_typetag == TYPETAG_VECTOR ) // { // Vector *pVector = (Vector *)pInstance->_userpointer; // m_pBuffer->PutFloat( pVector->x ); // m_pBuffer->PutFloat( pVector->y ); // m_pBuffer->PutFloat( pVector->z ); // } // else // { // InstanceContext_t *pContext = ((InstanceContext_t *)pInstance->_userpointer); // WriteObject( pContext->name ); // m_pBuffer->PutPtr( pContext->pInstance ); // } // } // else // { // WriteUserPointer( NULL ); // } // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteGenerator( SQGenerator *pGenerator ) // { // ExecuteOnce( Msg( "Save load of generators not well tested. caveat emptor\n" ) ); // WriteObject(pGenerator->_closure); // // m_pBuffer->PutInt( OT_GENERATOR ); // m_pBuffer->PutPtr( pGenerator ); // // if ( pGenerator->_uiRef & MARK_FLAG ) // return; // pGenerator->_uiRef |= MARK_FLAG; // // WriteObject( pGenerator->_closure ); // m_pBuffer->PutInt( pGenerator->_stack.size() ); // for(SQUnsignedInteger i = 0; i < pGenerator->_stack.size(); i++) WriteObject(pGenerator->_stack[i]); // m_pBuffer->PutInt( pGenerator->_vargsstack.size() ); // for(SQUnsignedInteger j = 0; j < pGenerator->_vargsstack.size(); j++) WriteObject(pGenerator->_vargsstack[j]); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteClosure( SQClosure *pClosure ) // { // m_pBuffer->PutInt( OT_CLOSURE ); // m_pBuffer->PutPtr( pClosure ); // if ( pClosure->_uiRef & MARK_FLAG ) // return; // pClosure->_uiRef |= MARK_FLAG; // // WriteObject( pClosure->_function ); // WriteObject( pClosure->_env ); // // m_pBuffer->PutInt( pClosure->_outervalues.size() ); // for(SQUnsignedInteger i = 0; i < pClosure->_outervalues.size(); i++) WriteObject(pClosure->_outervalues[i]); // m_pBuffer->PutInt( pClosure->_defaultparams.size() ); // for(SQUnsignedInteger i = 0; i < pClosure->_defaultparams.size(); i++) WriteObject(pClosure->_defaultparams[i]); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteNativeClosure( SQNativeClosure *pNativeClosure ) // { // m_pBuffer->PutInt( OT_NATIVECLOSURE ); // m_pBuffer->PutPtr( pNativeClosure ); // // if ( pNativeClosure->_uiRef & MARK_FLAG ) // return; // pNativeClosure->_uiRef |= MARK_FLAG; // // WriteObject( pNativeClosure->_name ); // return; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteUserData( SQUserData *pUserData ) // { // m_pBuffer->PutInt( OT_USERDATA ); // m_pBuffer->PutPtr( pUserData ); // // if ( pUserData->_uiRef & MARK_FLAG ) // return; // pUserData->_uiRef |= MARK_FLAG; // // // Need to call back or something. Unsure, TBD. [4/3/2008 tom] // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteUserPointer( SQUserPointer pUserPointer ) // { // m_pBuffer->PutInt( OT_USERPOINTER ); // // Need to call back or something. Unsure, TBD. [4/3/2008 tom] // m_pBuffer->PutPtr( pUserPointer ); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // static SQInteger SqWriteFunc(SQUserPointer up,SQUserPointer data, SQInteger size) // { // CPythonVM *pThis = (CPythonVM *)up; // pThis->m_pBuffer->Put( data, size ); // return size; // } // // void WriteFuncProto( SQFunctionProto *pFuncProto ) // { // m_pBuffer->PutInt( OT_FUNCPROTO ); // m_pBuffer->PutPtr( pFuncProto ); // // // Using the map to track these as they're not collectables // if ( m_PtrMap.Find( pFuncProto ) != m_PtrMap.InvalidIndex() ) // { // return; // } // m_PtrMap.Insert( pFuncProto, pFuncProto ); // // pFuncProto->Save( m_hVM, this, &SqWriteFunc ); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void WriteWeakRef( SQWeakRef *pWeakRef ) // { // m_pBuffer->PutInt( OT_WEAKREF ); // WriteObject( pWeakRef->_obj ); // } // // //-------------------------------------------------------- // template // bool BeginRead( T **ppOld, T **ppNew ) // { // *ppOld = (T *)m_pBuffer->GetPtr(); // if ( *ppOld ) // { // int iNew = m_PtrMap.Find( *ppOld ); // if ( iNew != m_PtrMap.InvalidIndex() ) // { // *ppNew = (T*)m_PtrMap[iNew]; // return false; // } // } // *ppNew = NULL; // return true; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void MapPtr( void *pOld, void *pNew ) // { // Assert( m_PtrMap.Find( pOld ) == m_PtrMap.InvalidIndex() ); // m_PtrMap.Insert( pOld, pNew ); // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // bool ReadObject( SQObjectPtr &objectOut, const char *pszName = NULL ) // { // SQObject object; // bool bResult = true; // object._type = (SQObjectType)m_pBuffer->GetInt(); // if ( _RAW_TYPE(object._type) < _RT_TABLE ) // { // switch ( object._type ) // { // case OT_NULL: // object._unVal.pUserPointer = 0; // break; // case OT_INTEGER: // object._unVal.nInteger = m_pBuffer->GetInt(); // break; // case OT_FLOAT: // object._unVal.fFloat = m_pBuffer->GetFloat(); // break; // case OT_BOOL: // object._unVal.nInteger = m_pBuffer->GetInt(); // break; // case OT_STRING: // { // int len = m_pBuffer->GetInt(); // char *pString = (char *)stackalloc( len + 1 ); // m_pBuffer->GetString( pString, len + 1 ); // pString[len] = 0; // object._unVal.pString = SQString::Create( m_hVM->_sharedstate, pString, len ); // break; // } // default: // Assert( 0 ); // break; // } // } // else // { // switch ( object._type ) // { // case OT_TABLE: // { // object._unVal.pTable = ReadTable(); // break; // } // case OT_ARRAY: // { // object._unVal.pArray = ReadArray(); // break; // } // case OT_USERDATA: // { // object._unVal.pUserData = ReadUserData(); // break; // } // case OT_CLOSURE: // { // object._unVal.pClosure = ReadClosure(); // break; // } // case OT_NATIVECLOSURE: // { // object._unVal.pNativeClosure = ReadNativeClosure(); // break; // } // case OT_GENERATOR: // { // object._unVal.pGenerator = ReadGenerator(); // break; // } // case OT_USERPOINTER: // { // object._unVal.pUserPointer = ReadUserPointer(); // break; // } // case OT_THREAD: // { // object._unVal.pThread = ReadVM(); // break; // } // case OT_FUNCPROTO: // { // object._unVal.pFunctionProto = ReadFuncProto(); // break; // } // case OT_CLASS: // { // object._unVal.pClass = ReadClass(); // break; // } // case OT_INSTANCE: // { // object._unVal.pInstance = ReadInstance(); // if ( !object._unVal.pInstance ) // { // // Look for a match in the current root table // HPYOBJECT hExistingObject = LookupObject( pszName, NULL, false ); // if ( sq_isinstance( hExistingObject ) ) // { // object._unVal.pInstance = hExistingObject._unVal.pInstance; // } // } // break; // } // case OT_WEAKREF: // { // object._unVal.pWeakRef = ReadWeakRef(); // break; // } // default: // { // object._unVal.pUserPointer = NULL; // Assert( 0 ); // } // } // if ( !object._unVal.pUserPointer ) // { // DevMsg( "Failed to restore a Python object of type %s\n", SQTypeToString( object._type ) ); // object._type = OT_NULL; // bResult = false; // } // } // //#ifdef VPYTHON_DEBUG_SERIALIZATION // lastType = object._type; // SQObjectPtr res; // if ( ISREFCOUNTED(object._type) ) // object._unVal.pRefCounted->_uiRef++; // m_hVM->ToString( object, res ); // if ( ISREFCOUNTED(object._type) ) // object._unVal.pRefCounted->_uiRef--; // Msg( "%d: %s [%d]\n", m_pBuffer->TellGet(), res._unVal.pString->_val, ( ISREFCOUNTED(object._type) ) ? object._unVal.pRefCounted->_uiRef : -1 ); //#ifdef VPYTHON_DEBUG_SERIALIZATION_HEAPCHK // _heapchk(); //#endif //#endif // objectOut = object; // return bResult; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // PYVM *ReadVM() // { // PYVM *pVM = sq_newthread( m_hVM, MIN_STACK_OVERHEAD + 2 ); // m_hVM->Pop(); // return pVM; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // void ReadVM( PYVM *pVM ) // { // unsigned i; // // ReadObject( pVM->_roottable ); // pVM->_top = m_pBuffer->GetInt(); // pVM->_stackbase = m_pBuffer->GetInt(); // unsigned stackSize = m_pBuffer->GetUnsignedInt(); // pVM->_stack.resize( stackSize ); // for( i = 0; i < pVM->_stack.size(); i++ ) // { // ReadObject( pVM->_stack[i] ); // } // stackSize = m_pBuffer->GetUnsignedInt(); // for( i = 0; i < pVM->_vargsstack.size(); i++ ) // { // ReadObject( pVM->_vargsstack[i] ); // } // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- // SQTable *ReadTable() // { // SQTable *pOld; // SQTable *pTable; // // if ( !BeginRead( &pOld, &pTable ) ) // { // return pTable; // } // // pTable = SQTable::Create(_ss(m_hVM), 0); // // MapPtr( pOld, pTable ); // // if ( m_pBuffer->GetInt() ) // { // SQObjectPtr delegate; // ReadObject( delegate ); // pTable->SetDelegate( delegate._unVal.pTable ); // } // else // { // pTable->_delegate = NULL; // } // int n = m_pBuffer->GetInt(); // while ( n-- ) // { // SQObjectPtr key, value; // ReadObject( key ); // if ( !ReadObject( value, ( key._type == OT_STRING ) ? key._unVal.pString->_val : NULL ) ) // { // DevMsg( "Failed to read Python table entry %s", ( key._type == OT_STRING ) ? key._unVal.pString->_val : SQTypeToString( key._type ) ); // } // if ( key._type != OT_NULL ) // { // pTable->NewSlot( key, value ); // } // } // return pTable; // } // // //------------------------------------------------------------- // // // //------------------------------------------------------------- //SQArray *ReadArray() //{ // SQArray *pOld; // SQArray *pArray; // if ( !BeginRead( &pOld, &pArray ) ) // { // return pArray; // } // pArray = SQArray::Create(_ss(m_hVM), 0); // MapPtr( pOld, pArray ); // int n = m_pBuffer->GetInt(); // pArray->Reserve( n ); // while ( n-- ) // { // SQObjectPtr value; // ReadObject( value ); // pArray->Append( value ); // } // return pArray; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQClass *ReadClass() //{ // SQClass *pOld; // SQClass *pClass; // if ( !BeginRead( &pOld, &pClass ) ) // { // return pClass; // } // SQClass *pBase = NULL; // bool bIsNative = !!m_pBuffer->GetInt(); // // If it's not a C++ defined type... // if ( !bIsNative ) // { // if ( m_pBuffer->GetInt() ) // { // SQObjectPtr base; // ReadObject( base ); // pBase = base._unVal.pClass; // } // SQClass *pClass = SQClass::Create( _ss(m_hVM), pBase ); // MapPtr( pOld, pClass ); // SQObjectPtr members; // ReadObject( members ); // pClass->_members->Release(); // pClass->_members = members._unVal.pTable; // __ObjAddRef( members._unVal.pTable ); // ReadObject( pClass->_attributes ); // unsigned i, n; // n = m_pBuffer->GetUnsignedInt(); // pClass->_defaultvalues.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pClass->_defaultvalues[i].val); // ReadObject(pClass->_defaultvalues[i].attrs); // } // n = m_pBuffer->GetUnsignedInt(); // pClass->_methods.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pClass->_methods[i].val); // ReadObject(pClass->_methods[i].attrs); // } // n = m_pBuffer->GetUnsignedInt(); // pClass->_metamethods.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pClass->_metamethods[i]); // } // return pClass; // } // else // { // char *pszName = (char *)stackalloc( 1024 ); // m_pBuffer->GetString( pszName, 1024 ); // pszName[1023] = 0; // SQObjectPtr value; // if ( m_hVM->_roottable._unVal.pTable->Get( SQString::Create( _ss(m_hVM ), pszName ), value ) && sq_isclass( value ) ) // { // MapPtr( pOld, value._unVal.pClass ); // return value._unVal.pClass; // } // MapPtr( pOld, NULL ); // } // return NULL; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQInstance *ReadInstance() //{ // SQInstance *pOld; // SQInstance *pInstance; // if ( !BeginRead( &pOld, &pInstance ) ) // { // return pInstance; // } // SQObjectPtr pClass; // ReadObject( pClass ); // unsigned i, n; // if ( pClass._unVal.pClass ) // { // pInstance = SQInstance::Create( _ss(m_hVM), pClass._unVal.pClass ); // n = m_pBuffer->GetUnsignedInt(); // for ( i = 0; i < n; i++ ) // { // ReadObject(pInstance->_values[i]); // } // m_pBuffer->GetPtr(); // ignored in this path // if ( pInstance->_class->_typetag ) // { // if ( pInstance->_class->_typetag == TYPETAG_VECTOR ) // { // Vector *pValue = new Vector; // pValue->x = m_pBuffer->GetFloat(); // pValue->y = m_pBuffer->GetFloat(); // pValue->z = m_pBuffer->GetFloat(); // pInstance->_userpointer = pValue; // } // else // { // InstanceContext_t *pContext = new InstanceContext_t; // pContext->pInstance = NULL; // ReadObject( pContext->name ); // pContext->pClassDesc = (ScriptClassDesc_t *)( pInstance->_class->_typetag ); // void *pOldInstance = m_pBuffer->GetPtr(); // if ( sq_isstring(pContext->name) ) // { // char *pszName = pContext->name._unVal.pString->_val; // if ( pContext->pClassDesc->pHelper ) // { // HPYOBJECT *pInstanceHandle = new HPYOBJECT; // pInstanceHandle->_type = OT_INSTANCE; // pInstanceHandle->_unVal.pInstance = pInstance; // pContext->pInstance = pContext->pClassDesc->pHelper->BindOnRead( (HSCRIPT)pInstanceHandle, pOldInstance, pszName ); // if ( pContext->pInstance ) // { // pInstance->_uiRef++; // sq_addref( m_hVM, pInstanceHandle ); // pInstance->_uiRef--; // } // else // { // delete pInstanceHandle; // } // } // if ( !pContext->pInstance ) // { // // Look for a match in the current root table // HPYOBJECT hExistingObject = LookupObject( pszName, NULL, false ); // if ( sq_isinstance(hExistingObject) && hExistingObject._unVal.pInstance->_class == pInstance->_class ) // { // delete pInstance; // return hExistingObject._unVal.pInstance; // } // pContext->pInstance = NULL; // } // } // pInstance->_userpointer = pContext; // } // } // else // { // Verify( m_pBuffer->GetInt() == OT_USERPOINTER ); // pInstance->_userpointer = ReadUserPointer(); // Assert( pInstance->_userpointer == NULL ); // } // MapPtr( pOld, pInstance ); // } // else // { // MapPtr( pOld, NULL ); // n = m_pBuffer->GetUnsignedInt(); // for ( i = 0; i < n; i++ ) // { // SQObjectPtr ignored; // ReadObject(ignored); // } // void *pOldTypeTag = m_pBuffer->GetPtr(); // ignored in this path // if ( pOldTypeTag ) // { // if ( pOldTypeTag == TYPETAG_VECTOR ) // { // m_pBuffer->GetFloat(); // m_pBuffer->GetFloat(); // m_pBuffer->GetFloat(); // } // else // { // SQObjectPtr ignored; // ReadObject( ignored ); // m_pBuffer->GetPtr(); // } // } // else // { // Verify( m_pBuffer->GetInt() == OT_USERPOINTER ); // ReadUserPointer(); // } // pInstance = NULL; // } // return pInstance; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQGenerator *ReadGenerator() //{ // SQGenerator *pOld; // SQGenerator *pGenerator; // if ( !BeginRead( &pOld, &pGenerator ) ) // { // return pGenerator; // } // SQObjectPtr closure; // ReadObject( closure ); // pGenerator = SQGenerator::Create( _ss(m_hVM), closure._unVal.pClosure ); // MapPtr( pOld, pGenerator ); // unsigned i, n; // n = m_pBuffer->GetUnsignedInt(); // pGenerator->_stack.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pGenerator->_stack[i]); // } // n = m_pBuffer->GetUnsignedInt(); // pGenerator->_vargsstack.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pGenerator->_vargsstack[i]); // } // return pGenerator; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQClosure *ReadClosure() //{ // SQClosure *pOld; // SQClosure *pClosure; // if ( !BeginRead( &pOld, &pClosure ) ) // { // return pClosure; // } // SQObjectPtr proto; // ReadObject( proto ); // pClosure = SQClosure::Create( _ss(m_hVM), proto._unVal.pFunctionProto ); // MapPtr( pOld, pClosure ); // ReadObject( pClosure->_env ); // unsigned i, n; // n = m_pBuffer->GetUnsignedInt(); // pClosure->_outervalues.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pClosure->_outervalues[i]); // } // n = m_pBuffer->GetUnsignedInt(); // pClosure->_defaultparams.resize( n ); // for ( i = 0; i < n; i++ ) // { // ReadObject(pClosure->_defaultparams[i]); // } // return pClosure; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQNativeClosure *ReadNativeClosure() //{ // SQNativeClosure *pOld; // SQNativeClosure *pClosure; // if ( !BeginRead( &pOld, &pClosure ) ) // { // return pClosure; // } // SQObjectPtr name; // ReadObject( name ); // SQObjectPtr value; // if ( m_hVM->_roottable._unVal.pTable->Get( name, value ) && sq_isnativeclosure(value) ) // { // MapPtr( pOld, value._unVal.pNativeClosure ); // return value._unVal.pNativeClosure; // } // MapPtr( pOld, NULL ); // return NULL; // @TBD [4/15/2008 tom] //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQUserData *ReadUserData() //{ // m_pBuffer->GetPtr(); // return NULL; // @TBD [4/15/2008 tom] //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQUserPointer *ReadUserPointer() //{ // m_pBuffer->GetPtr(); // return NULL; // @TBD [4/15/2008 tom] //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //static SQInteger SqReadFunc(SQUserPointer up,SQUserPointer data, SQInteger size) // { // CPythonVM *pThis = (CPythonVM *)up; // pThis->m_pBuffer->Get( data, size ); // return size; // } // ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQFunctionProto *ReadFuncProto() //{ // SQFunctionProto *pOld; // SQFunctionProto *pResult; // if ( !BeginRead( &pOld, &pResult ) ) // { // return pResult; // } // SQObjectPtr result; // SQFunctionProto::Load( m_hVM, this, &SqReadFunc, result ); // pResult = result._unVal.pFunctionProto; // pResult->_uiRef++; // result.Null(); // pResult->_uiRef--; // MapPtr( pOld, pResult ); // return pResult; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //SQWeakRef *ReadWeakRef( ) //{ // SQObjectPtr obj; // ReadObject( obj ); // if ( !obj._unVal.pRefCounted ) // { // return NULL; // } // // Need to up ref count if read order has weak ref loading first // Assert( ISREFCOUNTED(obj._type) ); // SQRefCounted *pRefCounted = obj._unVal.pRefCounted; // pRefCounted->_uiRef++; // SQWeakRef *pResult = obj._unVal.pRefCounted->GetWeakRef( obj._type ); // obj.Null(); // pRefCounted->_uiRef--; // return pResult; //} ////------------------------------------------------------------- //// ////------------------------------------------------------------- //bool FindKeyForObject( const SQObjectPtr &table, void *p, SQObjectPtr &key ) //{ // SQTable *pTable = table._unVal.pTable; // int len = pTable->_numofnodes; // for(int i = 0; i < len; i++) // { // if ( pTable->_nodes[i].val._unVal.pUserPointer == p ) // { // key = pTable->_nodes[i].key; // return true; // } // if ( sq_istable( pTable->_nodes[i].val ) ) // { // if ( FindKeyForObject( pTable->_nodes[i].val, p, key ) ) // { // return true; // } // } // } // return false; //} // add instance object to consistency checker void debugTrackObject( PyObject *pobj ) { #ifdef DEBUG_PY if (m_debugObjCount < 1000) m_debugObjects[m_debugObjCount++] = pobj; #endif // DEBUG_PY } void debugRemoveTrackedObject( PyObject *pobj ) { #ifdef DEBUG_PY for (int i = 0; i < m_debugObjCount; i++) if ( m_debugObjects[i] == pobj ) m_debugObjects[i] = NULL; #endif // DEBUG_PY } CPyScope *m_pRootScope; // __main__ module scope CPyScope *m_pValveScope; // valve module scope public: int m_iMethodDef; int m_iClassDef; void *m_rgpMethodDefs[MAX_VALVE_FUNCTIONS_EXPORTED]; // array of pointers to fixed blocks of memory - passed to python, must not move! void *m_rgpClassDefs[MAX_VALVE_CLASSES_EXPORTED]; bool m_bInitialized; private: char m_szScriptPath[MAX_PATH]; // full path to scripts/vscript directory int64 m_iUniqueIdSerialNumber; float m_TimeStartExecute; int m_debugObjCount; PyObject *m_debugObjects[1000]; #ifdef VPYTHON_TEST ConVar developer; #else ConVarRef developer; #endif CUtlHashFast m_TypeMap; // map pClassDesc to PyTypeObject python type CUtlHashFast m_ClassMap; // map PyTypeObject to pClassDesc // friend class CVPythonSerializer; // // Serialization support // CUtlBuffer *m_pBuffer; // CUtlMap m_PtrMap; }; //------------------------------------------------------------------------- // Serialization and Debug Helpers //------------------------------------------------------------------------- //const char *FieldTypeToString( int type ) //{ // switch( type ) // { // case FIELD_VOID: return "void"; // case FIELD_FLOAT: return "float"; // case FIELD_CSTRING: return "string"; // case FIELD_VECTOR: return "Vector"; // case FIELD_INTEGER: return "int"; // case FIELD_BOOLEAN: return "bool"; // case FIELD_CHARACTER: return "char"; // case FIELD_HSCRIPT: return "handle"; // default: return ""; // } //} //static const char *SQTypeToString( SQObjectType sqType ) //{ // switch( sqType ) // { // case OT_FLOAT: return "FLOAT"; // case OT_INTEGER: return "INTEGER"; // case OT_BOOL: return "BOOL"; // case OT_STRING: return "STRING"; // case OT_NULL: return "NULL"; // case OT_TABLE: return "TABLE"; // case OT_ARRAY: return "ARRAY"; // case OT_CLOSURE: return "CLOSURE"; // case OT_NATIVECLOSURE: return "NATIVECLOSURE"; // case OT_USERDATA: return "USERDATA"; // case OT_GENERATOR: return "GENERATOR"; // case OT_THREAD: return "THREAD"; // case OT_USERPOINTER: return "USERPOINTER"; // case OT_CLASS: return "CLASS"; // case OT_INSTANCE: return "INSTANCE"; // case OT_WEAKREF: return "WEAKREF"; // } // return ""; //} //---------------------------------------------------------------------------------- // return true if interpreter init is finalized - block attempts at re-init. //---------------------------------------------------------------------------------- inline bool VMInitFinalized( void ) { return ((CPythonVM *)g_pVm)->m_bInitialized; } //---------------------------------------------------------------------------------- // create interpreter singleton and save pointer to interface in g_pVm //---------------------------------------------------------------------------------- IScriptVM *ScriptCreatePythonVM() { if ( !g_pVm ) { g_pVm = new CPythonVM; } else { // set semaphore to block more than one init of interpreter, // this blocks registration of any more classes or functions, but not of instances ((CPythonVM *)g_pVm)->m_bInitialized = true; } return (IScriptVM *)g_pVm; } //---------------------------------------------------------------------------------- // called after shutdown() on restart or level load or exit // NOTE: we should not actually kill the interpreter unless it's a full game exit. // UNDONE: we don't cleanly shut down the interpeter on game exit. //---------------------------------------------------------------------------------- void ScriptDestroyPythonVM( IScriptVM *pVm ) { // UNDONE: ivscript interface needs a flag to indicate game exit - the only case we should actually kill the interpreter bool bGameExit = false; if ( bGameExit ) { // release memory associated with dynamically allocated data CPythonVM *pPythonVM = assert_cast( pVm ); if (1) { // UNDONE: ONE shutdown per game session only! // whether these are allocated using new/delete, python PyObject_Malloc/Free, // this dealloc causes the next Py_Initialize/Py_Finalize to fail intermittently (ie on restart). // Python documentation indicates that Py_Finalize does NOT reliably release all allocated resources, // and users of various versions report problems with Py_Finalize followed by a subsequent // Py_Initialize. int i; for (i = 0; i < pPythonVM->m_iMethodDef; i++) { PyMem_Free( pPythonVM->m_rgpMethodDefs[i] ); } for (i = 0; i < pPythonVM->m_iClassDef; i++) { PyMem_Free( pPythonVM->m_rgpClassDefs[i] ); } pPythonVM->m_iMethodDef = 0; pPythonVM->m_iClassDef = 0; } if (1) // TEST2: delete pPythonVM; } } //------------------------------------------------------------------- // C function & method call proxies: // Do a bunch of proxy work to expose server-side functions/methods // with their specific bindings to python. // // Since python does not support // specifying user data along with c-defined function/method callbacks, // and we need specific python->c binding data for each function/method callback, // we have to create unique proxy functions for every callback. // // Each unique proxy gets the correct binding info for the function, // then calls the general TranslateCall routine to do the dispatch. //------------------------------------------------------------------- //------------------------------------------------------------------ // define a static callback proxy - python calls this back to access // a function or method definition of a valve class //----------------------------------------------------------------- #define DPX(N) \ static PyObject *Translate_##N( PyObject *pSelf, PyObject *pArgs ) \ { \ ScriptFunctionBinding_t *pBinding = GetProxyBinding( ##N ); \ return ( CPythonVM::TranslateCall( pBinding, (scriptClassInstance_t *)pSelf, pArgs ) ); \ } \ // set proxy definition #define SPX(N) g_proxies[##N].pfn = Translate_##N; static int g_proxyid = 0; static int GetNewProxyId( void ) { int proxyId = g_proxyid; Assert ( proxyId < MAX_VALVE_FUNCTIONS_EXPORTED ); // if this fails, just need to bump up MAX_VALVE_FUNCTIONS_EXPORTED and add more DPX(n),SPX(n) entries g_proxyid++; return proxyId; } // associate the function and/or class binding with the proxy function id static void SetProxyBinding( int proxyId, ScriptFunctionBinding_t *pBinding ) { Assert( proxyId < MAX_VALVE_FUNCTIONS_EXPORTED ); // if this fails, just need to bump up MAX_VALVE_FUNCTIONS_EXPORTED and add more DPX(n) entries g_proxies[proxyId].pBinding = pBinding; } static PyCFunction GetProxyFunction( int proxyId ) { Assert( proxyId < MAX_VALVE_FUNCTIONS_EXPORTED ); // invalid proxyId return g_proxies[proxyId].pfn; } // retrieve the function binding, given the proxy index static ScriptFunctionBinding_t *GetProxyBinding( int proxyId ) { Assert( proxyId < MAX_VALVE_FUNCTIONS_EXPORTED ); // invalid proxyId return ( g_proxies[proxyId].pBinding ); } DPX(0) DPX(1) DPX(2) DPX(3) DPX(4) DPX(5) DPX(6) DPX(7) DPX(8) DPX(9) DPX(10) DPX(11) DPX(12) DPX(13) DPX(14) DPX(15) DPX(16) DPX(17) DPX(18) DPX(19) DPX(20) DPX(21) DPX(22) DPX(23) DPX(24) DPX(25) DPX(26) DPX(27) DPX(28) DPX(29) DPX(30) DPX(31) DPX(32) DPX(33) DPX(34) DPX(35) DPX(36) DPX(37) DPX(38) DPX(39) DPX(40) DPX(41) DPX(42) DPX(43) DPX(44) DPX(45) DPX(46) DPX(47) DPX(48) DPX(49) DPX(50) DPX(51) DPX(52) DPX(53) DPX(54) DPX(55) DPX(56) DPX(57) DPX(58) DPX(59) DPX(60) DPX(61) DPX(62) DPX(63) DPX(64) DPX(65) DPX(66) DPX(67) DPX(68) DPX(69) DPX(70) DPX(71) DPX(72) DPX(73) DPX(74) DPX(75) DPX(76) DPX(77) DPX(78) DPX(79) DPX(80) DPX(81) DPX(82) DPX(83) DPX(84) DPX(85) DPX(86) DPX(87) DPX(88) DPX(89) DPX(90) DPX(91) DPX(92) DPX(93) DPX(94) DPX(95) DPX(96) DPX(97) DPX(98) DPX(99) DPX(100) DPX(101) DPX(102) DPX(103) DPX(104) DPX(105) DPX(106) DPX(107) DPX(108) DPX(109) DPX(110) DPX(111) DPX(112) DPX(113) DPX(114) DPX(115) DPX(116) DPX(117) DPX(118) DPX(119) DPX(120) DPX(121) DPX(122) DPX(123) DPX(124) DPX(125) DPX(126) DPX(127) DPX(128) DPX(129) DPX(130) DPX(131) DPX(132) DPX(133) DPX(134) DPX(135) DPX(136) DPX(137) DPX(138) DPX(139) DPX(140) DPX(141) DPX(142) DPX(143) DPX(144) DPX(145) DPX(146) DPX(147) DPX(148) DPX(149) DPX(150) DPX(151) DPX(152) DPX(153) DPX(154) DPX(155) DPX(156) DPX(157) DPX(158) DPX(159) DPX(160) DPX(161) DPX(162) DPX(163) DPX(164) DPX(165) DPX(166) DPX(167) DPX(168) DPX(169) DPX(170) DPX(171) DPX(172) DPX(173) DPX(174) DPX(175) DPX(176) DPX(177) DPX(178) DPX(179) DPX(180) DPX(181) DPX(182) DPX(183) DPX(184) DPX(185) DPX(186) DPX(187) DPX(188) DPX(189) DPX(190) DPX(191) DPX(192) DPX(193) DPX(194) DPX(195) DPX(196) DPX(197) DPX(198) DPX(199) DPX(200) DPX(201) DPX(202) DPX(203) DPX(204) DPX(205) DPX(206) DPX(207) DPX(208) DPX(209) DPX(210) DPX(211) DPX(212) DPX(213) DPX(214) DPX(215) DPX(216) DPX(217) DPX(218) DPX(219) DPX(220) DPX(221) DPX(222) DPX(223) DPX(224) DPX(225) DPX(226) DPX(227) DPX(228) DPX(229) DPX(230) DPX(231) DPX(232) DPX(233) DPX(234) DPX(235) DPX(236) DPX(237) DPX(238) DPX(239) DPX(240) DPX(241) DPX(242) DPX(243) DPX(244) DPX(245) DPX(246) DPX(247) DPX(248) DPX(249) DPX(250) DPX(251) DPX(252) DPX(253) DPX(254) DPX(255) DPX(256) DPX(257) DPX(258) DPX(259) static void InitProxyTable( void ) { g_proxyid = 0; SPX(0) SPX(1) SPX(2) SPX(3) SPX(4) SPX(5) SPX(6) SPX(7) SPX(8) SPX(9) SPX(10) SPX(11) SPX(12) SPX(13) SPX(14) SPX(15) SPX(16) SPX(17) SPX(18) SPX(19) SPX(20) SPX(21) SPX(22) SPX(23) SPX(24) SPX(25) SPX(26) SPX(27) SPX(28) SPX(29) SPX(30) SPX(31) SPX(32) SPX(33) SPX(34) SPX(35) SPX(36) SPX(37) SPX(38) SPX(39) SPX(40) SPX(41) SPX(42) SPX(43) SPX(44) SPX(45) SPX(46) SPX(47) SPX(48) SPX(49) SPX(50) SPX(51) SPX(52) SPX(53) SPX(54) SPX(55) SPX(56) SPX(57) SPX(58) SPX(59) SPX(60) SPX(61) SPX(62) SPX(63) SPX(64) SPX(65) SPX(66) SPX(67) SPX(68) SPX(69) SPX(70) SPX(71) SPX(72) SPX(73) SPX(74) SPX(75) SPX(76) SPX(77) SPX(78) SPX(79) SPX(80) SPX(81) SPX(82) SPX(83) SPX(84) SPX(85) SPX(86) SPX(87) SPX(88) SPX(89) SPX(90) SPX(91) SPX(92) SPX(93) SPX(94) SPX(95) SPX(96) SPX(97) SPX(98) SPX(99) SPX(100) SPX(101) SPX(102) SPX(103) SPX(104) SPX(105) SPX(106) SPX(107) SPX(108) SPX(109) SPX(110) SPX(111) SPX(112) SPX(113) SPX(114) SPX(115) SPX(116) SPX(117) SPX(118) SPX(119) SPX(120) SPX(121) SPX(122) SPX(123) SPX(124) SPX(125) SPX(126) SPX(127) SPX(128) SPX(129) SPX(130) SPX(131) SPX(132) SPX(133) SPX(134) SPX(135) SPX(136) SPX(137) SPX(138) SPX(139) SPX(140) SPX(141) SPX(142) SPX(143) SPX(144) SPX(145) SPX(146) SPX(147) SPX(148) SPX(149) SPX(150) SPX(151) SPX(152) SPX(153) SPX(154) SPX(155) SPX(156) SPX(157) SPX(158) SPX(159) SPX(160) SPX(161) SPX(162) SPX(163) SPX(164) SPX(165) SPX(166) SPX(167) SPX(168) SPX(169) SPX(170) SPX(171) SPX(172) SPX(173) SPX(174) SPX(175) SPX(176) SPX(177) SPX(178) SPX(179) SPX(180) SPX(181) SPX(182) SPX(183) SPX(184) SPX(185) SPX(186) SPX(187) SPX(188) SPX(189) SPX(190) SPX(191) SPX(192) SPX(193) SPX(194) SPX(195) SPX(196) SPX(197) SPX(198) SPX(199) SPX(200) SPX(201) SPX(202) SPX(203) SPX(204) SPX(205) SPX(206) SPX(207) SPX(208) SPX(209) SPX(210) SPX(211) SPX(212) SPX(213) SPX(214) SPX(215) SPX(216) SPX(217) SPX(218) SPX(219) SPX(220) SPX(221) SPX(222) SPX(223) SPX(224) SPX(225) SPX(226) SPX(227) SPX(228) SPX(229) SPX(230) SPX(231) SPX(232) SPX(233) SPX(234) SPX(235) SPX(236) SPX(237) SPX(238) SPX(239) SPX(240) SPX(241) SPX(242) SPX(243) SPX(244) SPX(245) SPX(246) SPX(247) SPX(248) SPX(249) SPX(250) SPX(251) SPX(252) SPX(253) SPX(254) SPX(255) SPX(256) SPX(257) SPX(258) SPX(259) } #ifdef VPYTHON_TEST CPythonVM g_PythonVM; IScriptVM *g_pScriptVM = &g_PythonVM; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- #include int main( int argc, const char **argv) { if ( argc < 2 ) { printf( "No script specified" ); return 1; } g_pScriptVM->Init(); const char *pszbuild; pszbuild = Py_GetVersion( ) ; printf("You should be using python 2.5.x. You are using python \"%s\".\n", pszbuild); // run a script without a source file - remote debugger will ask for a source file g_pScriptVM->Run( "import pydevd\n" //"pydevd.settrace(stdoutToServer = True, stderrToServer = True, port = 5678, suspend = True)\n" "from time import time,ctime\n" "print 'Today is',ctime(time())\n" ); //CCycleCount count; //count.Sample(); //RandomSeed( time( NULL ) ^ count.GetMicroseconds() ); ScriptRegisterFunction( g_pScriptVM, RandomFloat, "" ); ScriptRegisterFunction( g_pScriptVM, RandomInt, "" ); if ( argc == 3 && *argv[2] == 'd' ) { g_pScriptVM->ConnectDebugger(); // exec a script that invokes the remote python debugger in eclipse pydev // NOTE: this is the only valid way to get an hFile to use with Py_ calls. FILE * returned from fopen is not compatible. PyObject* pyfile = PyFile_FromString( "debug.py", "r"); FILE* hFile = PyFile_AsFile( pyfile); int ret = PyRun_AnyFile(hFile, "debug.py"); Py_XDECREF(pyfile); } int key; CScriptScope scope; scope.Init( "TestScope" ); do { const char *pszScript = argv[1]; FILE *hFile = fopen( pszScript, "rb" ); if ( !hFile ) { printf( "\"%s\" not found.\n", pszScript ); return 1; } int nFileLen = _filelength( _fileno( hFile ) ); char *pBuf = new char[nFileLen + 1]; fread( pBuf, 1, nFileLen, hFile ); pBuf[nFileLen] = 0; fclose( hFile ); if (1) { printf( "Executing script \"%s\"\n----------------------------------------\n", pszScript ); HSCRIPT hScript = g_pScriptVM->CompileScript( pBuf, ( strrchr( pszScript, '\\' ) ? strrchr( pszScript, '\\' ) + 1 : pszScript ) ); if ( hScript ) { if ( scope.Run( hScript ) != SCRIPT_ERROR ) { printf( "----------------------------------------\n" ); printf("Script complete. Press q to exit, m to dump memory usage, enter to run again.\n"); } else { printf( "----------------------------------------\n" ); printf("Script execution error. Press q to exit, m to dump memory usage, enter to run again.\n"); } g_pScriptVM->ReleaseScript( hScript ); } else { printf( "----------------------------------------\n" ); printf("Script failed to compile. Press q to exit, m to dump memory usage, enter to run again.\n"); } } key = _getch(); // Keypress before exit if ( key == 'm' ) { Msg( "%d\n", g_pMemAlloc->GetSize( NULL ) ); } delete pBuf; } while ( key != 'q' ); scope.Term(); g_pScriptVM->DisconnectDebugger(); g_pScriptVM->Shutdown(); return 0; } #endif