diff options
Diffstat (limited to 'source/gameengine/GameLogic/SCA_PythonController.cpp')
-rw-r--r-- | source/gameengine/GameLogic/SCA_PythonController.cpp | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/source/gameengine/GameLogic/SCA_PythonController.cpp b/source/gameengine/GameLogic/SCA_PythonController.cpp new file mode 100644 index 00000000000..2d57a5f116d --- /dev/null +++ b/source/gameengine/GameLogic/SCA_PythonController.cpp @@ -0,0 +1,400 @@ +/** + * Execute Python scripts + * + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "SCA_PythonController.h" +#include "SCA_LogicManager.h" +#include "SCA_ISensor.h" +#include "SCA_IActuator.h" +#include "compile.h" +#include "eval.h" + + +// initialize static member variables +SCA_PythonController* SCA_PythonController::m_sCurrentController = NULL; + + +SCA_PythonController::SCA_PythonController(SCA_IObject* gameobj, + PyTypeObject* T) + : SCA_IController(gameobj, T), + m_pythondictionary(NULL), + m_bytecode(NULL), + m_bModified(true) +{ +} + + + +SCA_PythonController::~SCA_PythonController() +{ + if (m_bytecode) + { + // + //printf("released python byte script\n"); + Py_DECREF(m_bytecode); + } +} + + + +CValue* SCA_PythonController::GetReplica() +{ + SCA_PythonController* replica = new SCA_PythonController(*this); + replica->m_bytecode = NULL; + replica->m_bModified = true; + // this will copy properties and so on... + CValue::AddDataToReplica(replica); + + return replica; +} + + + +void SCA_PythonController::SetScriptText(const STR_String& text) +{ + m_scriptText = text; + m_bModified = true; +} + + + +void SCA_PythonController::SetScriptName(const STR_String& name) +{ + m_scriptName = name; +} + + + +void SCA_PythonController::SetDictionary(PyObject* pythondictionary) +{ + m_pythondictionary = pythondictionary; +} + + +static char* sPyGetCurrentController__doc__; + + +PyObject* SCA_PythonController::sPyGetCurrentController(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + m_sCurrentController->AddRef(); + return m_sCurrentController; +} + + +static char* sPyAddActiveActuator__doc__; + + +PyObject* SCA_PythonController::sPyAddActiveActuator( + + PyObject* self, + PyObject* args, + PyObject* kwds) +{ + + PyObject* ob1; + int activate; + if (!PyArg_ParseTuple(args, "Oi", &ob1,&activate)) + { + return NULL; + + } + // for safety, todo: only allow for registered actuators (pointertable) + // we don't want to crash gameengine/blender by python scripts + + CValue* ac = (CValue*)ob1; + CValue* boolval = new CBoolValue(activate!=0); + m_sCurrentLogicManager->AddActiveActuator((SCA_IActuator*)ac,boolval); + boolval->Release(); + + Py_INCREF(Py_None); + return Py_None; +} + + +char* SCA_PythonController::sPyGetCurrentController__doc__ = "getCurrentController()"; +char* SCA_PythonController::sPyAddActiveActuator__doc__= "addActiveActuator(actuator,bool)"; +char SCA_PythonController::GetActuators_doc[] = "getActuator"; + +PyTypeObject SCA_PythonController::Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "SCA_PythonController", + sizeof(SCA_PythonController), + 0, + PyDestructor, + 0, + __getattr, + __setattr, + 0, //&MyPyCompare, + __repr, + 0, //&cvalue_as_number, + 0, + 0, + 0, + 0 +}; + +PyParentObject SCA_PythonController::Parents[] = { + &SCA_PythonController::Type, + &SCA_IController::Type, + &CValue::Type, + NULL +}; +PyMethodDef SCA_PythonController::Methods[] = { + {"getActuators", (PyCFunction) SCA_PythonController::sPyGetActuators, + METH_VARARGS, SCA_PythonController::GetActuators_doc}, + {"getActuator", (PyCFunction) SCA_PythonController::sPyGetActuator, + METH_VARARGS, SCA_PythonController::GetActuator_doc}, + {"getSensors", (PyCFunction) SCA_PythonController::sPyGetSensors, + METH_VARARGS, SCA_PythonController::GetSensors_doc}, + {"getSensor", (PyCFunction) SCA_PythonController::sPyGetSensor, + METH_VARARGS, SCA_PythonController::GetSensor_doc} + , + {NULL,NULL} //Sentinel +}; + + + + /* XXX, function should be removed and PyDict_Copy used + * once we switch to all builds using Python 2.0 - zr */ +static PyObject *myPyDict_Copy(PyObject *odict) +{ + PyObject *ndict= PyDict_New(); + PyObject *key, *val; + int ppos= 0; + + while (PyDict_Next(odict, &ppos, &key, &val)) + PyDict_SetItem(ndict, key, val); + + return ndict; +} + +void SCA_PythonController::Trigger(SCA_LogicManager* logicmgr) +{ + m_sCurrentController = this; + m_sCurrentLogicManager = logicmgr; + + if (m_bModified) + { + // if a script already exists, decref it before replace the pointer to a new script + if (m_bytecode) + { + Py_DECREF(m_bytecode); + m_bytecode=NULL; + } + // recompile the scripttext into bytecode + m_bytecode = Py_CompileString(m_scriptText.Ptr(), m_scriptName.Ptr(), Py_file_input); + if (m_bytecode) + { + // store the + int i=0; + i+=2; // so compiler doesn't complain about unused variable + PyRun_SimpleString("import GameLogic\n"); + } else + { + // didn't compile, so instead of compile, complain + int i=0; + i++; // so compiler doesn't complain about unused variable + } + m_bModified=false; + } + + /* + * This part here with excdict is a temporary patch + * to avoid python/gameengine crashes when python + * inadvertently holds references to game objects + * in global variables. + * + * The idea is always make a fresh dictionary, and + * destroy it right after it is used to make sure + * python won't hold any gameobject references. + * + * Note that the PyDict_Clear _is_ necessary before + * the Py_DECREF() because it is possible for the + * variables inside the dictionary to hold references + * to the dictionary (ie. generate a cycle), so we + * break it by hand, then DECREF (which in this case + * should always ensure excdict is cleared). + */ + PyObject *excdict= myPyDict_Copy(m_pythondictionary); + struct _object* resultobj = PyEval_EvalCode((PyCodeObject*)m_bytecode, + excdict, + excdict + ); + PyDict_Clear(excdict); + Py_DECREF(excdict); + + if (resultobj) + { + Py_DECREF(resultobj); + } else + { + // something is wrong, tell the user what went wrong + printf("PYTHON SCRIPT ERROR:\n"); + PyRun_SimpleString(m_scriptText.Ptr()); + } + + m_sCurrentController = NULL; +} + + + +PyObject* SCA_PythonController::_getattr(char* attr) +{ + _getattr_up(SCA_IController); +} + + + +PyObject* SCA_PythonController::PyGetActuators(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + int index; + + PyObject* resultlist = PyList_New(m_linkedactuators.size()); + for (index=0;index<m_linkedactuators.size();index++) + { + PyList_SetItem(resultlist,index,m_linkedactuators[index]->AddRef()); + } + + return resultlist; +} + +char SCA_PythonController::GetSensor_doc[] = +"GetSensor (char sensorname) return linked sensor that is named [sensorname]\n"; +PyObject* +SCA_PythonController::PyGetSensor(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + + char *scriptArg; + + if (!PyArg_ParseTuple(args, "s", &scriptArg)) { + return NULL; + } + + int index; + for (index=0;index<m_linkedsensors.size();index++) + { + SCA_ISensor* sensor = m_linkedsensors[index]; + STR_String realname = sensor->GetName(); + if (realname == scriptArg) + { + return sensor->AddRef(); + } + } + + PyErr_SetString(PyExc_AttributeError, "Unable to find requested sensor"); + return NULL; +} + + + +char SCA_PythonController::GetActuator_doc[] = +"GetActuator (char sensorname) return linked actuator that is named [actuatorname]\n"; +PyObject* +SCA_PythonController::PyGetActuator(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + + char *scriptArg; + + if (!PyArg_ParseTuple(args, "s", &scriptArg)) { + return NULL; + } + + int index; + for (index=0;index<m_linkedactuators.size();index++) + { + SCA_IActuator* actua = m_linkedactuators[index]; + STR_String realname = actua->GetName(); + if (realname == scriptArg) + { + return actua->AddRef(); + } + } + + PyErr_SetString(PyExc_AttributeError, "Unable to find requested actuator"); + return NULL; +} + + +char SCA_PythonController::GetSensors_doc[] = "getSensors returns a list of all attached sensors"; +PyObject* +SCA_PythonController::PyGetSensors(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + int index; + + PyObject* resultlist = PyList_New(m_linkedsensors.size()); + for (index=0;index<m_linkedsensors.size();index++) + { + PyList_SetItem(resultlist,index,m_linkedsensors[index]->AddRef()); + } + + return resultlist; +} + +/* 1. getScript */ +PyObject* SCA_PythonController::PyGetScript(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + return PyString_FromString(m_scriptText); +} + +/* 2. setScript */ +PyObject* SCA_PythonController::PySetScript(PyObject* self, + PyObject* args, + PyObject* kwds) +{ + char *scriptArg; + if (!PyArg_ParseTuple(args, "s", &scriptArg)) { + return NULL; + } + + /* set scripttext sets m_bModified to true, + so next time the script is needed, a reparse into byte code is done */ + + this->SetScriptText(scriptArg); + + Py_Return; +} + +/* eof */ |