BGE Python API
Support for assigning any Type to a KX_GameObject so you can do... gameOb.follow = otherGameOb gameOb[otherGameOb] = distanceTo gameOb["path"] = [(x,y,x), (x,y,x)] del gameOb[mesh] * types that cannot be converted into CValue types are written into the KX_GameObject dict * the KX_GameObject dict is only initialized when needed * Python properties in this dict cannot be accessed by logic bricks * dir(ob) and ob.getPropertyNames() return items from both CValue and Py dictionary properties. Also found that CType was converting python lists to CType Lists but very buggy, would crash after printing the list most times. Use python lists instead since logic bricks dont deal with lists.
This commit is contained in:
@@ -130,9 +130,10 @@ static inline void Py_Fatal(const char *M) {
|
||||
* was because the attribute didnt exits of if there was some problem setting the value
|
||||
*/
|
||||
|
||||
#define PY_SET_ATTR_COERCE_FAIL 2
|
||||
#define PY_SET_ATTR_FAIL 1
|
||||
#define PY_SET_ATTR_MISSING -1
|
||||
#define PY_SET_ATTR_SUCCESS 0
|
||||
#define PY_SET_ATTR_MISSING -1
|
||||
#define PY_SET_ATTR_SUCCESS 0
|
||||
|
||||
#define py_setattro_up(Parent) \
|
||||
PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \
|
||||
|
||||
@@ -719,7 +719,8 @@ CValue* CValue::ConvertPythonToValue(PyObject* pyobj)
|
||||
{
|
||||
|
||||
CValue* vallie = NULL;
|
||||
|
||||
/* refcounting is broking here! - this crashes anyway, just store a python list for KX_GameObject */
|
||||
#if 0
|
||||
if (PyList_Check(pyobj))
|
||||
{
|
||||
CListValue* listval = new CListValue();
|
||||
@@ -750,6 +751,7 @@ CValue* CValue::ConvertPythonToValue(PyObject* pyobj)
|
||||
}
|
||||
|
||||
} else
|
||||
#endif
|
||||
if (PyFloat_Check(pyobj))
|
||||
{
|
||||
vallie = new CFloatValue( (float)PyFloat_AsDouble(pyobj) );
|
||||
@@ -790,21 +792,34 @@ int CValue::py_delattro(PyObject *attr)
|
||||
|
||||
int CValue::py_setattro(PyObject *attr, PyObject* pyobj)
|
||||
{
|
||||
char *attr_str= PyString_AsString(attr);
|
||||
CValue* oldprop = GetProperty(attr_str);
|
||||
|
||||
CValue* vallie = ConvertPythonToValue(pyobj);
|
||||
if (vallie)
|
||||
{
|
||||
char *attr_str= PyString_AsString(attr);
|
||||
CValue* oldprop = GetProperty(attr_str);
|
||||
|
||||
if (oldprop)
|
||||
oldprop->SetValue(vallie);
|
||||
else
|
||||
SetProperty(attr_str, vallie);
|
||||
|
||||
vallie->Release();
|
||||
} else
|
||||
{
|
||||
return PY_SET_ATTR_FAIL; /* ConvertPythonToValue sets the error message */
|
||||
}
|
||||
else {
|
||||
// ConvertPythonToValue sets the error message
|
||||
// must return missing so KX_GameObect knows this
|
||||
// attribute was not a function or bult in attribute,
|
||||
//
|
||||
// CValue attributes override internal attributes
|
||||
// so if it exists as a CValue attribute already,
|
||||
// assume your trying to set it to a differnt CValue attribute
|
||||
// otherwise return PY_SET_ATTR_MISSING so children
|
||||
// classes know they can set it without conflict
|
||||
|
||||
if (GetProperty(attr_str))
|
||||
return PY_SET_ATTR_COERCE_FAIL; /* failed to set an existing attribute */
|
||||
else
|
||||
return PY_SET_ATTR_MISSING; /* allow the KX_GameObject dict to set */
|
||||
}
|
||||
|
||||
//PyObjectPlus::py_setattro(attr,value);
|
||||
|
||||
@@ -97,7 +97,8 @@ KX_GameObject::KX_GameObject(
|
||||
m_pPhysicsEnvironment(NULL),
|
||||
m_xray(false),
|
||||
m_pHitObject(NULL),
|
||||
m_isDeformable(false)
|
||||
m_isDeformable(false),
|
||||
m_attrlist(NULL)
|
||||
{
|
||||
m_ignore_activity_culling = false;
|
||||
m_pClient_info = new KX_ClientObjectInfo(this, KX_ClientObjectInfo::ACTOR);
|
||||
@@ -139,6 +140,10 @@ KX_GameObject::~KX_GameObject()
|
||||
{
|
||||
delete m_pGraphicController;
|
||||
}
|
||||
|
||||
if (m_attrlist) {
|
||||
Py_DECREF(m_attrlist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,6 +328,9 @@ void KX_GameObject::ProcessReplica(KX_GameObject* replica)
|
||||
replica->m_pClient_info = new KX_ClientObjectInfo(*m_pClient_info);
|
||||
replica->m_pClient_info->m_gameobject = replica;
|
||||
replica->m_state = 0;
|
||||
if(m_attrlist)
|
||||
replica->m_attrlist= PyDict_Copy(m_attrlist);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1138,69 +1146,124 @@ PyObject* KX_GameObject::PyGetPosition(PyObject* self)
|
||||
|
||||
Py_ssize_t KX_GameObject::Map_Len(PyObject* self_v)
|
||||
{
|
||||
return (static_cast<KX_GameObject*>(self_v))->GetPropertyCount();
|
||||
KX_GameObject* self= static_cast<KX_GameObject*>(self_v);
|
||||
Py_ssize_t len= self->GetPropertyCount();
|
||||
if(self->m_attrlist)
|
||||
len += PyDict_Size(self->m_attrlist);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
PyObject *KX_GameObject::Map_GetItem(PyObject *self_v, PyObject *item)
|
||||
{
|
||||
KX_GameObject* self= static_cast<KX_GameObject*>(self_v);
|
||||
const char *attr= PyString_AsString(item);
|
||||
const char *attr_str= PyString_AsString(item);
|
||||
CValue* resultattr;
|
||||
PyObject* pyconvert;
|
||||
|
||||
|
||||
if(attr==NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "KX_GameObject key but a string");
|
||||
/* first see if the attributes a string and try get the cvalue attribute */
|
||||
if(attr_str && (resultattr=self->GetProperty(attr_str))) {
|
||||
pyconvert = resultattr->ConvertValueToPython();
|
||||
return pyconvert ? pyconvert:resultattr;
|
||||
}
|
||||
/* no CValue attribute, try get the python only m_attrlist attribute */
|
||||
else if (self->m_attrlist && (pyconvert=PyDict_GetItem(self->m_attrlist, item))) {
|
||||
|
||||
if (attr_str)
|
||||
PyErr_Clear();
|
||||
Py_INCREF(pyconvert);
|
||||
return pyconvert;
|
||||
}
|
||||
else {
|
||||
if(attr_str) PyErr_Format(PyExc_KeyError, "KX_GameObject key \"%s\" does not exist", attr_str);
|
||||
else PyErr_SetString(PyExc_KeyError, "KX_GameObject key does not exist");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resultattr = self->GetProperty(attr);
|
||||
|
||||
if(resultattr==NULL) {
|
||||
PyErr_SetString(PyExc_KeyError, "KX_GameObject key does not exist");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pyconvert = resultattr->ConvertValueToPython();
|
||||
|
||||
return pyconvert ? pyconvert:resultattr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int KX_GameObject::Map_SetItem(PyObject *self_v, PyObject *key, PyObject *val)
|
||||
{
|
||||
KX_GameObject* self= static_cast<KX_GameObject*>(self_v);
|
||||
const char *attr= PyString_AsString(key);
|
||||
|
||||
if(attr==NULL) {
|
||||
PyErr_SetString(PyExc_TypeError, "KX_GameObject key but a string");
|
||||
return 1;
|
||||
}
|
||||
const char *attr_str= PyString_AsString(key);
|
||||
if(attr_str==NULL)
|
||||
PyErr_Clear();
|
||||
|
||||
if (val==NULL) { /* del ob["key"] */
|
||||
if (self->RemoveProperty(attr)==false) {
|
||||
PyErr_Format(PyExc_KeyError, "KX_GameObject key \"%s\" not found", attr);
|
||||
return 1;
|
||||
int del= 0;
|
||||
|
||||
/* try remove both just incase */
|
||||
if(attr_str)
|
||||
del |= (self->RemoveProperty(attr_str)==true) ? 1:0;
|
||||
|
||||
if(self->m_attrlist)
|
||||
del |= (PyDict_DelItem(self->m_attrlist, key)==0) ? 1:0;
|
||||
|
||||
if (del==0) {
|
||||
if(attr_str) PyErr_Format(PyExc_KeyError, "KX_GameObject key \"%s\" not found", attr_str);
|
||||
else PyErr_SetString(PyExc_KeyError, "KX_GameObject key not found");
|
||||
return -1;
|
||||
}
|
||||
else if (self->m_attrlist) {
|
||||
PyErr_Clear(); /* PyDict_DelItem sets an error when it fails */
|
||||
}
|
||||
}
|
||||
else { /* ob["key"] = value */
|
||||
CValue* vallie = self->ConvertPythonToValue(val);
|
||||
int set= 0;
|
||||
|
||||
if(vallie==NULL)
|
||||
return 1; /* ConvertPythonToValue sets the error */
|
||||
/* as CValue */
|
||||
if(attr_str)
|
||||
{
|
||||
CValue* vallie = self->ConvertPythonToValue(val);
|
||||
|
||||
if(vallie)
|
||||
{
|
||||
CValue* oldprop = self->GetProperty(attr_str);
|
||||
|
||||
if (oldprop)
|
||||
oldprop->SetValue(vallie);
|
||||
else
|
||||
self->SetProperty(attr_str, vallie);
|
||||
|
||||
vallie->Release();
|
||||
set= 1;
|
||||
|
||||
/* try remove dict value to avoid double ups */
|
||||
if (self->m_attrlist){
|
||||
if (PyDict_DelItem(self->m_attrlist, key) != 0)
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
|
||||
CValue* oldprop = self->GetProperty(attr);
|
||||
if(set==0)
|
||||
{
|
||||
if (self->m_attrlist==NULL) /* lazy init */
|
||||
self->m_attrlist= PyDict_New();
|
||||
|
||||
|
||||
if(PyDict_SetItem(self->m_attrlist, key, val)==0)
|
||||
{
|
||||
if(attr_str)
|
||||
self->RemoveProperty(attr_str); /* overwrite the CValue if it exists */
|
||||
set= 1;
|
||||
}
|
||||
else {
|
||||
if(attr_str) PyErr_Format(PyExc_KeyError, "KX_GameObject key \"%s\" not be added to internal dictionary", attr_str);
|
||||
else PyErr_SetString(PyExc_KeyError, "KX_GameObject key not be added to internal dictionary");
|
||||
}
|
||||
}
|
||||
|
||||
if (oldprop)
|
||||
oldprop->SetValue(vallie);
|
||||
else
|
||||
self->SetProperty(attr, vallie);
|
||||
if(set==0)
|
||||
return -1; /* pythons error value */
|
||||
|
||||
vallie->Release();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
|
||||
@@ -1223,9 +1286,11 @@ PyTypeObject KX_GameObject::Type = {
|
||||
0,
|
||||
0,
|
||||
py_base_repr,
|
||||
0,0,0,0,0,0,
|
||||
py_base_getattro,
|
||||
py_base_setattro,
|
||||
0,0,
|
||||
&Mapping,
|
||||
0,0,0,
|
||||
py_base_getattro_gameobject,
|
||||
py_base_setattro_gameobject,
|
||||
0,0,0,0,0,0,0,0,0,
|
||||
Methods
|
||||
};
|
||||
@@ -1533,6 +1598,10 @@ PyObject* KX_GameObject::pyattr_get_dir_dict(void *self_v, const KX_PYATTRIBUTE_
|
||||
|
||||
Py_DECREF(list);
|
||||
|
||||
/* Add m_attrlist if we have it */
|
||||
if(self->m_attrlist)
|
||||
PyDict_Update(dict, self->m_attrlist);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
@@ -2042,7 +2111,17 @@ PyObject* KX_GameObject::PyGetPhysicsId(PyObject* self)
|
||||
|
||||
PyObject* KX_GameObject::PyGetPropertyNames(PyObject* self)
|
||||
{
|
||||
return ConvertKeysToPython();
|
||||
PyObject *list= ConvertKeysToPython();
|
||||
|
||||
if(m_attrlist) {
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
|
||||
while (PyDict_Next(m_attrlist, &pos, &key, &value)) {
|
||||
PyList_Append(list, key);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
KX_PYMETHODDEF_DOC_O(KX_GameObject, getDistanceTo,
|
||||
|
||||
@@ -103,6 +103,26 @@ protected:
|
||||
public:
|
||||
bool m_isDeformable;
|
||||
|
||||
// Python attributes that wont convert into CValue
|
||||
//
|
||||
// there are 2 places attributes can be stored, in the CValue,
|
||||
// where attributes are converted into BGE's CValue types
|
||||
// these can be used with property actuators
|
||||
//
|
||||
// For the python API, For types that cannot be converted into CValues (lists, dicts, GameObjects)
|
||||
// these will be put into "m_attrlist", logic bricks cannot access them.
|
||||
//
|
||||
// rules for setting attributes.
|
||||
//
|
||||
// * there should NEVER be a CValue and a m_attrlist attribute with matching names. get/sets make sure of this.
|
||||
// * if CValue conversion fails, use a PyObject in "m_attrlist"
|
||||
// * when assigning a value, first see if it can be a CValue, if it can remove the "m_attrlist" and set the CValue
|
||||
//
|
||||
|
||||
PyObject* m_attrlist;
|
||||
|
||||
|
||||
|
||||
virtual void /* This function should be virtual - derived classed override it */
|
||||
Relink(
|
||||
GEN_Map<GEN_HashedPtr, void*> *map
|
||||
@@ -768,11 +788,108 @@ public:
|
||||
/**
|
||||
* @section Python interface functions.
|
||||
*/
|
||||
|
||||
|
||||
virtual PyObject* py_getattro(PyObject *attr);
|
||||
virtual int py_setattro(PyObject *attr, PyObject *value); // py_setattro method
|
||||
virtual PyObject* py_repr(void) { return PyString_FromString(GetName().ReadPtr()); }
|
||||
|
||||
/* we need our own getattr and setattr types */
|
||||
/* See m_attrlist definition for rules on how this works */
|
||||
static PyObject *py_base_getattro_gameobject(PyObject * self, PyObject *attr)
|
||||
{
|
||||
PyObject *object= ((KX_GameObject *) self)->py_getattro(attr);
|
||||
|
||||
if (object==NULL && ((KX_GameObject *) self)->m_attrlist) {
|
||||
/* backup the exception incase the attr doesnt exist in the dict either */
|
||||
PyObject *err_type, *err_value, *err_tb;
|
||||
PyErr_Fetch(&err_type, &err_value, &err_tb);
|
||||
|
||||
object= PyDict_GetItem(((KX_GameObject *) self)->m_attrlist, attr);
|
||||
if (object) {
|
||||
Py_INCREF(object);
|
||||
|
||||
PyErr_Clear();
|
||||
Py_XDECREF( err_type );
|
||||
Py_XDECREF( err_value );
|
||||
Py_XDECREF( err_tb );
|
||||
}
|
||||
else {
|
||||
PyErr_Restore(err_type, err_value, err_tb); /* use the error from the parent function */
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
static int py_base_setattro_gameobject(PyObject * self, PyObject *attr, PyObject *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Delete the item */
|
||||
if (value==NULL)
|
||||
{
|
||||
ret= ((PyObjectPlus*) self)->py_delattro(attr);
|
||||
|
||||
if (ret != 0) /* CValue attribute failed, try KX_GameObject m_attrlist dict */
|
||||
{
|
||||
if (((KX_GameObject *) self)->m_attrlist)
|
||||
{
|
||||
/* backup the exception incase the attr doesnt exist in the dict either */
|
||||
PyObject *err_type, *err_value, *err_tb;
|
||||
PyErr_Fetch(&err_type, &err_value, &err_tb);
|
||||
|
||||
if (PyDict_DelItem(((KX_GameObject *) self)->m_attrlist, attr) == 0)
|
||||
{
|
||||
ret= 0;
|
||||
PyErr_Clear();
|
||||
Py_XDECREF( err_type );
|
||||
Py_XDECREF( err_value );
|
||||
Py_XDECREF( err_tb );
|
||||
}
|
||||
else {
|
||||
PyErr_Restore(err_type, err_value, err_tb); /* use the error from the parent function */
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
ret= ((PyObjectPlus*) self)->py_setattro(attr, value);
|
||||
|
||||
if (ret==PY_SET_ATTR_SUCCESS) {
|
||||
/* remove attribute in our own dict to avoid double ups */
|
||||
if (((KX_GameObject *) self)->m_attrlist) {
|
||||
if (PyDict_DelItem(((KX_GameObject *) self)->m_attrlist, attr) != 0)
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (ret==PY_SET_ATTR_COERCE_FAIL) {
|
||||
/* CValue attribute exists, remove and add dict value */
|
||||
((KX_GameObject *) self)->RemoveProperty(STR_String(PyString_AsString(attr)));
|
||||
ret= PY_SET_ATTR_MISSING;
|
||||
}
|
||||
|
||||
if (ret==PY_SET_ATTR_MISSING) {
|
||||
/* Lazy initialization */
|
||||
if (((KX_GameObject *) self)->m_attrlist==NULL)
|
||||
((KX_GameObject *) self)->m_attrlist = PyDict_New();
|
||||
|
||||
if (PyDict_SetItem(((KX_GameObject *) self)->m_attrlist, attr, value)==0) {
|
||||
PyErr_Clear();
|
||||
ret= PY_SET_ATTR_SUCCESS;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_AttributeError, "failed assigning value to KX_GameObject internal dictionary");
|
||||
ret= PY_SET_ATTR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
KX_PYMETHOD_NOARGS(KX_GameObject,GetPosition);
|
||||
KX_PYMETHOD_O(KX_GameObject,SetPosition);
|
||||
KX_PYMETHOD_O(KX_GameObject,SetWorldPosition);
|
||||
|
||||
@@ -603,6 +603,7 @@ public:
|
||||
ret= PY_SET_ATTR_SUCCESS;
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_AttributeError, "failed assigning value to KX_Scenes internal dictionary");
|
||||
ret= PY_SET_ATTR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user