/*==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 . 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); } }