Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-04-15 14:57:28 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-04-15 14:57:28 +0400
commite8f4d9322112e64677df11ba834a26159874b332 (patch)
treea26e20a97d0c6b79c06da995b6b64c01a0fb057c /source/gameengine/Ketsji
parent19c869ab64a0e14727217c7d221e03d32a614132 (diff)
Disable using KX_GameObjects in python that have been removed from the scene (zombie objects) by raising a RuntimeError when accessing methods, attributes or passing to a function.
Common cases of this are when python references an object from the AddObject actuator that has ended, or a scene has been loaded and the old objects freed. This means some scripts will raise errors now in certain cases but better give the error early rather then failing silently with strange hard to track down behavior & crashes. Added "isValid" attribute for checking objects are in a scene. At the moment it uses the SceneGraph Node to check of the objects valid but it might be better to do this in a more generic way so scenes, meshes etc also have this check.
Diffstat (limited to 'source/gameengine/Ketsji')
-rw-r--r--source/gameengine/Ketsji/KX_Camera.cpp11
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp152
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h110
-rw-r--r--source/gameengine/Ketsji/KX_Light.cpp15
4 files changed, 183 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);