[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [19788] trunk/blender/source/gameengine: BGE Python API
Campbell Barton
ideasman42 at gmail.com
Sun Apr 19 14:46:39 CEST 2009
Revision: 19788
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=19788
Author: campbellbarton
Date: 2009-04-19 14:46:39 +0200 (Sun, 19 Apr 2009)
Log Message:
-----------
BGE Python API
This changes how the BGE classes and Python work together, which hasnt changed since blender went opensource.
The main difference is PyObjectPlus - the base class for most game engine classes, no longer inherit from PyObject, and cannot be cast to a PyObject.
This has the advantage that the BGE does not have to keep 2 reference counts valid for C++ and Python.
Previously C++ classes would never be freed while python held a reference, however this reference could be problematic eg: a GameObject that isnt in a scene anymore should not be used by python, doing so could even crash blender in some cases.
Instead PyObjectPlus has a member "PyObject *m_proxy" which is lazily initialized when python needs it. m_proxy reference counts are managed by python, though it should never be freed while the C++ class exists since it holds a reference to avoid making and freeing it all the time.
When the C++ class is free'd it sets the m_proxy reference to NULL, If python accesses this variable it will raise a RuntimeError, (check the isValid attribute to see if its valid without raising an error).
- This replaces the m_zombie bool and IsZombie() tests added recently.
In python return values that used to be..
return value->AddRef();
Are now
return value->GetProxy();
or...
return value->NewProxy(true); // true means python owns this C++ value which will be deleted when the PyObject is freed
Modified Paths:
--------------
trunk/blender/source/gameengine/Converter/BL_BlenderDataConversion.cpp
trunk/blender/source/gameengine/Expressions/InputParser.cpp
trunk/blender/source/gameengine/Expressions/ListValue.cpp
trunk/blender/source/gameengine/Expressions/ListValue.h
trunk/blender/source/gameengine/Expressions/PyObjectPlus.cpp
trunk/blender/source/gameengine/Expressions/PyObjectPlus.h
trunk/blender/source/gameengine/Expressions/Value.cpp
trunk/blender/source/gameengine/Expressions/Value.h
trunk/blender/source/gameengine/GameLogic/SCA_ILogicBrick.cpp
trunk/blender/source/gameengine/GameLogic/SCA_PythonController.cpp
trunk/blender/source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageSensor.cpp
trunk/blender/source/gameengine/Ketsji/KX_BlenderMaterial.cpp
trunk/blender/source/gameengine/Ketsji/KX_CameraActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_GameObject.cpp
trunk/blender/source/gameengine/Ketsji/KX_GameObject.h
trunk/blender/source/gameengine/Ketsji/KX_MeshProxy.cpp
trunk/blender/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp
trunk/blender/source/gameengine/Ketsji/KX_ParentActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_PolyProxy.cpp
trunk/blender/source/gameengine/Ketsji/KX_PolygonMaterial.cpp
trunk/blender/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp
trunk/blender/source/gameengine/Ketsji/KX_PythonInit.cpp
trunk/blender/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
trunk/blender/source/gameengine/Ketsji/KX_RaySensor.cpp
trunk/blender/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_Scene.cpp
trunk/blender/source/gameengine/Ketsji/KX_SceneActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_TouchSensor.cpp
trunk/blender/source/gameengine/Ketsji/KX_TrackToActuator.cpp
trunk/blender/source/gameengine/Ketsji/KX_VehicleWrapper.cpp
trunk/blender/source/gameengine/VideoTexture/FilterSource.h
trunk/blender/source/gameengine/VideoTexture/ImageRender.cpp
Modified: trunk/blender/source/gameengine/Converter/BL_BlenderDataConversion.cpp
===================================================================
--- trunk/blender/source/gameengine/Converter/BL_BlenderDataConversion.cpp 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Converter/BL_BlenderDataConversion.cpp 2009-04-19 12:46:39 UTC (rev 19788)
@@ -2354,7 +2354,7 @@
// Remove the child reference in the local list!
// Note: there may be descendents already if the children of the child were processed
// by this loop before the child. In that case, we must remove the children also
- CListValue* childrenlist = (CListValue*)childobj->PyGetChildrenRecursive(childobj);
+ CListValue* childrenlist = childobj->GetChildrenRecursive();
childrenlist->Add(childobj->AddRef());
for ( i=0;i<childrenlist->GetCount();i++)
{
Modified: trunk/blender/source/gameengine/Expressions/InputParser.cpp
===================================================================
--- trunk/blender/source/gameengine/Expressions/InputParser.cpp 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Expressions/InputParser.cpp 2009-04-19 12:46:39 UTC (rev 19788)
@@ -649,7 +649,7 @@
CExpression* expr = parser.ProcessText(txt);
CValue* val = expr->Calculate();
expr->Release();
- return val;
+ return val->GetProxy();
}
static PyMethodDef CParserMethods[] =
Modified: trunk/blender/source/gameengine/Expressions/ListValue.cpp
===================================================================
--- trunk/blender/source/gameengine/Expressions/ListValue.cpp 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Expressions/ListValue.cpp 2009-04-19 12:46:39 UTC (rev 19788)
@@ -27,45 +27,61 @@
#define Py_ssize_t int
#endif
-Py_ssize_t listvalue_bufferlen(PyObject* list)
+Py_ssize_t listvalue_bufferlen(PyObject* self)
{
- return (Py_ssize_t)( ((CListValue*)list)->GetCount());
+ CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
+ if (list==NULL)
+ return 0;
+
+ return (Py_ssize_t)list->GetCount();
}
-PyObject* listvalue_buffer_item(PyObject* list,Py_ssize_t index)
+PyObject* listvalue_buffer_item(PyObject* self, Py_ssize_t index)
{
- int count = ((CListValue*) list)->GetCount();
+ CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
+ if (list==NULL) {
+ PyErr_SetString(PyExc_IndexError, BGE_PROXY_ERROR_MSG);
+ return NULL;
+ }
+ int count = list->GetCount();
+
if (index < 0)
index = count+index;
if (index >= 0 && index < count)
{
- PyObject* pyobj = ((CListValue*) list)->GetValue(index)->ConvertValueToPython();
+ PyObject* pyobj = list->GetValue(index)->ConvertValueToPython();
if (pyobj)
return pyobj;
else
- return ((CListValue*) list)->GetValue(index)->AddRef();
+ return list->GetValue(index)->GetProxy();
}
PyErr_SetString(PyExc_IndexError, "Python ListIndex out of range");
return NULL;
}
-PyObject* listvalue_mapping_subscript(PyObject* list,PyObject* pyindex)
+PyObject* listvalue_mapping_subscript(PyObject* self, PyObject* pyindex)
{
+ CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
+ if (list==NULL) {
+ PyErr_SetString(PyExc_IndexError, BGE_PROXY_ERROR_MSG);
+ return NULL;
+ }
+
if (PyString_Check(pyindex))
{
STR_String index(PyString_AsString(pyindex));
CValue *item = ((CListValue*) list)->FindValue(index);
if (item)
- return (PyObject*) item;
+ return item->GetProxy();
}
if (PyInt_Check(pyindex))
{
int index = PyInt_AsLong(pyindex);
- return listvalue_buffer_item(list, index);
+ return listvalue_buffer_item(self, index);
}
PyObject *pyindex_str = PyObject_Repr(pyindex); /* new ref */
@@ -76,10 +92,16 @@
/* just slice it into a python list... */
-PyObject* listvalue_buffer_slice(PyObject* list,Py_ssize_t ilow, Py_ssize_t ihigh)
+PyObject* listvalue_buffer_slice(PyObject* self,Py_ssize_t ilow, Py_ssize_t ihigh)
{
+ CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
+ if (list==NULL) {
+ PyErr_SetString(PyExc_IndexError, BGE_PROXY_ERROR_MSG);
+ return NULL;
+ }
+
int i, j;
- PyListObject *newlist;
+ PyObject *newlist;
if (ilow < 0) ilow = 0;
@@ -90,18 +112,18 @@
if (ihigh < ilow)
ihigh = ilow;
- newlist = (PyListObject *) PyList_New(ihigh - ilow);
+ newlist = PyList_New(ihigh - ilow);
if (!newlist)
return NULL;
for (i = ilow, j = 0; i < ihigh; i++, j++)
{
- PyObject* pyobj = ((CListValue*) list)->GetValue(i)->ConvertValueToPython();
+ PyObject* pyobj = list->GetValue(i)->ConvertValueToPython();
if (!pyobj)
- pyobj = ((CListValue*) list)->GetValue(i)->AddRef();
- newlist->ob_item[j] = pyobj;
+ pyobj = list->GetValue(i)->GetProxy();
+ PyList_SET_ITEM(newlist, i, pyobj);
}
- return (PyObject *) newlist;
+ return newlist;
}
@@ -109,11 +131,16 @@
static PyObject *
listvalue_buffer_concat(PyObject * self, PyObject * other)
{
+ CListValue *listval= static_cast<CListValue *>(BGE_PROXY_REF(self));
+ if (listval==NULL) {
+ PyErr_SetString(PyExc_IndexError, BGE_PROXY_ERROR_MSG);
+ return NULL;
+ }
+
// for now, we support CListValue concatenated with items
// and CListValue concatenated to Python Lists
// and CListValue concatenated with another CListValue
-
- CListValue* listval = (CListValue*) self;
+
listval->AddRef();
if (other->ob_type == &PyList_Type)
{
@@ -519,8 +546,8 @@
int numelem = GetCount();
for (int i=0;i<numelem;i++)
{
- if (reinterpret_cast<BGE_ID_TYPE>(static_cast<PyObject*>(m_pValueArray[i])) == id)
- return GetValue(i);
+ if (reinterpret_cast<BGE_ID_TYPE>(m_pValueArray[i]->m_proxy) == id)
+ return GetValue(i)->GetProxy();
}
PyErr_SetString(PyExc_IndexError, "from_id(#), id not found in CValueList");
Modified: trunk/blender/source/gameengine/Expressions/ListValue.h
===================================================================
--- trunk/blender/source/gameengine/Expressions/ListValue.h 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Expressions/ListValue.h 2009-04-19 12:46:39 UTC (rev 19788)
@@ -61,9 +61,11 @@
virtual PyObject* py_getattro(PyObject* attr);
virtual PyObject* py_repr(void) {
- PyObject *py_list= PySequence_List((PyObject *)this);
+ PyObject *py_proxy= this->GetProxy();
+ PyObject *py_list= PySequence_List(py_proxy);
PyObject *py_string= PyObject_Repr(py_list);
Py_DECREF(py_list);
+ Py_DECREF(py_proxy);
return py_string;
}
Modified: trunk/blender/source/gameengine/Expressions/PyObjectPlus.cpp
===================================================================
--- trunk/blender/source/gameengine/Expressions/PyObjectPlus.cpp 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Expressions/PyObjectPlus.cpp 2009-04-19 12:46:39 UTC (rev 19788)
@@ -54,6 +54,7 @@
* PyObjectPlus Type -- Every class, even the abstract one should have a Type
------------------------------*/
+
PyTypeObject PyObjectPlus::Type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
@@ -74,21 +75,33 @@
Methods
};
+
PyObjectPlus::~PyObjectPlus()
{
- if (ob_refcnt)
- {
- _Py_ForgetReference(this);
+ if(m_proxy) {
+ Py_DECREF(m_proxy); /* Remove own reference, python may still have 1 */
+ BGE_PROXY_REF(m_proxy)= NULL;
}
// assert(ob_refcnt==0);
}
+void PyObjectPlus::PyDestructor(PyObject *self) // python wrapper
+{
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus) {
+ if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
+ delete self_plus;
+ }
+
+ BGE_PROXY_REF(self)= NULL; // not really needed
+ }
+ PyObject_DEL( self );
+};
+
PyObjectPlus::PyObjectPlus(PyTypeObject *T) // constructor
{
MT_assert(T != NULL);
- this->ob_type = T;
- _Py_NewReference(this);
- SetZombie(false);
+ m_proxy= NULL;
};
/*------------------------------
@@ -131,7 +144,7 @@
if (PyCObject_Check(descr)) {
return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr));
} else if (descr->ob_type->tp_descr_get) {
- return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, (PyObject *)this);
+ return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy);
} else {
fprintf(stderr, "Unknown attribute type (PyObjectPlus::py_getattro)");
return descr;
@@ -794,5 +807,47 @@
return pydict;
}
+
+
+PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp)
+{
+ if (self->m_proxy==NULL)
+ {
+ self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
+ BGE_PROXY_PYOWNS(self->m_proxy) = false;
+ }
+ //PyObject_Print(self->m_proxy, stdout, 0);
+ //printf("ref %d\n", self->m_proxy->ob_refcnt);
+
+ BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
+ Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
+ return self->m_proxy;
+}
+
+PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns)
+{
+ if (self->m_proxy)
+ {
+ if(py_owns)
+ { /* Free */
+ BGE_PROXY_REF(self->m_proxy) = NULL;
+ Py_DECREF(self->m_proxy);
+ self->m_proxy= NULL;
+ }
+ else {
+ Py_INCREF(self->m_proxy);
+ return self->m_proxy;
+ }
+
+ }
+
+ GetProxy_Ext(self, tp);
+ if(py_owns) {
+ BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
+ Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
+ }
+ return self->m_proxy;
+}
+
#endif //NO_EXP_PYTHON_EMBEDDING
Modified: trunk/blender/source/gameengine/Expressions/PyObjectPlus.h
===================================================================
--- trunk/blender/source/gameengine/Expressions/PyObjectPlus.h 2009-04-19 12:26:31 UTC (rev 19787)
+++ trunk/blender/source/gameengine/Expressions/PyObjectPlus.h 2009-04-19 12:46:39 UTC (rev 19788)
@@ -81,6 +81,20 @@
exit(-1);
};
+typedef struct {
+ PyObject_HEAD /* required python macro */
+ class PyObjectPlus *ref;
+ bool py_owns;
+} PyObjectPlus_Proxy;
+
+#define BGE_PROXY_ERROR_MSG "Blender Game Engine data has been freed, cannot use this python variable"
+#define BGE_PROXY_REF(_self) (((PyObjectPlus_Proxy *)_self)->ref)
+#define BGE_PROXY_PYOWNS(_self) (((PyObjectPlus_Proxy *)_self)->py_owns)
+
+/* Note, sometimes we dont care what BGE type this is as long as its a proxy */
+#define BGE_PROXY_CHECK_TYPE(_self) ((_self)->ob_type->tp_dealloc == PyDestructor)
+
+
// This must be the first line of each
// PyC++ class
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list