#ifdef USE_PYTHON #include "parse/python.h" #include "parse/parselo.h" #include "localization\localize.h" #include "globalincs/pstypes.h" #include "graphics/2d.h" #include "gamesequence/gamesequence.h" #include "ship/ship.h" #include "globalincs/pstypes.h" #include //*************************GLOBALS************************* PyObject *Python_global_dict; int Python_initted = 0; static int Total_lines_parsed = 0; static int Total_blocks_parsed = 0; //*************************Python helpers************************* #ifdef USE_PYTHON std::vector Python_chunks; //Note that PyBytecodelib should be a linked list or btree(?) in the future class PyBytecodelib { private: std::vector Python_chunks; public: int Add(PyBytecode &in); PyBytecode *Get(int idx); } //Returns the index in the lib, or -1 if invalid int PyBytecodelib::Add(PyBytecode &in) { if(in.Get() != NULL) { Python_chunks.push_back(in); return Python_chunks.size()-1; } else { return -1; } } //Returns pointer to the item, or NULL if invalid index PyBytecode *PyBytecodelib::Get(int idx) { Assert(idx > -1); if(idx < Python_chunks.size()) { return &Python_chunks[idx]; } return NULL; } //TODO: Make this a linked list PyBytecodelib PyBytecodeLib; #endif //*************************Types************************* //Returns true and a value to dest if possible //Returns false and sets dest to 0 on failure/unsupported type bool PyResult::GetLong(long *dest) { if(rslt==NULL) { *dest = 0; return false; } else if(PyString_Check(rslt)) { char buf[32]; GetString(buf, sizeof(buf)); *dest = atoi(buf); return true; } else { *dest = PyInt_AsLong(rslt); return true; } } //Returns true and a value to dest if possible //Returns false and sets dest to 0 on failure/unsupported type bool PyResult::GetDouble(double *dest) { if(rslt==NULL) { *dest = 0; return false; } else if(PyString_Check(rslt)) { char buf[32]; GetString(buf, sizeof(buf)); *dest = atof(buf); return true; } else { *dest = PyFloat_AsDouble(rslt); return true; } } //Returns num_chars and a string to dest if possible //Returns false and sets dests to 0 on failure/unsupported type int PyResult::GetString(char *dest, int dest_size) { if(rslt == NULL) { strcpy(dest, ""); return 0; } else if(PyInt_Check(rslt)) { sprintf(dest,"%d",PyInt_AsLong(rslt)); return strlen(dest); } else if(PyFloat_Check(rslt)) { sprintf(dest,"%f",PyFloat_AsDouble(rslt)); return strlen(dest); } else { int len = PyString_Size(rslt); if(len > -1) { strncpy(dest, PyString_AsString(rslt), dest_size-1); if(len >= dest_size) dest[dest_size-1] = '\0'; else dest[len] = '\0'; return len; } else { strcpy(dest, ""); return 0; } } } //*************************Libraries************************* /* typedef struct { PyObject_HEAD int ship_index; } py_ship; //Handles any initial arguments (causes segmentation fault right now) static int py_class_ship_init(py_vec *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"x", "y", "z", NULL}; if(!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &self->x, &self->y, &self->z)) return -1; return 0; } //Member function that puts the data in a format of type "{x y z}" static PyObject* pyvec_string(py_vec *self) { static PyObject *format = NULL; PyObject *args, *result; if(format == NULL) { format = PyString_FromString("{%f %f %f}"); if(format == NULL) return NULL; } Error(LOCATION, "Vector: %f %f %f", self->x, self->y, self->z); args = Py_BuildValue("fff", self->x, self->y, self->z); if(args == NULL) return NULL; result = PyString_Format(format, args); Py_DECREF(args); return result; } //Methods table static PyMethodDef pyvec_methods[] = { {"string", (PyCFunction)pyvec_string, METH_NOARGS, "return the vector in string format"}, {NULL} //sentinel? }; //Defines variables accessible //"Geteric Attribute Management" has access stuff static PyMemberDef pyvec_members[] = { {NULL} }; static PyTypeObject pyvecType = { PyObject_HEAD_INIT(NULL) 0, //ob_size "noddy.vec", //tp_name sizeof(py_ship), //tp_basicsize 0, //tp_itemsize 0, //tp_dealloc 0, //tp_print 0, //tp_getattr 0, //tp_setattr 0, //tp_compare 0, //tp_repr 0, //tp_as_number 0, //tp_as_sequence 0, //tp_as_mapping 0, //tp_hash 0, //tp_call 0, //tp_str 0, //tp_getattro 0, //tp_setattro 0, //tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //tp_flags "Ship object", //tp_doc 0, //tp_traverse 0, //tp_clear 0, //tp_richcompare 0, //tp_weaklistoffset 0, //tp_iter 0, //tp_iternext pyvec_methods, //tp_methods pyvec_members, //tp_members 0, //tp_getset 0, //tp_base 0, //tp_dict 0, //tp_descr_get 0, //tp_descr_set 0, //tp_dictoffset (initproc)pyvec_init, //tp_init 0, //tp_alloc py_class_ship_new, //tp_new }; PyMODINIT_FUNC initpyvec(void) { if(PyType_Ready(&pyvecType) < 0) return; Py_INCREF(&pyvecType); } */ extern int Gr_inited; static PyObject* py_lib_gr_set_color(PyObject *self, PyObject *args) { if(!Gr_inited) return Py_BuildValue("i",0); int rgba[4]={255,255,255,255}; PyArg_ParseTuple(args, "iii|i", &rgba[0], &rgba[1], &rgba[2], &rgba[3]); //Check for(int i = 0; i < 4; i++) { if(rgba[i] > 255) rgba[i] = 255; if(rgba[i] < 0) rgba[i] = 0; } //Do color clr; gr_init_alphacolor(&clr, rgba[0], rgba[1], rgba[2], rgba[3]); gr_set_color_fast(&clr); return Py_BuildValue("i",1); } static PyObject* py_lib_gr_line(PyObject *self, PyObject *args) { if(!Gr_inited) return Py_BuildValue("i",0); int x1=0,y1=0,x2=0,y2=0; PyArg_ParseTuple(args, "iiii", &x1, &y1, &x2, &y2); gr_line(x1,y1,x2,y2); return Py_BuildValue("i",1); } static PyObject* py_lib_gr_string(PyObject *self, PyObject *args) { if(!Gr_inited) return Py_BuildValue("i",0); int x=0,y=0; char *s = NULL; PyArg_ParseTuple(args, "iis", &x, &y, &s); if(s != NULL) gr_string(x,y,s); else return Py_BuildValue("i",0); return Py_BuildValue("i",1); } static std::vector PythImageList; static void py_add_image(int idx) { if(idx == -1) return; for(uint i = 0; i < PythImageList.size(); i++) { if(PythImageList[i] == idx) return; } PythImageList.push_back(idx); } void py_clear_images() { for(uint i = PythImageList.size()-1; i >= 0; i--) { bm_unload(PythImageList[i]); } } static PyObject* py_lib_gr_image(PyObject *self, PyObject *args) { if(!Gr_inited) return Py_BuildValue("i",0); int x=0,y=0; char *s = NULL; PyArg_ParseTuple(args, "iis", &x, &y, &s); if(s == NULL) return Py_BuildValue("i",0); int idx = bm_load(s); if(idx != -1) { py_add_image(idx); gr_set_bitmap(idx); gr_bitmap(x,y); } else return Py_BuildValue("i",0); return Py_BuildValue("i",1); } static PyMethodDef GrMethods[] = { {"setColor", py_lib_gr_set_color, METH_VARARGS, "Sets a color"}, {"drawLine", py_lib_gr_line, METH_VARARGS, "Draws a line"}, {"drawString", py_lib_gr_string, METH_VARARGS, "Draws a string"}, {"drawImage", py_lib_gr_image, METH_VARARGS, "Draws an image"}, {NULL, NULL, 0, NULL} }; void py_lib_init_gr() { Py_InitModule("gr", GrMethods); //PyModule_AddObject(Py_InitModule("gr", GrMethods), "vec", (PyObject *)&pyvecType); } //********************SHP static PyObject* py_lib_shp_get_class(PyObject *self, PyObject *args) { char *ship_name = NULL; PyArg_ParseTuple(args, "s", &ship_name); if(ship_name == NULL) { return Py_BuildValue("i",0); } int ship_idx = ship_name_lookup(ship_name); if(ship_idx == -1) { return Py_BuildValue("i",-1); } return PyString_FromString(Ship_info[Ships[ship_idx].ship_info_index].name); } static PyObject* py_lib_shp_get_hull(PyObject *self, PyObject *args) { char *ship_name = NULL; PyArg_ParseTuple(args, "s", &ship_name); if(ship_name == NULL) { return NULL; } int ship_idx = ship_name_lookup(ship_name); if(ship_idx == -1) { return NULL; } return Py_BuildValue("i",Objects[Ships[ship_idx].objnum].hull_strength); } static PyObject* py_lib_shp_get_shields(PyObject *self, PyObject *args) { char *ship_name = NULL; PyArg_ParseTuple(args, "s", &ship_name); if(ship_name == NULL) { return NULL; } int ship_idx = ship_name_lookup(ship_name); if(ship_idx == -1) { return NULL; } return Py_BuildValue("i",Ships[ship_idx].shield_hits); } static PyObject* py_lib_shp_get_target(PyObject *self, PyObject *args) { char *ship_name = NULL; PyArg_ParseTuple(args, "s", &ship_name); if(ship_name == NULL) { return NULL; } int ship_idx = ship_name_lookup(ship_name); if(ship_idx == -1) { return NULL; } if(Ships[ship_idx].ai_index == -1) { return NULL; } ship_idx = Ai_info[Ships[ship_idx].ai_index].target_objnum; if(Objects[ship_idx].type != OBJ_SHIP) { return NULL; } ship_idx = Objects[ship_idx].instance; return PyString_FromString(Ships[ship_idx].ship_name); } static PyObject* py_lib_shp_get_player_name(PyObject *self, PyObject *args) { if(Player_ship == NULL) { return NULL; } return PyString_FromString(Player_ship->ship_name); } static PyMethodDef ShpMethods[] = { {"getClass", py_lib_shp_get_class, METH_VARARGS, "Gets a ship's class"}, {"getTarget", py_lib_shp_get_target, METH_VARARGS, "Returns ship's target ship"}, {"getPlayerName", py_lib_shp_get_player_name, METH_VARARGS, "Gets player ship name"}, {"getShields", py_lib_shp_get_shields, METH_VARARGS, "Gets a ship's shield strength"}, {"getHull", py_lib_shp_get_hull, METH_VARARGS, "Gets a ship's hull strength"}, {NULL, NULL, 0, NULL}, }; //********************FS2 static PyObject* py_lib_fs2_get_state(PyObject *self, PyObject *args) { int depth = 0; PyArg_ParseTuple(args, "|i", &depth); PyObject *res = PyString_FromString(GS_state_text[gameseq_get_state(depth)]); return res; } static PyObject* py_lib_fs2_get_viewer_mode(PyObject *self, PyObject *args) { return Py_BuildValue("i",Viewer_mode); } static PyMethodDef Fs2Methods[] = { {"getState", py_lib_fs2_get_state, METH_VARARGS, "Gets current game state"}, {"getViewerMode", py_lib_fs2_get_viewer_mode, METH_VARARGS, "Gets current viewer mode"}, {NULL, NULL, 0, NULL} }; void py_lib_init_fs2() { Py_InitModule("fs2", Fs2Methods); Py_InitModule("shp", Fs2Methods); } //*************************GLOBAL FUNCS************************* std::vector PythonFuncs; PySession Temp_session; static char* Test_file = "python.tbl"; int python_test() { int rval; // open localization lcl_ext_open(); if ((rval = setjmp(parse_abort)) != 0) { mprintf(("PYTHON: Unable to parse '%s'! Error code = %i.\n", Test_file, rval)); lcl_ext_close(); return 0; } read_file_text(Test_file); reset_parse(); Temp_session = py_session_create(); //py_session_import_module(&my_session, "gr"); //py_session_use_namespace(&my_session, "gr"); while(optional_string("$Python:")) { PyBytecode me = python_parse(); if(me.Get() == NULL) { Error(LOCATION, "Parse error encountered in Python script"); } else { PythonFuncs.push_back(me); } } // close localization lcl_ext_close(); return 1; } void python_do_frame() { for(uint i = 0; i < PythonFuncs.size(); i++) { py_session_evaluate(&Temp_session, &PythonFuncs[i]); } if(Gr_inited) { // gr_flip(); } } void python_init() { if(!Python_initted) { //Initialize Python Py_Initialize(); Python_global_dict = PyDict_New (); //Get builtins. //Why? I don't know -WMC PyDict_SetItemString (Python_global_dict, "__builtins__", PyEval_GetBuiltins ()); mprintf(("PYTHON: Core initialized")); //*************************LIBRARY INITIALIZATION************************* mprintf(("PYTHON: Initializing libraries...")); py_lib_init_gr(); py_lib_init_fs2(); //initpyvec(); //PyDict_Update //TODO: Add all library initializations here. //************************************************************************ mprintf(("PYTHON: Fully initialized")); Python_initted=1; } else { Warning(LOCATION, "Attempt to initialize Python twice was detected."); } python_test(); } void python_close() { Py_Finalize(); Py_XDECREF(Python_global_dict); py_clear_images(); mprintf(("PYTHON: Shutdown complete")); Python_initted=0; } //*************************SESSION FUNCTIONS************************* //py_session_create //Returns the local dictionary for this session. //Use for an individual subsystem (eg HUD, armor.tbl) //Any namespace changes and new functions or classes //made by the user will be persistent in a session. //IMPORTANT: Use PY_XDECREF to unload this when //you're done with a session PySession py_session_create() { //Get rid of local dictionary return PySession(PyDict_New ()); } void py_session_import_module(PySession* session, char* module_name) { Assert(session != NULL && session->Get() != NULL); //PyDict_Update(session->Get(),PyImport_Import(PyString_FromString(module_name))); PyDict_Update(session->Get(), PyModule_GetDict(PyImport_ImportModule(module_name))); } //Adds a given module to the local dictionary, so that functions //and such can be called without use of the name. //EG: "math.cos()" could be invoked with "cos()" void py_session_use_namespace(PySession* session, char* module_name) { Assert(session != NULL && session->Get() != NULL); //PyDict_Update(session, PyModule_GetDict(PyImport_Import(PyString_FromString(module_name)))); PyDict_Update(session->Get(), PyModule_GetDict(PyImport_AddModule(module_name))); //PyDict_Update(loc, PyModule_GetDict(PyImport_Import(PyString_FromString("time")))); } //parse_python() //Throw this anywhere you want to parse python. //It'll handle both large blocks and single lines //To define a block, use '{' and '}' //Returns the parsed Python object. //After it's been parsed, call python_evaluate() to run it. PyBytecode python_parse() { //Placeholder for this call char debug_str[32]; //If "{" is present, we assume multi-line Python //If it is not present, single-line Python if(!check_for_string("{")) { //We have a line sprintf(debug_str, "parse_python(ln) %d", Total_lines_parsed++); //Stuff it char buf[PARSE_BUF_SIZE]; stuff_string(buf, F_RAW, sizeof(buf)); //Compile & return it return PyBytecode(Py_CompileString(buf, debug_str, Py_eval_input)); } else { //We have a block sprintf(debug_str, "parse_python(bk) %d", Total_blocks_parsed++); //Get the block char* raw_python = alloc_block("{","}"); //Compile & return return PyBytecode(Py_CompileString(raw_python, debug_str, Py_file_input)); } } //given a session, evaluates compiled Python code PyResult py_session_evaluate(PySession* session, PyBytecode* code) { Assert(code != NULL); if(code->Get() != NULL) { PyObject *pyr = PyEval_EvalCode((PyCodeObject*)code->Get(),Python_global_dict,session->Get()); if(pyr != NULL) { return PyResult(pyr); } } return PyResult(NULL); } #endif