blender/source/gameengine/Ketsji/KX_PythonSeq.cpp

516 lines
16 KiB
C++
Raw Normal View History

/**
2010-03-21 01:14:04 +00:00
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: none of this file.
*
* Contributor(s): Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
* Readonly sequence wrapper for lookups on logic bricks
*/
#ifdef WITH_PYTHON
#include "KX_PythonSeq.h"
#include "KX_GameObject.h"
#include "BL_ArmatureObject.h"
#include "SCA_ISensor.h"
#include "SCA_IController.h"
#include "SCA_IActuator.h"
PyObject *KX_PythonSeq_CreatePyObject( PyObject *base, short type )
{
KX_PythonSeq *seq = PyObject_NEW( KX_PythonSeq, &KX_PythonSeq_Type);
seq->base = base;
Py_INCREF(base); /* so we can always access to check if its valid */
seq->type = type;
seq->iter = -1; /* init */
return (PyObject *)seq;
}
static void KX_PythonSeq_dealloc( KX_PythonSeq * self )
{
Py_DECREF(self->base);
PyObject_DEL( self );
}
static Py_ssize_t KX_PythonSeq_len( PyObject * self )
{
PyObjectPlus *self_plus= BGE_PROXY_REF(((KX_PythonSeq *)self)->base);
if(self_plus==NULL) {
PyErr_SetString(PyExc_SystemError, "len(seq): "BGE_PROXY_ERROR_MSG);
return -1;
}
switch(((KX_PythonSeq *)self)->type) {
case KX_PYGENSEQ_CONT_TYPE_SENSORS:
return ((SCA_IController *)self_plus)->GetLinkedSensors().size();
case KX_PYGENSEQ_CONT_TYPE_ACTUATORS:
return ((SCA_IController *)self_plus)->GetLinkedActuators().size();
case KX_PYGENSEQ_OB_TYPE_SENSORS:
return ((KX_GameObject *)self_plus)->GetSensors().size();
case KX_PYGENSEQ_OB_TYPE_CONTROLLERS:
return ((KX_GameObject *)self_plus)->GetControllers().size();
case KX_PYGENSEQ_OB_TYPE_ACTUATORS:
return ((KX_GameObject *)self_plus)->GetActuators().size();
case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
return ((BL_ArmatureObject *)self_plus)->GetConstraintNumber();
case KX_PYGENSEQ_OB_TYPE_CHANNELS:
return ((BL_ArmatureObject *)self_plus)->GetChannelNumber();
default:
/* Should never happen */
PyErr_SetString(PyExc_SystemError, "invalid type, internal error");
return -1;
}
}
static PyObject *KX_PythonSeq_getIndex(PyObject* self, int index)
{
PyObjectPlus *self_plus= BGE_PROXY_REF(((KX_PythonSeq *)self)->base);
if(self_plus==NULL) {
PyErr_SetString(PyExc_SystemError, "val = seq[i]: "BGE_PROXY_ERROR_MSG);
return NULL;
}
switch(((KX_PythonSeq *)self)->type) {
case KX_PYGENSEQ_CONT_TYPE_SENSORS:
{
BGE performance, 4th round: logic This commit extends the technique of dynamic linked list to the logic system to eliminate as much as possible temporaries, map lookup or full scan. The logic engine is now free of memory allocation, which is an important stability factor. The overhead of the logic system is reduced by a factor between 3 and 6 depending on the logic setup. This is the speed-up you can expect on a logic setup using simple bricks. Heavy bricks like python controllers and ray sensors will still take about the same time to execute so the speed up will be less important. The core of the logic engine has been much reworked but the functionality is still the same except for one thing: the priority system on the execution of controllers. The exact same remark applies to actuators but I'll explain for controllers only: Previously, it was possible, with the "executePriority" attribute to set a controller to run before any other controllers in the game. Other than that, the sequential execution of controllers, as defined in Blender was guaranteed by default. With the new system, the sequential execution of controllers is still guaranteed but only within the controllers of one object. the user can no longer set a controller to run before any other controllers in the game. The "executePriority" attribute controls the execution of controllers within one object. The priority is a small number starting from 0 for the first controller and incrementing for each controller. If this missing feature is a must, a special method can be implemented to set a controller to run before all other controllers. Other improvements: - Systematic use of reference in parameter passing to avoid unnecessary data copy - Use pre increment in iterator instead of post increment to avoid temporary allocation - Use const char* instead of STR_String whenever possible to avoid temporary allocation - Fix reference counting bugs (memory leak) - Fix a crash in certain cases of state switching and object deletion - Minor speed up in property sensor - Removal of objects during the game is a lot faster
2009-05-10 20:53:58 +00:00
vector<SCA_ISensor*>& linkedsensors = ((SCA_IController *)self_plus)->GetLinkedSensors();
if(index<0) index += linkedsensors.size();
if(index<0 || index>= linkedsensors.size()) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return linkedsensors[index]->GetProxy();
}
case KX_PYGENSEQ_CONT_TYPE_ACTUATORS:
{
BGE performance, 4th round: logic This commit extends the technique of dynamic linked list to the logic system to eliminate as much as possible temporaries, map lookup or full scan. The logic engine is now free of memory allocation, which is an important stability factor. The overhead of the logic system is reduced by a factor between 3 and 6 depending on the logic setup. This is the speed-up you can expect on a logic setup using simple bricks. Heavy bricks like python controllers and ray sensors will still take about the same time to execute so the speed up will be less important. The core of the logic engine has been much reworked but the functionality is still the same except for one thing: the priority system on the execution of controllers. The exact same remark applies to actuators but I'll explain for controllers only: Previously, it was possible, with the "executePriority" attribute to set a controller to run before any other controllers in the game. Other than that, the sequential execution of controllers, as defined in Blender was guaranteed by default. With the new system, the sequential execution of controllers is still guaranteed but only within the controllers of one object. the user can no longer set a controller to run before any other controllers in the game. The "executePriority" attribute controls the execution of controllers within one object. The priority is a small number starting from 0 for the first controller and incrementing for each controller. If this missing feature is a must, a special method can be implemented to set a controller to run before all other controllers. Other improvements: - Systematic use of reference in parameter passing to avoid unnecessary data copy - Use pre increment in iterator instead of post increment to avoid temporary allocation - Use const char* instead of STR_String whenever possible to avoid temporary allocation - Fix reference counting bugs (memory leak) - Fix a crash in certain cases of state switching and object deletion - Minor speed up in property sensor - Removal of objects during the game is a lot faster
2009-05-10 20:53:58 +00:00
vector<SCA_IActuator*>& linkedactuators = ((SCA_IController *)self_plus)->GetLinkedActuators();
if(index<0) index += linkedactuators.size();
if(index<0 || index>= linkedactuators.size()) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return linkedactuators[index]->GetProxy();
}
case KX_PYGENSEQ_OB_TYPE_SENSORS:
{
SCA_SensorList& linkedsensors= ((KX_GameObject *)self_plus)->GetSensors();
if(index<0) index += linkedsensors.size();
if(index<0 || index>= linkedsensors.size()) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return linkedsensors[index]->GetProxy();
}
case KX_PYGENSEQ_OB_TYPE_CONTROLLERS:
{
SCA_ControllerList& linkedcontrollers= ((KX_GameObject *)self_plus)->GetControllers();
if(index<0) index += linkedcontrollers.size();
if(index<0 || index>= linkedcontrollers.size()) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return linkedcontrollers[index]->GetProxy();
}
case KX_PYGENSEQ_OB_TYPE_ACTUATORS:
{
SCA_ActuatorList& linkedactuators= ((KX_GameObject *)self_plus)->GetActuators();
if(index<0) index += linkedactuators.size();
if(index<0 || index>= linkedactuators.size()) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return linkedactuators[index]->GetProxy();
}
case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
{
int nb_constraint = ((BL_ArmatureObject *)self_plus)->GetConstraintNumber();
if(index<0)
index += nb_constraint;
if(index<0 || index>= nb_constraint) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return ((BL_ArmatureObject *)self_plus)->GetConstraint(index)->GetProxy();
}
case KX_PYGENSEQ_OB_TYPE_CHANNELS:
{
int nb_channel = ((BL_ArmatureObject *)self_plus)->GetChannelNumber();
if(index<0)
index += nb_channel;
if(index<0 || index>= nb_channel) {
PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
return NULL;
}
return ((BL_ArmatureObject *)self_plus)->GetChannel(index)->GetProxy();
}
}
PyErr_SetString(PyExc_SystemError, "invalid sequence type, this is a bug");
return NULL;
}
static PyObjectPlus * KX_PythonSeq_subscript__internal(PyObject *self, char *key)
{
PyObjectPlus *self_plus= BGE_PROXY_REF(((KX_PythonSeq *)self)->base);
switch(((KX_PythonSeq *)self)->type) {
case KX_PYGENSEQ_CONT_TYPE_SENSORS:
{
BGE performance, 4th round: logic This commit extends the technique of dynamic linked list to the logic system to eliminate as much as possible temporaries, map lookup or full scan. The logic engine is now free of memory allocation, which is an important stability factor. The overhead of the logic system is reduced by a factor between 3 and 6 depending on the logic setup. This is the speed-up you can expect on a logic setup using simple bricks. Heavy bricks like python controllers and ray sensors will still take about the same time to execute so the speed up will be less important. The core of the logic engine has been much reworked but the functionality is still the same except for one thing: the priority system on the execution of controllers. The exact same remark applies to actuators but I'll explain for controllers only: Previously, it was possible, with the "executePriority" attribute to set a controller to run before any other controllers in the game. Other than that, the sequential execution of controllers, as defined in Blender was guaranteed by default. With the new system, the sequential execution of controllers is still guaranteed but only within the controllers of one object. the user can no longer set a controller to run before any other controllers in the game. The "executePriority" attribute controls the execution of controllers within one object. The priority is a small number starting from 0 for the first controller and incrementing for each controller. If this missing feature is a must, a special method can be implemented to set a controller to run before all other controllers. Other improvements: - Systematic use of reference in parameter passing to avoid unnecessary data copy - Use pre increment in iterator instead of post increment to avoid temporary allocation - Use const char* instead of STR_String whenever possible to avoid temporary allocation - Fix reference counting bugs (memory leak) - Fix a crash in certain cases of state switching and object deletion - Minor speed up in property sensor - Removal of objects during the game is a lot faster
2009-05-10 20:53:58 +00:00
vector<SCA_ISensor*>& linkedsensors = ((SCA_IController *)self_plus)->GetLinkedSensors();
SCA_ISensor* sensor;
for (unsigned int index=0;index<linkedsensors.size();index++) {
sensor = linkedsensors[index];
if (sensor->GetName() == key)
return static_cast<PyObjectPlus *>(sensor);
}
break;
}
case KX_PYGENSEQ_CONT_TYPE_ACTUATORS:
{
BGE performance, 4th round: logic This commit extends the technique of dynamic linked list to the logic system to eliminate as much as possible temporaries, map lookup or full scan. The logic engine is now free of memory allocation, which is an important stability factor. The overhead of the logic system is reduced by a factor between 3 and 6 depending on the logic setup. This is the speed-up you can expect on a logic setup using simple bricks. Heavy bricks like python controllers and ray sensors will still take about the same time to execute so the speed up will be less important. The core of the logic engine has been much reworked but the functionality is still the same except for one thing: the priority system on the execution of controllers. The exact same remark applies to actuators but I'll explain for controllers only: Previously, it was possible, with the "executePriority" attribute to set a controller to run before any other controllers in the game. Other than that, the sequential execution of controllers, as defined in Blender was guaranteed by default. With the new system, the sequential execution of controllers is still guaranteed but only within the controllers of one object. the user can no longer set a controller to run before any other controllers in the game. The "executePriority" attribute controls the execution of controllers within one object. The priority is a small number starting from 0 for the first controller and incrementing for each controller. If this missing feature is a must, a special method can be implemented to set a controller to run before all other controllers. Other improvements: - Systematic use of reference in parameter passing to avoid unnecessary data copy - Use pre increment in iterator instead of post increment to avoid temporary allocation - Use const char* instead of STR_String whenever possible to avoid temporary allocation - Fix reference counting bugs (memory leak) - Fix a crash in certain cases of state switching and object deletion - Minor speed up in property sensor - Removal of objects during the game is a lot faster
2009-05-10 20:53:58 +00:00
vector<SCA_IActuator*>& linkedactuators = ((SCA_IController *)self_plus)->GetLinkedActuators();
SCA_IActuator* actuator;
for (unsigned int index=0;index<linkedactuators.size();index++) {
actuator = linkedactuators[index];
if (actuator->GetName() == key)
return static_cast<PyObjectPlus *>(actuator);
}
break;
}
case KX_PYGENSEQ_OB_TYPE_SENSORS:
{
SCA_SensorList& linkedsensors= ((KX_GameObject *)self_plus)->GetSensors();
SCA_ISensor *sensor;
for (unsigned int index=0;index<linkedsensors.size();index++) {
sensor= linkedsensors[index];
if (sensor->GetName() == key)
return static_cast<PyObjectPlus *>(sensor);
}
break;
}
case KX_PYGENSEQ_OB_TYPE_CONTROLLERS:
{
SCA_ControllerList& linkedcontrollers= ((KX_GameObject *)self_plus)->GetControllers();
SCA_IController *controller;
for (unsigned int index=0;index<linkedcontrollers.size();index++) {
controller= linkedcontrollers[index];
if (controller->GetName() == key)
return static_cast<PyObjectPlus *>(controller);
}
break;
}
case KX_PYGENSEQ_OB_TYPE_ACTUATORS:
{
SCA_ActuatorList& linkedactuators= ((KX_GameObject *)self_plus)->GetActuators();
SCA_IActuator *actuator;
for (unsigned int index=0;index<linkedactuators.size();index++) {
actuator= linkedactuators[index];
if (actuator->GetName() == key)
return static_cast<PyObjectPlus *>(actuator);
}
break;
}
case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
{
return ((BL_ArmatureObject*)self_plus)->GetConstraint(key);
}
case KX_PYGENSEQ_OB_TYPE_CHANNELS:
{
return ((BL_ArmatureObject*)self_plus)->GetChannel(key);
}
}
return NULL;
}
static PyObject * KX_PythonSeq_subscript(PyObject * self, PyObject *key)
{
PyObjectPlus *self_plus= BGE_PROXY_REF(((KX_PythonSeq *)self)->base);
if(self_plus==NULL) {
PyErr_SetString(PyExc_SystemError, "val = seq[key], KX_PythonSeq: "BGE_PROXY_ERROR_MSG);
return NULL;
}
if (PyLong_Check(key)) {
return KX_PythonSeq_getIndex(self, PyLong_AsSsize_t( key ));
}
else if ( PyUnicode_Check(key) ) {
char *name = _PyUnicode_AsString(key);
PyObjectPlus *ret = KX_PythonSeq_subscript__internal(self, name);
if(ret) {
return ret->GetProxy();
} else {
PyErr_Format( PyExc_KeyError, "requested item \"%s\" does not exist", name);
return NULL;
}
}
else {
PyErr_SetString( PyExc_TypeError, "expected a string or an index" );
return NULL;
}
}
static int KX_PythonSeq_contains(PyObject *self, PyObject *key)
{
PyObjectPlus *self_plus= BGE_PROXY_REF(((KX_PythonSeq *)self)->base);
if(self_plus==NULL) {
PyErr_SetString(PyExc_SystemError, "key in seq, KX_PythonSeq: "BGE_PROXY_ERROR_MSG);
return -1;
}
if(!PyUnicode_Check(key)) {
PyErr_SetString(PyExc_SystemError, "key in seq, KX_PythonSeq: key must be a string");
return -1;
}
if(KX_PythonSeq_subscript__internal(self, _PyUnicode_AsString(key)))
return 1;
return 0;
}
/* Matches python dict.get(key, [default]) */
PyObject* KX_PythonSeq_get(PyObject * self, PyObject *args)
{
char *key;
PyObject* def = Py_None;
PyObjectPlus* ret_plus;
if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
return NULL;
if((ret_plus = KX_PythonSeq_subscript__internal(self, key)))
return ret_plus->GetProxy();
Py_INCREF(def);
return def;
}
PySequenceMethods KX_PythonSeq_as_sequence = {
NULL, /* Cant set the len otherwise it can evaluate as false */
NULL, /* sq_concat */
NULL, /* sq_repeat */
NULL, /* sq_item */
NULL, /* sq_slice */
NULL, /* sq_ass_item */
NULL, /* sq_ass_slice */
(objobjproc)KX_PythonSeq_contains, /* sq_contains */
(binaryfunc) NULL, /* sq_inplace_concat */
(ssizeargfunc) NULL, /* sq_inplace_repeat */
};
static PyMappingMethods KX_PythonSeq_as_mapping = {
KX_PythonSeq_len, /* mp_length */
KX_PythonSeq_subscript, /* mp_subscript */
0, /* mp_ass_subscript */
};
PyMethodDef KX_PythonSeq_methods[] = {
// dict style access for props
{"get",(PyCFunction) KX_PythonSeq_get, METH_VARARGS},
{NULL,NULL} //Sentinel
};
/*
* Initialize the interator index
*/
static PyObject *KX_PythonSeq_getIter(KX_PythonSeq *self)
{
if(BGE_PROXY_REF(self->base)==NULL) {
PyErr_SetString(PyExc_SystemError, "for i in seq: "BGE_PROXY_ERROR_MSG);
return NULL;
}
/* create a new iterator if were already using this one */
if (self->iter == -1) {
self->iter = 0;
Py_INCREF(self);
return (PyObject *)self;
} else {
return KX_PythonSeq_CreatePyObject(self->base, self->type);
}
}
/*
* Return next KX_PythonSeq iter.
*/
static PyObject *KX_PythonSeq_nextIter(KX_PythonSeq *self)
{
PyObject *object = KX_PythonSeq_getIndex((PyObject *)self, self->iter);
self->iter++;
if( object==NULL ) {
self->iter= -1; /* for reuse */
PyErr_SetString(PyExc_StopIteration, "iterator at end");
}
return object; /* can be NULL for end of iterator */
}
static int KX_PythonSeq_compare( KX_PythonSeq * a, KX_PythonSeq * b )
{
return ( a->type == b->type && a->base == b->base) ? 0 : -1;
}
static PyObject *KX_PythonSeq_richcmp(PyObject *a, PyObject *b, int op)
{
PyObject *res;
int ok= -1; /* zero is true */
if(BPy_KX_PythonSeq_Check(a) && BPy_KX_PythonSeq_Check(b))
ok= KX_PythonSeq_compare((KX_PythonSeq *)a, (KX_PythonSeq *)b);
switch (op) {
case Py_NE:
ok = !ok; /* pass through */
case Py_EQ:
res = ok ? Py_False : Py_True;
break;
case Py_LT:
case Py_LE:
case Py_GT:
case Py_GE:
res = Py_NotImplemented;
break;
default:
PyErr_BadArgument();
return NULL;
}
Py_INCREF(res);
return res;
}
/*
* repr function
* convert to a list and get its string value
*/
static PyObject *KX_PythonSeq_repr( KX_PythonSeq * self )
{
PyObject *list = PySequence_List((PyObject *)self);
PyObject *repr = PyObject_Repr(list);
Py_DECREF(list);
return repr;
}
/*****************************************************************************/
/* Python KX_PythonSeq_Type structure definition: */
/*****************************************************************************/
PyTypeObject KX_PythonSeq_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
/* For printing, in format "<module>.<name>" */
"KX_PythonSeq", /* char *tp_name; */
sizeof( KX_PythonSeq ), /* int tp_basicsize; */
0, /* tp_itemsize; For allocation */
/* Methods to implement standard operations */
( destructor ) KX_PythonSeq_dealloc, /* destructor tp_dealloc; */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
NULL, /* cmpfunc tp_compare; */
( reprfunc ) KX_PythonSeq_repr, /* reprfunc tp_repr; */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
&KX_PythonSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */
&KX_PythonSeq_as_mapping, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/*** Flags to define presence of optional/expanded features ***/
Py_TPFLAGS_DEFAULT, /* long tp_flags; */
NULL, /* char *tp_doc; Documentation string */
/*** Assigned meaning in release 2.0 ***/
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/*** Assigned meaning in release 2.1 ***/
/*** rich comparisons ***/
(richcmpfunc)KX_PythonSeq_richcmp, /* richcmpfunc tp_richcompare; */
/*** weak reference enabler ***/
0, /* long tp_weaklistoffset; */
/*** Added in release 2.2 ***/
/* Iterators */
( getiterfunc) KX_PythonSeq_getIter, /* getiterfunc tp_iter; */
( iternextfunc ) KX_PythonSeq_nextIter, /* iternextfunc tp_iternext; */
/*** Attribute descriptor and subclassing stuff ***/
KX_PythonSeq_methods, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
NULL, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
NULL, /* PyObject *tp_dict; */
NULL, /* descrgetfunc tp_descr_get; */
NULL, /* descrsetfunc tp_descr_set; */
0, /* long tp_dictoffset; */
NULL, /* initproc tp_init; */
NULL, /* allocfunc tp_alloc; */
NULL, /* newfunc tp_new; */
/* Low-level free-memory routine */
NULL, /* freefunc tp_free; */
/* For PyObject_IS_GC */
NULL, /* inquiry tp_is_gc; */
NULL, /* PyObject *tp_bases; */
/* method resolution order */
NULL, /* PyObject *tp_mro; */
NULL, /* PyObject *tp_cache; */
NULL, /* PyObject *tp_subclasses; */
NULL, /* PyObject *tp_weaklist; */
NULL
};
#endif // WITH_PYTHON