Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4969 lines
146 KiB

//========== Copyright <EFBFBD> 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 <stdio.h>
#include <io.h>
#include <conio.h>
#include <direct.h>
#define _HAS_EXCEPTIONS 0
#include <string>
#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 <crtdbg.h> // 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("<Vector : null>");
}
return PyString_FromString( (static_cast<const char *>(CFmtStr("<Vector: %f %f %f>", 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<const char *>(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 + <gamename>/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__", "<synthetic>");
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<ScriptVariant_t, 14> 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 += "<unnamed>";
// }
// 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 <typename T>
// 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<PyTypeObject *, CUtlHashFastGenericHash> m_TypeMap; // map pClassDesc to PyTypeObject python type
CUtlHashFast<ScriptClassDesc_t *, CUtlHashFastGenericHash> m_ClassMap; // map PyTypeObject to pClassDesc
// friend class CVPythonSerializer;
// // Serialization support
// CUtlBuffer *m_pBuffer;
// CUtlMap<void *, void *> 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 "<unknown>";
// }
//}
//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 "<unknown>";
//}
//----------------------------------------------------------------------------------
// 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<CPythonVM *>( 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 <time.h>
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