//***************************************************************************** // // pymud.h // // a python module that provides some useful utility functions for interacting // with the MUD. Includes stuff like global variables, messaging functions, // and a whole bunch of other stuff. // // WORK FOR FUTURE: Our use of Py_INCREF is probably creating a // memory leak somewhere. // //***************************************************************************** #include <Python.h> #include <structmember.h> #include "../mud.h" #include "../utils.h" #include "../character.h" #include "../inform.h" #include "../handler.h" #include "../parse.h" #include "../races.h" #include "scripts.h" #include "pyroom.h" #include "pychar.h" #include "pyobj.h" #include "pyplugs.h" #include "pyexit.h" #include "pysocket.h" //***************************************************************************** // local variables and functions //***************************************************************************** // global variables we have set. PyObject *globals = NULL; // a list of methods to add to the mud module LIST *pymud_methods = NULL; // a placeholder of the movement command, as set by one of our modules PyObject *py_cmd_move = NULL; //***************************************************************************** // // GLOBAL VARIABLES // // the following functions allow scriptors to store/access global variables. // globals are stored in a python map, that maps two python objects together. // the functions used to interact with globals are: // get_global(key) // set_global(key, val) // erase_global(key) // //***************************************************************************** PyObject *mud_get_global(PyObject *self, PyObject *args) { PyObject *key = NULL; // get the key if (!PyArg_ParseTuple(args, "O", &key)) { PyErr_Format(PyExc_TypeError, "Could not retrieve global variable - no key provided"); return NULL; } PyObject *val = PyDict_GetItem(globals, key); if(val == NULL) val = Py_None; Py_INCREF(val); return val; } PyObject *mud_set_global(PyObject *self, PyObject *args) { PyObject *key = NULL, *val = NULL; if (!PyArg_ParseTuple(args, "OO", &key, &val)) { PyErr_Format(PyExc_TypeError, "Could not set global variable - need key and value"); return NULL; } PyDict_SetItem(globals, key, val); return Py_BuildValue("i", 1); } PyObject *mud_erase_global(PyObject *self, PyObject *args) { PyObject *key = NULL; if (!PyArg_ParseTuple(args, "O", &key)) { PyErr_Format(PyExc_TypeError, "Could not erase global variable - need key"); return NULL; } PyDict_SetItem(globals, key, Py_None); return Py_BuildValue("i", 1); } // // format a string to be into a typical description style PyObject *mud_format_string(PyObject *self, PyObject *args) { char *string = NULL; bool indent = TRUE; // parse all of the values if (!PyArg_ParseTuple(args, "s|b", &string, &indent)) { PyErr_Format(PyExc_TypeError, "Can not format non-string values."); return NULL; } // dup the string so we can work with it and not intrude on the PyString data BUFFER *buf = newBuffer(MAX_BUFFER); bufferCat(buf, string); bufferFormat(buf, SCREEN_WIDTH, (indent ? PARA_INDENT : 0)); PyObject *ret = Py_BuildValue("s", bufferString(buf)); deleteBuffer(buf); return ret; } // // parses arguments for character commands PyObject *mud_parse_args(PyObject *self, PyObject *args) { PyObject *pych = NULL; bool show_errors = FALSE; char *cmd = NULL; char *pyargs = NULL; char *syntax = NULL; char *parse_args = NULL; CHAR_DATA *ch = NULL; // parse our arguments if(!PyArg_ParseTuple(args, "Obsss", &pych, &show_errors, &cmd, &pyargs, &syntax)) { PyErr_Format(PyExc_TypeError, "Invalid arguments to parse_args"); return NULL; } // convert the character if(!PyChar_Check(pych) || (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_TypeError, "First argument must be an existent character!"); return NULL; } // strdup our py args; they might be edited in the parse function parse_args = strdup(pyargs); // finish up and garbage collections PyObject *retval = Py_parse_args(ch, show_errors, cmd, parse_args, syntax); free(parse_args); return retval; } // // a wrapper around NakedMud's generic_find() function PyObject *mud_generic_find(PyObject *self, PyObject *args) { PyObject *py_looker = Py_None; CHAR_DATA *looker = NULL; char *type_str = NULL; bitvector_t type = 0; char *scope_str = NULL; bitvector_t scope = 0; char *arg = NULL; bool all_ok = TRUE; // parse the arguments if(!PyArg_ParseTuple(args, "Osss|b", &py_looker, &arg, &type_str, &scope_str, &all_ok)) { PyErr_Format(PyExc_TypeError, "Invalid arguments supplied to mud.generic_find()"); return NULL; } // convert the looker if(py_looker != Py_None) { if(!PyChar_Check(py_looker) || (looker = PyChar_AsChar(py_looker)) == NULL){ PyErr_Format(PyExc_TypeError, "First argument must be an existent character or None!"); return NULL; } } // convert the scope if(is_keyword(scope_str, "room", FALSE)) SET_BIT(scope, FIND_SCOPE_ROOM); if(is_keyword(scope_str, "inv", FALSE)) SET_BIT(scope, FIND_SCOPE_INV); if(is_keyword(scope_str, "worn", FALSE)) SET_BIT(scope, FIND_SCOPE_WORN); if(is_keyword(scope_str, "world", FALSE)) SET_BIT(scope, FIND_SCOPE_WORLD); if(is_keyword(scope_str, "visible", FALSE)) SET_BIT(scope, FIND_SCOPE_VISIBLE); if(is_keyword(scope_str, "immediate", FALSE)) SET_BIT(scope, FIND_SCOPE_IMMEDIATE); if(is_keyword(scope_str, "all", FALSE)) SET_BIT(scope, FIND_SCOPE_ALL); // convert the types if(is_keyword(type_str, "obj", FALSE)) SET_BIT(type, FIND_TYPE_OBJ); if(is_keyword(type_str, "char", FALSE)) SET_BIT(type, FIND_TYPE_CHAR); if(is_keyword(type_str, "exit", FALSE)) SET_BIT(type, FIND_TYPE_EXIT); if(is_keyword(type_str, "in", FALSE)) SET_BIT(type, FIND_TYPE_IN_OBJ); if(is_keyword(type_str, "all", FALSE)) SET_BIT(type,FIND_TYPE_OBJ | FIND_TYPE_CHAR | FIND_TYPE_EXIT | FIND_TYPE_IN_OBJ); // do the search int found_type = FOUND_NONE; void *found = generic_find(looker, arg, type, scope, all_ok, &found_type); if(found_type == FOUND_CHAR) { // were we searching for one type, or multiple types? if(!strcasecmp("char", type_str)) return Py_BuildValue("O", charGetPyFormBorrowed(found)); else return Py_BuildValue("Os", charGetPyFormBorrowed(found), "char"); } else if(found_type == FOUND_EXIT) { // were we searching for one type, or multiple types? PyObject *exit = newPyExit(found); PyObject *retval = NULL; if(!strcasecmp("exit", type_str)) retval = Py_BuildValue("O", exit); else retval = Py_BuildValue("Os", exit, "obj"); Py_DECREF(exit); return retval; } else if(found_type == FOUND_OBJ) { // were we searching for one type, or multiple types? if(!strcasecmp("obj", type_str)) return Py_BuildValue("O", objGetPyFormBorrowed(found)); else return Py_BuildValue("Os", objGetPyFormBorrowed(found), "obj"); } else if(found_type == FOUND_IN_OBJ) { // were we searching for one type, or multiple types? if(!strcasecmp("in", type_str)) return Py_BuildValue("O", objGetPyFormBorrowed(found)); else return Py_BuildValue("Os", objGetPyFormBorrowed(found), "in"); } // now it gets a bit more tricky... we have to see what other bit was set else if(found_type == FOUND_LIST) { PyObject *list = PyList_New(0); LIST_ITERATOR *found_i = newListIterator(found); void *one_found = NULL; if(IS_SET(type, FIND_TYPE_CHAR)) { ITERATE_LIST(one_found, found_i) PyList_Append(list, charGetPyFormBorrowed(one_found)); } else if(IS_SET(type, FIND_TYPE_OBJ | FIND_TYPE_IN_OBJ)) { ITERATE_LIST(one_found, found_i) PyList_Append(list, charGetPyFormBorrowed(one_found)); } deleteListIterator(found_i); deleteList(found); PyObject *retval = Py_BuildValue("Os", list, "list"); Py_DECREF(list); return retval; } // nothing was found... return Py_BuildValue("Os", Py_None, Py_None); } // // execute message() from inform.h PyObject *mud_message(PyObject *self, PyObject *args) { // the python/C representations of the various variables that message() needs PyObject *pych = NULL; CHAR_DATA *ch = NULL; PyObject *pyvict = NULL; CHAR_DATA *vict = NULL; PyObject *pyobj = NULL; OBJ_DATA *obj = NULL; PyObject *pyvobj = NULL; OBJ_DATA *vobj = NULL; char *pyrange = NULL; bitvector_t range = 0; char *mssg = NULL; int hide_nosee = 0; // parse all of the arguments if(!PyArg_ParseTuple(args, "OOOObss", &pych, &pyvict, &pyobj, &pyvobj, &hide_nosee, &pyrange, &mssg)) { PyErr_Format(PyExc_TypeError,"Invalid arguments supplied to mud.message()"); return NULL; } // convert the character if(pych != Py_None) { if(!PyChar_Check(pych) || (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_TypeError, "First argument must be an existent character or None!"); return NULL; } } // convert the victim if(pyvict != Py_None) { if(!PyChar_Check(pyvict) || (vict = PyChar_AsChar(pyvict)) == NULL) { PyErr_Format(PyExc_TypeError, "Second argument must be an existent character or None!"); return NULL; } } // convert the object if(pyobj != Py_None) { if(!PyObj_Check(pyobj) || (obj = PyObj_AsObj(pyobj)) == NULL) { PyErr_Format(PyExc_TypeError, "Third argument must be an existent object or None!"); return NULL; } } // convert the target object if(pyvobj != Py_None) { if(!PyObj_Check(pyvobj) || (vobj = PyObj_AsObj(pyvobj)) == NULL) { PyErr_Format(PyExc_TypeError, "Fourth argument must be an existent object or None!"); return NULL; } } // check all of our keywords: char, vict, room if(is_keyword(pyrange, "to_char", FALSE)) SET_BIT(range, TO_CHAR); if(is_keyword(pyrange, "to_vict", FALSE)) SET_BIT(range, TO_VICT); if(is_keyword(pyrange, "to_room", FALSE)) SET_BIT(range, TO_ROOM); if(is_keyword(pyrange, "to_world", FALSE)) SET_BIT(range, TO_WORLD); // finally, send out the message message(ch, vict, obj, vobj, hide_nosee, range, mssg); return Py_BuildValue("i", 1); } // // extracts an mob or object from the game PyObject *mud_extract(PyObject *self, PyObject *args) { PyObject *thing = NULL; // parse the value if (!PyArg_ParseTuple(args, "O", &thing)) { PyErr_Format(PyExc_TypeError, "extract must be provided with an object or mob to extract!."); return NULL; } // check its type if(PyChar_Check(thing)) { CHAR_DATA *ch = PyChar_AsChar(thing); if(ch != NULL) extract_mobile(ch); else { PyErr_Format(PyExc_StandardError, "Tried to extract nonexistent character!"); return NULL; } } else if(PyObj_Check(thing)) { OBJ_DATA *obj = PyObj_AsObj(thing); if(obj != NULL) extract_obj(obj); else { PyErr_Format(PyExc_StandardError, "Tried to extract nonexistent object!"); return NULL; } } // success return Py_BuildValue("i", 1); } // // functional form of if/then/else PyObject *mud_ite(PyObject *self, PyObject *args) { PyObject *condition = NULL; PyObject *true_act = NULL; PyObject *false_act = Py_None; if (!PyArg_ParseTuple(args, "OO|O", &condition, &true_act, &false_act)) { PyErr_Format(PyExc_TypeError, "ite must be specified 2 and an optional 3rd " "arg"); return NULL; } // check to see if our condition is true if( (PyInt_Check(condition) && PyInt_AsLong(condition) != 0) || (PyString_Check(condition) && strlen(PyString_AsString(condition)) > 0)) return true_act; else return false_act; } // // returns whether or not two database keys have the same name. Locale sensitive PyObject *mud_keys_equal(PyObject *self, PyObject *args) { char *key1 = NULL; char *key2 = NULL; if(!PyArg_ParseTuple(args, "ss", &key1, &key2)) { PyErr_Format(PyExc_TypeError, "keys_equal takes two string arguments"); return NULL; } char *fullkey1 = strdup(get_fullkey_relative(key1, get_script_locale())); char *fullkey2 = strdup(get_fullkey_relative(key2, get_script_locale())); bool ok = !strcasecmp(fullkey1, fullkey2); free(fullkey1); free(fullkey2); return Py_BuildValue("i", ok); } // // returns the mud's message of the day PyObject *mud_get_motd(PyObject *self, PyObject *args) { return Py_BuildValue("s", bufferString(motd)); } PyObject *mud_log_string(PyObject *self, PyObject *args) { char *mssg = NULL; if(!PyArg_ParseTuple(args, "s", &mssg)) { PyErr_Format(PyExc_TypeError, "a message must be supplied to log_string"); return NULL; } // we have to strip all %'s out of this message BUFFER *buf = newBuffer(1); bufferCat(buf, mssg); bufferReplace(buf, "%", "%%", TRUE); log_string(bufferString(buf)); deleteBuffer(buf); return Py_BuildValue("i", 1); } PyObject *mud_set_cmd_move(PyObject *self, PyObject *args) { PyObject *cmd = NULL; if(!PyArg_ParseTuple(args, "O", &cmd)) { PyErr_Format(PyExc_TypeError, "a command must be suppled"); return NULL; } // make sure it's a function if(!PyFunction_Check(cmd)) { PyErr_Format(PyExc_TypeError, "a command must be suppled"); return NULL; } Py_XDECREF(py_cmd_move); py_cmd_move = cmd; return Py_BuildValue("i", 1); } PyObject *mud_is_race(PyObject *self, PyObject *args) { char *race = NULL; bool player_only = FALSE; if(!PyArg_ParseTuple(args, "s|b", &race, &player_only)) { PyErr_Format(PyExc_TypeError, "a string must be supplied"); return NULL; } if(player_only) return Py_BuildValue("i", raceIsForPC(race)); else return Py_BuildValue("i", isRace(race)); } PyObject *mud_list_races(PyObject *self, PyObject *args) { bool player_only = FALSE; if(!PyArg_ParseTuple(args, "|b", &player_only)) { PyErr_Format(PyExc_TypeError, "true/false value to list only player races must be provided."); return NULL; } return Py_BuildValue("s", raceGetList(player_only)); } //***************************************************************************** // MUD module //***************************************************************************** void PyMud_addMethod(const char *name, void *f, int flags, const char *doc) { // make sure our list of methods is created if(pymud_methods == NULL) pymud_methods = newList(); // make the Method def PyMethodDef *def = calloc(1, sizeof(PyMethodDef)); def->ml_name = strdup(name); def->ml_meth = (PyCFunction)f; def->ml_flags = flags; def->ml_doc = (doc ? strdup(doc) : NULL); listPut(pymud_methods, def); } PyMODINIT_FUNC init_PyMud(void) { // add all of our methods PyMud_addMethod("get_global", mud_get_global, METH_VARARGS, "Get the value of a global variable."); PyMud_addMethod("set_global", mud_set_global, METH_VARARGS, "Set the value of a global variable."); PyMud_addMethod("erase_global", mud_erase_global, METH_VARARGS, "Erase the value of a global variable."); PyMud_addMethod("message", mud_message, METH_VARARGS, "plugs into the message() function from inform.h"); PyMud_addMethod("format_string", mud_format_string, METH_VARARGS, "format a string to be 80 chars wide and indented."); PyMud_addMethod("generic_find", mud_generic_find, METH_VARARGS, "Python wrapper around the generic_find() function"); PyMud_addMethod("extract", mud_extract, METH_VARARGS, "extracts an object or character from the game."); PyMud_addMethod("keys_equal", mud_keys_equal, METH_VARARGS, "Returns whether or not two db keys are equal, given the ." "locale that the script is running in."); PyMud_addMethod("ite", mud_ite, METH_VARARGS, "A functional form of an if-then-else statement. Takes 2 " "arguments (condition, if action) and an optional third " "(else action). If no else action is specified and the " "condition is false, None is returned."); PyMud_addMethod("parse_args", mud_parse_args, METH_VARARGS, "equivalent to parse_args written in C"); PyMud_addMethod("get_motd", mud_get_motd, METH_VARARGS, "returns the mud's message of the day"); PyMud_addMethod("log_string", mud_log_string, METH_VARARGS, "adds a string to the mudlog"); PyMud_addMethod("set_cmd_move", mud_set_cmd_move, METH_VARARGS, "sets the movement command"); PyMud_addMethod("is_race", mud_is_race, METH_VARARGS, "returns whether or not the string is a valid race."); PyMud_addMethod("list_races", mud_list_races, METH_VARARGS, "returns a list of all the races available. Can take one " "argument that specifies whether or not to list player " "races."); Py_InitModule3("mud", makePyMethods(pymud_methods), "The mud module, for all MUD misc mud utils."); globals = PyDict_New(); Py_INCREF(globals); } void *get_cmd_move(void) { return py_cmd_move; }