/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. 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 3 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, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "HeadSpin.h" #include "plPythonMgr.h" #include "../MaxComponent/plAutoUIBlock.h" //#include "Python.h" #include "plMaxCFGFile.h" #include "../plFile/hsFiles.h" #include "plgDispatch.h" #include "../pfPython/cyPythonInterface.h" #include "hsUtils.h" plPythonMgr::plPythonMgr() { } plPythonMgr& plPythonMgr::Instance() { static plPythonMgr theInstance; return theInstance; } // Python wants char, not const char, LAME! static char* kGetBlockID = "glue_getBlockID"; static char* kGetClassName = "glue_getClassName"; static char* kGetNumParams = "glue_getNumParams"; static char* kGetParam = "glue_getParam"; static char* kGetVersion = "glue_getVersion"; static char* kIsMultiModifier = "glue_isMultiModifier"; static char* kGetVisInfo = "glue_getVisInfo"; bool ICallVoidFunc(PyObject *dict, char *funcName, PyObject*& val) { PyObject *func = PyDict_GetItemString(dict, (char*)funcName); if (func ) { if (PyCallable_Check(func)) { val = PyObject_CallFunction(func, NULL); if (val) { // there might have been some message printed, so get it out to the log file PythonInterface::getOutputAndReset(); return true; } // There was an error when calling the function // get the error message PyErr_Print(); PyErr_Clear(); PythonInterface::getOutputAndReset(); } } return false; } bool ICallIntFunc(PyObject *dict, char *funcName, int& val) { PyObject *obj; if (ICallVoidFunc(dict, funcName, obj)) { if (PyInt_Check(obj)) { val = PyInt_AsLong(obj); Py_DECREF(obj); return true; } } return false; } bool ICallStrFunc(PyObject *dict, char *funcName, char*& val) { PyObject *obj; if (ICallVoidFunc(dict, funcName, obj)) { if (PyString_Check(obj)) { val = hsStrcpy(PyString_AsString(obj)); Py_DECREF(obj); return true; } } return false; } #include "../MaxComponent/plPythonFileComponent.h" #include "hsUtils.h" enum ParamTypes { // These numbers used in the python/plasma/glue.py code kTypeUndefined, // 0 kTypeBool, // 1 kTypeInt, // 2 kTypeFloat, // 3 kTypeString, // 4 kTypeSceneObj, // 5 kTypeSceneObjList, // 6 kTypeActivatorList, // 7 kTypeActivator, // 8 kTypeResponder, // 9 kTypeResponderList, // 10 kTypeDynamicText, // 11 kTypeGUIDialog, // 12 kTypeExcludeRegion, // 13 (x-rude-oh legion-oh) kTypeAnimation, // 14 (animation component) kTypeAvatarBehavior, // 15 (avatar behaviors, such as one-shots or multistage behaviors) kTypeMaterial, // 16 (material type) kTypeGUIPopUpMenu, // 17 (GUI pop up menu) kTypeGUISkin, // 18 (Guess) kTypeWaterComponent, // 19 kTypeDropDownList, // 20 kTypeSwimCurrentInterface, // 21 kTypeClusterComponent, // 22 kTypeMaterialAnimation, // 23 kTypeGrassComponent, // 24 }; bool IGetTupleInt(PyObject *tuple, int pos, int& val) { PyObject *param = PyTuple_GetItem(tuple, pos); if (param && PyInt_Check(param)) { val = PyInt_AsLong(param); return true; } return false; } bool IGetTupleFloat(PyObject *tuple, int pos, float& val) { PyObject *param = PyTuple_GetItem(tuple, pos); if (param && PyFloat_Check(param)) { val = (float)PyFloat_AsDouble(param); return true; } return false; } bool IGetTupleString(PyObject *tuple, int pos, char*& val) { PyObject *param = PyTuple_GetItem(tuple, pos); if (param && PyString_Check(param)) { val = PyString_AsString(param); return true; } return false; } void IExtractVisInfo(PyObject* tuple, int* id, std::vector* vec) { PyObject* vid = PyTuple_GetItem(tuple, 0); PyObject* vstates = PyTuple_GetItem(tuple, 1); if (vid && PyInt_Check(vid)) { *id = PyInt_AsLong(vid); } if (vstates && PyList_Check(vstates)) { PyObject* element; int lsize = PyList_Size(vstates); for (int i = 0; i < lsize; i++) { element = PyList_GetItem(vstates, i); if (element && PyString_Check(element)) { std::string str = PyString_AsString(element); vec->push_back(str); } } } } bool plPythonMgr::IQueryPythonFile(char *fileName) { PyObject *module = PyImport_ImportModule(fileName); if (module) { // attach the glue python code to the end if ( !PythonInterface::RunString("execfile('.\\python\\plasma\\glue.py')", module) ) { // display any output (NOTE: this would be disabled in production) // get the messages PythonInterface::getOutputAndReset(); return false; // if we can't create the instance then there is nothing to do here } // Get the dictionary for this module PyObject *dict = PyModule_GetDict(module); // set the name of the file for the glue.py code to find PyObject* pfilename = PyString_FromString(fileName); PyDict_SetItemString(dict, "glue_name", pfilename); // Get the block ID int blockID = 0; if (!ICallIntFunc(dict, kGetBlockID, blockID)) { Py_DECREF(module); return false; } // Get the class name char *className = nil; if (!ICallStrFunc(dict, kGetClassName, className)) { Py_DECREF(module); return false; } // Get the number of parameters int numParams = 0; if (!ICallIntFunc(dict, kGetNumParams, numParams)) { Py_DECREF(module); return false; } // determine if this is a multimodifier int isMulti = 0; ICallIntFunc(dict, kIsMultiModifier, isMulti); // Get the version //====================== // Get version must be the last call that needs a pythonfile class instance // ... because it delete the instance afterwards // NOTE: get attribute params doesn't need the pythonfile class instance int version = 0; if (!ICallIntFunc(dict, kGetVersion, version)) { Py_DECREF(module); return false; } PyObject *getParamFunc = PyDict_GetItemString(dict, kGetParam); PyObject *getVisInfoFunc = PyDict_GetItemString(dict, kGetVisInfo); if (PyCallable_Check(getParamFunc)) { plAutoUIBlock *autoUI = TRACKED_NEW plAutoUIBlock(PythonFile::GetClassDesc(), blockID, className, version); // test to see if it is a multi-modifier type class if (isMulti) autoUI->SetMultiModifierFlag(true); for (int i = numParams-1; i >= 0; i--) { PyObject *ret = PyObject_CallFunction(getParamFunc, "l", i); PyObject *visinfo = nil; int ddlParamID = -1; std::vector vec; if (PyCallable_Check(getVisInfoFunc)) { visinfo = PyObject_CallFunction(getVisInfoFunc, "l", i); if (visinfo && PyTuple_Check(visinfo)) { IExtractVisInfo(visinfo, &ddlParamID, &vec); } } if (ret) { if (PyTuple_Check(ret)) { int paramID = -1; char *paramName = nil; int paramType = kTypeUndefined; // Get the param ID, name, and type if (IGetTupleInt(ret, 0, paramID) && IGetTupleString(ret, 1, paramName) && IGetTupleInt(ret, 2, paramType)) { // Get the type specific params and add the param to the AutoUI block switch (paramType) { case kTypeInt: IAddInt(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeFloat: IAddFloat(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeString: IAddString(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeBool: IAddBool(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeSceneObj: IAddSceneObj(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeSceneObjList: IAddSceneObjList(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeActivator: IAddActivator(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeActivatorList: IAddActivatorList(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeResponder: IAddResponder(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeResponderList: IAddResponderList(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeDynamicText: IAddDynamicText(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeGUIDialog: IAddGUIDialog(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeExcludeRegion: IAddExcludeRegion(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeAnimation: IAddAnimation(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeAvatarBehavior: IAddBehavior(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeMaterial: IAddMaterial(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeGUIPopUpMenu: IAddGUIPopUpMenu(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeGUISkin: IAddGUISkin(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeWaterComponent: IAddWaterComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeSwimCurrentInterface: IAddSwimCurrentInterface(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeDropDownList: IAddDropDownList(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeClusterComponent: IAddClusterComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeMaterialAnimation: IAddMaterialAnimation(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; case kTypeGrassComponent: IAddGrassComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); break; } } } Py_DECREF(ret); } } PythonFile::AddAutoUIBlock(autoUI); } delete [] className; Py_DECREF(module); } else { // There was an error when importing the module // get the error message and put it in the log PyErr_Print(); PyErr_Clear(); PythonInterface::getOutputAndReset(); } return false; } void plPythonMgr::IAddBool(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { hsBool def = false; IGetTupleInt(tuple, 3, def); autoUI->AddCheckBox(id, nil, paramName, vid, vstates, def); } void plPythonMgr::IAddInt(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { int def=0, min=0, max=100; IGetTupleInt(tuple, 3, def); PyObject *range = PyTuple_GetItem(tuple, 4); if (range && PyTuple_Check(range)) { IGetTupleInt(range, 0, min); IGetTupleInt(range, 1, max); } autoUI->AddIntSpinner(id, nil, paramName, vid, vstates, def, min, max); } void plPythonMgr::IAddFloat(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { float def=0, min=0, max=1; IGetTupleFloat(tuple, 3, def); PyObject *range = PyTuple_GetItem(tuple, 4); if (range && PyTuple_Check(range)) { IGetTupleFloat(range, 0, min); IGetTupleFloat(range, 1, max); } autoUI->AddFloatSpinner(id, nil, paramName, vid, vstates, def, min, max); } void plPythonMgr::IAddString(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { char *def = nil; IGetTupleString(tuple, 3, def); autoUI->AddEditBox(id, nil, paramName, vid, vstates, def); } void plPythonMgr::IAddSceneObj(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickNodeButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddSceneObjList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickNodeList(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddActivator(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickActivatorButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddActivatorList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickActivatorList(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddDynamicText(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickDynamicTextButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddGUIDialog(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickGUIDialogButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddExcludeRegion(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickExcludeRegionButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddWaterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickWaterComponentButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddSwimCurrentInterface(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickSwimCurrentInterfaceButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddClusterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickClusterComponentButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickAnimationButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddBehavior(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickBehaviorButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddMaterial(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickMaterialButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddGUIPopUpMenu(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickGUIPopUpMenuButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddGUISkin(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickGUISkinButton(id, nil, paramName, vid, vstates); } #include "../MaxComponent/plResponderComponent.h" void plPythonMgr::IAddResponder(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { std::vector cids; cids.push_back(RESPONDER_CID); autoUI->AddPickComponentButton(id, nil, paramName, vid, vstates, &cids, true); } void plPythonMgr::IAddResponderList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { std::vector cids; cids.push_back(RESPONDER_CID); autoUI->AddPickComponentList(id, nil, paramName, vid, vstates, &cids); } void plPythonMgr::IAddMaterialAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickMaterialAnimationButton(id, nil, paramName, vid, vstates); } void plPythonMgr::IAddGrassComponent(plAutoUIBlock *autoUI, PyObject *objTuple, std::string paramName, int id, int vid, std::vector* vstates) { autoUI->AddPickGrassComponentButton(id, nil, paramName.c_str(), vid, vstates); } #include void plPythonMgr::LoadPythonFiles() { const char *clientPath = plMaxConfig::GetClientPath(false, true); if (clientPath) { char oldCwd[MAX_PATH]; _getcwd(oldCwd, sizeof(oldCwd)); _chdir(clientPath); // Get the path to the Python subdirectory of the client char pythonPath[MAX_PATH]; strcpy(pythonPath, clientPath); strcat(pythonPath, "Python"); PythonInterface::initPython(); // Iterate through all the Python files in the folder hsFolderIterator folder(pythonPath); while (folder.NextFileSuffix(".py")) { // Get the filename without the ".py" (module name) const char *fullFileName = folder.GetFileName(); char fileName[_MAX_FNAME]; _splitpath(fullFileName, NULL, NULL, fileName, NULL); IQueryPythonFile(fileName); } PythonInterface::finiPython(); _chdir(oldCwd); } } void plPythonMgr::IAddDropDownList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) { PyObject *options = PyTuple_GetItem(tuple, 3); if (options && PyTuple_Check(options)) { int size = PyTuple_Size(options); char* opt = nil; std::vector optionsVec; for (int i = 0; i < size; i++) { IGetTupleString(options, i, opt); std::string str = opt; optionsVec.push_back(str); } autoUI->AddDropDownList(id, nil, paramName, vid, vstates, &optionsVec); } }