diff options
-rw-r--r-- | source/gameengine/Ketsji/KX_Camera.cpp | 11 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_GameObject.cpp | 152 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_GameObject.h | 110 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Light.cpp | 15 | ||||
-rw-r--r-- | source/gameengine/PyDoc/KX_GameObject.py | 3 |
5 files changed, 186 insertions, 105 deletions
diff --git a/source/gameengine/Ketsji/KX_Camera.cpp b/source/gameengine/Ketsji/KX_Camera.cpp index 8032e939a50..befc8462aa3 100644 --- a/source/gameengine/Ketsji/KX_Camera.cpp +++ b/source/gameengine/Ketsji/KX_Camera.cpp @@ -534,11 +534,22 @@ PyParentObject KX_Camera::Parents[] = { PyObject* KX_Camera::py_getattro(PyObject *attr) { + if (ValidPythonToGameObject(this)==false) { + if (!strcmp(PyString_AsString(attr), "isValid")) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; /* ValidPythonToGameObject sets the error */ + } + py_getattro_up(KX_GameObject); } int KX_Camera::py_setattro(PyObject *attr, PyObject *value) { + if (ValidPythonToGameObject(this)==false) + return -1; + py_setattro_up(KX_GameObject); } diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index fd9ba93f4b6..36fb9142adc 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -1106,6 +1106,8 @@ PyAttributeDef KX_GameObject::Attributes[] = { KX_PYATTRIBUTE_RO_FUNCTION("controllers", KX_GameObject, pyattr_get_controllers), KX_PYATTRIBUTE_RO_FUNCTION("actuators", KX_GameObject, pyattr_get_actuators), + KX_PYATTRIBUTE_RO_FUNCTION("isValid", KX_GameObject, pyattr_get_is_valid), + {NULL} //Sentinel }; @@ -1169,6 +1171,12 @@ PyObject* KX_GameObject::PyGetPosition(PyObject* self) Py_ssize_t KX_GameObject::Map_Len(PyObject* self_v) { KX_GameObject* self= static_cast<KX_GameObject*>(self_v); + + if (ValidPythonToGameObject(self)==false) { + PyErr_Clear(); + return 0; + } + Py_ssize_t len= self->GetPropertyCount(); if(self->m_attrlist) len += PyDict_Size(self->m_attrlist); @@ -1183,6 +1191,9 @@ PyObject *KX_GameObject::Map_GetItem(PyObject *self_v, PyObject *item) CValue* resultattr; PyObject* pyconvert; + if (ValidPythonToGameObject(self)==false) + 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(); @@ -1212,6 +1223,9 @@ int KX_GameObject::Map_SetItem(PyObject *self_v, PyObject *key, PyObject *val) if(attr_str==NULL) PyErr_Clear(); + if (ValidPythonToGameObject(self)==false) + return -1; + if (val==NULL) { /* del ob["key"] */ int del= 0; @@ -1537,7 +1551,7 @@ int KX_GameObject::pyattr_set_scaling(void *self_v, const KX_PYATTRIBUTE_DEF *at PyObject* KX_GameObject::pyattr_get_timeOffset(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_GameObject* self= static_cast<KX_GameObject*>(self_v); - SG_Node* sg_parent= self->GetSGNode()->GetSGParent(); + SG_Node* sg_parent= self->GetSGNode()->GetSGParent(); /* GetSGNode() is valid or exception would be raised */ if (sg_parent && sg_parent->IsSlowParent()) { return PyFloat_FromDouble(static_cast<KX_SlowParentRelation *>(sg_parent->GetParentRelation())->GetTimeOffset()); } else { @@ -1549,7 +1563,7 @@ int KX_GameObject::pyattr_set_timeOffset(void *self_v, const KX_PYATTRIBUTE_DEF { KX_GameObject* self= static_cast<KX_GameObject*>(self_v); MT_Scalar val = PyFloat_AsDouble(value); - SG_Node* sg_parent= self->GetSGNode()->GetSGParent(); + SG_Node* sg_parent= self->GetSGNode()->GetSGParent(); /* GetSGNode() is valid or exception would be raised */ if (val < 0.0f) { /* also accounts for non float */ PyErr_SetString(PyExc_AttributeError, "expected a float zero or above"); return 1; @@ -1604,6 +1618,10 @@ PyObject* KX_GameObject::pyattr_get_meshes(void *self_v, const KX_PYATTRIBUTE_DE return meshes; } +PyObject* KX_GameObject::pyattr_get_is_valid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + Py_RETURN_TRUE; +} /* experemental! */ PyObject* KX_GameObject::pyattr_get_sensors(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) @@ -1642,7 +1660,6 @@ PyObject* KX_GameObject::pyattr_get_actuators(void *self_v, const KX_PYATTRIBUTE return resultlist; } - /* __dict__ only for the purpose of giving useful dir() results */ PyObject* KX_GameObject::pyattr_get_dir_dict(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { @@ -1674,7 +1691,6 @@ PyObject* KX_GameObject::pyattr_get_dir_dict(void *self_v, const KX_PYATTRIBUTE_ return dict; } - PyObject* KX_GameObject::py_getattro(PyObject *attr) { py_getattro_up(SCA_IObject); @@ -1685,6 +1701,113 @@ int KX_GameObject::py_setattro(PyObject *attr, PyObject *value) // py_setattro m py_setattro_up(SCA_IObject); } + +/* we need our own getattr and setattr types */ +/* See m_attrlist definition for rules on how this works */ +PyObject *KX_GameObject::py_base_getattro_gameobject(PyObject * self, PyObject *attr) +{ + if(((KX_GameObject *) self)->GetSGNode()==NULL) { + if (!strcmp(PyString_AsString(attr), "isValid")) { + PyErr_Clear(); + Py_INCREF(Py_False); + return Py_False; + } + + ValidPythonToGameObject(((KX_GameObject *) self)); // we know its invalid, just get the error + return NULL; + } + + 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; +} + +int KX_GameObject::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; +} + PyObject* KX_GameObject::PyApplyForce(PyObject* self, PyObject* args) { int local = 0; @@ -1992,7 +2115,7 @@ static void walk_children(SG_Node* node, CListValue* list, bool recursive) PyObject* KX_GameObject::PyGetChildren(PyObject* self) { CListValue* list = new CListValue(); - walk_children(m_pSGNode, list, 0); + walk_children(GetSGNode(), list, 0); /* GetSGNode() is always valid or it would have raised an exception before this */ return list; } @@ -2569,6 +2692,11 @@ bool ConvertPythonToGameObject(PyObject * value, KX_GameObject **object, bool py if (PyObject_TypeCheck(value, &KX_GameObject::Type)) { *object = static_cast<KX_GameObject*>(value); + + /* sets the error */ + if (ValidPythonToGameObject(*object)==false) + return false; + return true; } @@ -2582,3 +2710,17 @@ bool ConvertPythonToGameObject(PyObject * value, KX_GameObject **object, bool py return false; } + +bool ValidPythonToGameObject(KX_GameObject *object) +{ + if (object->GetSGNode()==NULL) { + PyErr_Format( + PyExc_RuntimeError, + "KX_GameObject \"%s\" is not longer in a scene, " + "check for this case with the \"isValid\" attribute", + object->GetName().ReadPtr() ); + return false; + } + + return true; +}
\ No newline at end of file diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 94192580859..0bf3e60f34b 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -51,7 +51,6 @@ #include "SCA_LogicManager.h" /* for ConvertPythonToGameObject to search object names */ #define KX_OB_DYNAMIC 1 - //Forward declarations. struct KX_ClientObjectInfo; class KX_RayCast; @@ -61,6 +60,10 @@ class PHY_IGraphicController; class PHY_IPhysicsEnvironment; struct Object; +/* utility conversion function */ +bool ConvertPythonToGameObject(PyObject * value, KX_GameObject **object, bool py_none_ok); +bool ValidPythonToGameObject(KX_GameObject *object); + /** * KX_GameObject is the main class for dynamic objects. */ @@ -811,104 +814,15 @@ public: 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) + virtual PyObject* py_repr(void) { - 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; + if (ValidPythonToGameObject(this)==false) + return NULL; + return PyString_FromString(GetName().ReadPtr()); } - + static PyObject *py_base_getattro_gameobject(PyObject * self, PyObject *attr); + static int py_base_setattro_gameobject(PyObject * self, PyObject *attr, PyObject *value); KX_PYMETHOD_NOARGS(KX_GameObject,GetPosition); KX_PYMETHOD_O(KX_GameObject,SetPosition); @@ -979,6 +893,7 @@ public: static PyObject* pyattr_get_state(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_state(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject* pyattr_get_meshes(void* self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_is_valid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); /* for dir(), python3 uses __dir__() */ static PyObject* pyattr_get_dir_dict(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); @@ -1012,8 +927,5 @@ private : }; -/* utility conversion function */ -bool ConvertPythonToGameObject(PyObject * value, KX_GameObject **object, bool py_none_ok); - #endif //__KX_GAMEOBJECT diff --git a/source/gameengine/Ketsji/KX_Light.cpp b/source/gameengine/Ketsji/KX_Light.cpp index 059345ea8de..0fcd2c39078 100644 --- a/source/gameengine/Ketsji/KX_Light.cpp +++ b/source/gameengine/Ketsji/KX_Light.cpp @@ -177,6 +177,14 @@ PyObject* KX_LightObject::py_getattro(PyObject *attr) { char *attr_str= PyString_AsString(attr); + if (ValidPythonToGameObject(this)==false) { + if (!strcmp(attr_str, "isValid")) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + if (!strcmp(attr_str, "layer")) return PyInt_FromLong(m_lightobj.m_layer); @@ -216,9 +224,14 @@ PyObject* KX_LightObject::py_getattro(PyObject *attr) py_getattro_up(KX_GameObject); } -int KX_LightObject::py_setattro(PyObject *attr, PyObject *pyvalue) + +int KX_LightObject::py_setattro(PyObject *attr, PyObject *pyvalue) { char *attr_str= PyString_AsString(attr); + + if (ValidPythonToGameObject(this)==false) + return -1; + if (PyInt_Check(pyvalue)) { int value = PyInt_AsLong(pyvalue); diff --git a/source/gameengine/PyDoc/KX_GameObject.py b/source/gameengine/PyDoc/KX_GameObject.py index 44b84d44d8d..4aa9de2fe86 100644 --- a/source/gameengine/PyDoc/KX_GameObject.py +++ b/source/gameengine/PyDoc/KX_GameObject.py @@ -12,6 +12,7 @@ class KX_GameObject: # (SCA_IObject) All game objects are derived from this class. Properties assigned to game objects are accessible as attributes of this class. + - note: Calling ANY method or attribute on an object that has been removed from a scene will raise a RuntimeError, if an object may have been removed since last accessing it use the L{isValid} attribute to check. @ivar name: The object's name. (Read only) - note: Currently (Blender 2.49) the prefix "OB" is added to all objects name. This may change in blender 2.5. @@ -63,6 +64,8 @@ class KX_GameObject: # (SCA_IObject) - note: This attribute is experemental and may be removed (but probably wont be). - note: Changes to this list will not update the KX_GameObject. @type actuators: list + @ivar isValid: Retuerns fails when the object has been removed from the scene and can no longer be used. + @type isValid: bool """ def endObject(visible): """ |