diff options
-rw-r--r-- | source/gameengine/Expressions/PyObjectPlus.h | 5 | ||||
-rw-r--r-- | source/gameengine/Expressions/Value.cpp | 29 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_GameObject.cpp | 157 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_GameObject.h | 119 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Scene.h | 1 |
5 files changed, 262 insertions, 49 deletions
diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h index f61b9f8d0b8..ea26ea1d201 100644 --- a/source/gameengine/Expressions/PyObjectPlus.h +++ b/source/gameengine/Expressions/PyObjectPlus.h @@ -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); \ diff --git a/source/gameengine/Expressions/Value.cpp b/source/gameengine/Expressions/Value.cpp index f1e367d6e59..b8b7a05aa64 100644 --- a/source/gameengine/Expressions/Value.cpp +++ b/source/gameengine/Expressions/Value.cpp @@ -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); diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index f5bf868dd59..817afedd205 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -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"); - return NULL; + /* 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; } - - resultattr = self->GetProperty(attr); - - if(resultattr==NULL) { - PyErr_SetString(PyExc_KeyError, "KX_GameObject key does not exist"); + /* 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; } - - 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, diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 58f2c56c1da..172272150c0 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -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); diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index b70c6d3e8d3..55e7afa4957 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -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; } } |