You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

648 lines
22 KiB

/*==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 <http://www.gnu.org/licenses/>.
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 "plgDispatch.h"
#include "hsWindows.h"
#include "plFileSystem.h"
#include <Python.h>
#include <string>
#include <vector>
#include <iparamb2.h>
#include <max.h>
#include <direct.h>
#pragma hdrstop
#include "plPythonMgr.h"
#include "plMaxCFGFile.h"
#include "MaxComponent/plAutoUIBlock.h"
#include "MaxComponent/plPythonFileComponent.h"
#include "MaxComponent/plResponderComponent.h"
#include "pfPython/cyPythonInterface.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;
}
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<std::string>* 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(const 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 = 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<std::string> 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<std::string>* vstates)
{
12 years ago
int def = 0;
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<std::string>* 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<std::string>* 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<std::string>* 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<std::string>* vstates)
{
autoUI->AddPickNodeButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddSceneObjList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickNodeList(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddActivator(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickActivatorButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddActivatorList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickActivatorList(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddDynamicText(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickDynamicTextButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddGUIDialog(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickGUIDialogButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddExcludeRegion(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickExcludeRegionButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddWaterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickWaterComponentButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddSwimCurrentInterface(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickSwimCurrentInterfaceButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddClusterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickClusterComponentButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickAnimationButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddBehavior(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickBehaviorButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddMaterial(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickMaterialButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddGUIPopUpMenu(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickGUIPopUpMenuButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddGUISkin(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickGUISkinButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddResponder(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
std::vector<Class_ID> 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<std::string>* vstates)
{
std::vector<Class_ID> 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<std::string>* vstates)
{
autoUI->AddPickMaterialAnimationButton(id, nil, paramName, vid, vstates);
}
void plPythonMgr::IAddGrassComponent(plAutoUIBlock *autoUI, PyObject *objTuple, std::string paramName, int id, int vid, std::vector<std::string>* vstates)
{
autoUI->AddPickGrassComponentButton(id, nil, paramName.c_str(), vid, vstates);
}
void plPythonMgr::LoadPythonFiles()
{
plFileName clientPath = plMaxConfig::GetClientPath(false, true);
if (clientPath.IsValid())
{
plFileName oldCwd = plFileSystem::GetCWD();
plFileSystem::SetCWD(clientPath);
// Get the path to the Python subdirectory of the client
plFileName pythonPath = plFileName::Join(clientPath, "Python");
PythonInterface::initPython();
// Iterate through all the Python files in the folder
std::vector<plFileName> pys = plFileSystem::ListDir(pythonPath, "*.py");
for (auto iter = pys.begin(); iter != pys.end(); ++iter)
{
// Get the filename without the ".py" (module name)
plString fileName = iter->GetFileNameNoExt();
IQueryPythonFile(fileName.c_str());
}
PythonInterface::finiPython();
plFileSystem::SetCWD(oldCwd);
}
}
void plPythonMgr::IAddDropDownList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector<std::string>* vstates)
{
PyObject *options = PyTuple_GetItem(tuple, 3);
if (options && PyTuple_Check(options))
{
int size = PyTuple_Size(options);
char* opt = nil;
std::vector<std::string> 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);
}
}