//***************************************************************************** // // pymudsys.c // // A set of system level commands and variables that may be needed by python, // but which are not neccessarily needed by scripts. // //***************************************************************************** #include <Python.h> #include <structmember.h> #include "../mud.h" #include "../utils.h" #include "../character.h" #include "../socket.h" #include "../save.h" #include "../handler.h" #include "../account.h" #include "../storage.h" #include "../world.h" #include "../zone.h" #include "pymudsys.h" #include "scripts.h" #include "pyplugs.h" #include "pychar.h" #include "pyaccount.h" #include "pysocket.h" #include "pyroom.h" #include "pyexit.h" #include "pyobj.h" #include "pystorage.h" //****************************************************************************** // optional modules //****************************************************************************** #ifdef MODULE_HELP2 #include "../help2/help.h" #endif //***************************************************************************** // local variables and functions //***************************************************************************** // a dictionary of world types and their appropriate Python class PyObject *worldtypes = NULL; // a list of methods to add to the mudsys module LIST *pymudsys_methods = NULL; // a placeholder of the movement command, as set by one of our modules PyObject *py_cmd_move = NULL; // a list of checks that need to be performed before movement LIST *py_move_checks = NULL; // a list of default movement commands LIST *dflt_move_cmds = NULL; //***************************************************************************** // mudsys methods //***************************************************************************** PyObject *mudsys_set_sys_val(PyObject *self, PyObject *args) { char *key, *val; if(!PyArg_ParseTuple(args, "ss", &key, &val)) { PyErr_Format(PyExc_TypeError, "Provide a string key and value"); return NULL; } mudsettingSetString(key, val); return Py_BuildValue("i", 1); } PyObject *mudsys_get_sys_val(PyObject *self, PyObject *args) { char *key; if(!PyArg_ParseTuple(args, "s", &key)) { PyErr_Format(PyExc_TypeError, "Provide a string key"); return NULL; } return Py_BuildValue("s", mudsettingGetString(key)); } PyObject *mudsys_shutdown(PyObject *self, PyObject *args) { shut_down = TRUE; return Py_BuildValue("i", 1); } PyObject *mudsys_copyover(PyObject *self, PyObject *args) { do_copyover(); return Py_BuildValue("i", 1); } PyObject *mudsys_create_account(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } if(account_exists(name)) return Py_BuildValue("O", Py_None); if(account_creating(name)) return Py_BuildValue("O", Py_None); ACCOUNT_DATA *acct = newAccount(); accountSetName(acct, name); return Py_BuildValue("O", accountGetPyFormBorrowed(acct)); } PyObject *mudsys_create_player(PyObject *Self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } if(player_exists(name)) return Py_BuildValue("O", Py_None); if(player_creating(name)) return Py_BuildValue("O", Py_None); CHAR_DATA *ch = newChar(); charSetName(ch, name); // give the character a unique id int next_char_uid = mudsettingGetInt("puid") + 1; mudsettingSetInt("puid", next_char_uid); charSetUID(ch, next_char_uid); // if it's the first player, give him all priviledges if(charGetUID(ch) == 1) bitSet(charGetUserGroups(ch), "admin, wizard, builder, scripter, player, playtester"); // make sure we'll load back into the same room we were saved in charSetLoadroom(ch, START_ROOM); char_exist(ch); return Py_BuildValue("O", charGetPyFormBorrowed(ch)); } PyObject *mudsys_player_exists(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } return Py_BuildValue("i", player_exists(name)); } PyObject *mudsys_account_exists(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } return Py_BuildValue("i", account_exists(name)); } PyObject *mudsys_player_creating(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } return Py_BuildValue("i", player_creating(name)); } PyObject *mudsys_account_creating(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A string name must be supplied."); return NULL; } return Py_BuildValue("i", account_creating(name)); } // // registers a player into the system PyObject *mudsys_do_register(PyObject *Self, PyObject *args) { PyObject *val = NULL; CHAR_DATA *ch = NULL; ACCOUNT_DATA *acct = NULL; if(!PyArg_ParseTuple(args, "O", &val)) { PyErr_Format(PyExc_TypeError, "A char or account to be registered must be supplied."); return NULL; } if(PyChar_Check(val)) { if((ch = PyChar_AsChar(val)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to register nonexistant player, %d.", PyChar_AsUid(val)); return NULL; } if(!charIsNPC(ch)) register_player(ch); } else if(PyAccount_Check(val)) { if((acct = PyAccount_AsAccount(val)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to register nonexistant account."); return NULL; } register_account(acct); } else { PyErr_Format(PyExc_TypeError, "Only players and accounts may be registered."); return NULL; } return Py_BuildValue("i", 1); } // // saves a player to disk PyObject *mudsys_do_save(PyObject *self, PyObject *args) { PyObject *val = NULL; CHAR_DATA *ch = NULL; ACCOUNT_DATA *acct = NULL; if(!PyArg_ParseTuple(args, "O", &val)) { PyErr_Format(PyExc_TypeError, "A character or account must be supplied."); return NULL; } if(PyChar_Check(val)) { if( (ch = PyChar_AsChar(val)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to save nonexistant character, %d.", PyChar_AsUid(val)); return NULL; } // only save registered characters if(!player_exists(charGetName(ch))) return Py_BuildValue("i", 0); if(!charIsNPC(ch)) save_player(ch); } else if(PyAccount_Check(val)) { if( (acct = PyAccount_AsAccount(val)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to save nonexistant account."); return NULL; } // only save registered players if(!account_exists(accountGetName(acct))) return Py_BuildValue("i", 0); save_account(acct); } else { PyErr_Format(PyExc_TypeError, "Only characters and accounts may be saved."); return NULL; } return Py_BuildValue("i", 1); } // // attaches an account to a socket PyObject *mudsys_attach_account_socket(PyObject *self, PyObject *args) { PyObject *pyacct = NULL; PyObject *pysock = NULL; ACCOUNT_DATA *acct = NULL; SOCKET_DATA *sock = NULL; if(!PyArg_ParseTuple(args, "OO", &pyacct, &pysock)) { PyErr_Format(PyExc_TypeError,"An account and socket to be attached must be supplied."); return NULL; } if(!PyAccount_Check(pyacct)) { PyErr_Format(PyExc_TypeError, "First argument must be an account."); return NULL; } if(!PySocket_Check(pysock)) { PyErr_Format(PyExc_TypeError, "Second argument must be a socket."); return NULL; } if( (acct = PyAccount_AsAccount(pyacct)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to attach nonexistant account."); return NULL; } if( (sock = PySocket_AsSocket(pysock)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to attach nonexistant socket, %d.",PySocket_AsUid(pysock)); return NULL; } // first, do any nessessary detaching for our old character and old socket if(socketGetAccount(sock) != NULL && account_exists(accountGetName(socketGetAccount(sock)))) unreference_account(socketGetAccount(sock)); socketSetAccount(sock, acct); if(account_exists(accountGetName(acct))) reference_account(acct); return Py_BuildValue("i", 1); } // // attaches a character to a socket PyObject *mudsys_attach_char_socket(PyObject *self, PyObject *args) { PyObject *pych = NULL; PyObject *pysock = NULL; CHAR_DATA *ch = NULL; SOCKET_DATA *sock = NULL; if(!PyArg_ParseTuple(args, "OO", &pych, &pysock)) { PyErr_Format(PyExc_TypeError,"A character and socket to be attached must be supplied."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError, "First argument must be a character."); return NULL; } if(!PySocket_Check(pysock)) { PyErr_Format(PyExc_TypeError, "Second argument must be a socket."); return NULL; } if( (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to attach nonexistant character, %d.",PyChar_AsUid(pych)); return NULL; } if( (sock = PySocket_AsSocket(pysock)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to attach nonexistant socket, %d.",PySocket_AsUid(pysock)); return NULL; } // first, do any nessessary detaching for our old character and old socket if(socketGetChar(sock) != NULL) charSetSocket(socketGetChar(sock), NULL); if(charGetSocket(ch) != NULL) socketSetChar(charGetSocket(ch), NULL); charSetSocket(ch, sock); socketSetChar(sock, ch); return Py_BuildValue("i", 1); } // // detaches a character from its socket PyObject *mudsys_detach_char_socket(PyObject *self, PyObject *args) { PyObject *pych = NULL; CHAR_DATA *ch = NULL; SOCKET_DATA *sock = NULL; if(!PyArg_ParseTuple(args, "O", &pych)) { PyErr_Format(PyExc_TypeError,"A character to be detached must be supplied."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError, "Only characters may be detached."); return NULL; } if( (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to detach nonexistant character, %d.",PyChar_AsUid(pych)); return NULL; } sock = charGetSocket(ch); if(sock != NULL) socketSetChar(sock, NULL); charSetSocket(ch, NULL); return Py_BuildValue("i", 1); } // // quits a character from the game PyObject *mudsys_do_quit(PyObject *self, PyObject *args) { PyObject *pych = NULL; CHAR_DATA *ch = NULL; if(!PyArg_ParseTuple(args, "O", &pych)) { PyErr_Format(PyExc_TypeError,"A character to be quitted must be supplied."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError, "Only characters may be quitted."); return NULL; } if( (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to quit nonexistant character, %d.",PyChar_AsUid(pych)); return NULL; } if(!charIsNPC(ch) && charGetSocket(ch)) { SOCKET_DATA *sock = charGetSocket(ch); charSetSocket(ch, NULL); socketSetChar(sock, NULL); socketPopInputHandler(sock); } extract_mobile(ch); return Py_BuildValue("i", 1); } // // loads an account from disk PyObject *mudsys_load_account(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "An account name must be supplied."); return NULL; } // newPyAccount increases the reference count on the account. We can // unreference it after we've created the Python wrapper for it ACCOUNT_DATA *acct = get_account(name); if(acct == NULL) return Py_BuildValue("O", Py_None); PyObject *retval = Py_BuildValue("O", accountGetPyFormBorrowed(acct)); unreference_account(acct); return retval; } // // loads a character from disk PyObject *mudsys_load_char(PyObject *self, PyObject *args) { char *name = NULL; if(!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "A character name must be supplied."); return NULL; } CHAR_DATA *ch = get_player(name); if(ch == NULL) return Py_BuildValue("O", Py_None); char_exist(ch); return Py_BuildValue("O", charGetPyFormBorrowed(ch)); } // // tries to put the player into the game PyObject *mudsys_try_enter_game(PyObject *self, PyObject *args) { PyObject *pych = NULL; CHAR_DATA *ch = NULL; if(!PyArg_ParseTuple(args, "O", &pych)) { PyErr_Format(PyExc_TypeError,"A character to enter game must be supplied."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError, "Only characters may enter game."); return NULL; } if((ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried enter game with nonexistant character, %d.", PyChar_AsUid(pych)); return NULL; } // only enter game if we're not already in the game if(listIn(mobile_list, ch)) return Py_BuildValue("i", 0); else return Py_BuildValue("i", try_enter_game(ch)); } // // disconnects a character from its socket PyObject *mudsys_do_disconnect(PyObject *self, PyObject *args) { PyObject *pych = NULL; CHAR_DATA *ch = NULL; if(!PyArg_ParseTuple(args, "O", &pych)) { PyErr_Format(PyExc_TypeError,"A character to be dc'd must be supplied."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError, "Only characters may be dc'd."); return NULL; } if( (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to dc nonexistant character, %d.",PyChar_AsUid(pych)); return NULL; } if(charGetSocket(ch)) { SOCKET_DATA *sock = charGetSocket(ch); charSetSocket(ch, NULL); socketSetChar(sock, NULL); close_socket(sock, FALSE); } return Py_BuildValue("i", 1); } PyObject *mudsys_password_matches(PyObject *self, PyObject *args) { PyObject *pyacct = NULL; char *pwd = NULL; ACCOUNT_DATA *acct = NULL; if(!PyArg_ParseTuple(args, "Os", &pyacct, &pwd)) { PyErr_Format(PyExc_TypeError, "an account and password must be supplied."); return NULL; } if(!PyAccount_Check(pyacct)) { PyErr_Format(PyExc_TypeError, "only accounts may have passwords checked."); return NULL; } if( (acct = PyAccount_AsAccount(pyacct)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to check password for nonexistant account."); return NULL; } return Py_BuildValue("i", compares(crypt(pwd, accountGetName(acct)), accountGetPassword(acct))); } PyObject *mudsys_set_password(PyObject *self, PyObject *args) { PyObject *pyacct = NULL; char *pwd = NULL; ACCOUNT_DATA *acct = NULL; if(!PyArg_ParseTuple(args, "Os", &pyacct, &pwd)) { PyErr_Format(PyExc_TypeError, "an account and password must be supplied."); return NULL; } if(!PyAccount_Check(pyacct)) { PyErr_Format(PyExc_TypeError, "only accounts may have passwords set."); return NULL; } if( (acct = PyAccount_AsAccount(pyacct)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to set password for nonexistant account."); return NULL; } accountSetPassword(acct, crypt(pwd, accountGetName(acct))); return Py_BuildValue("i", 1); } // // add a new command to the mud, via a python script or module. Takes in a // command name, a sort_by command, the function, a minimum and maximum // position in the form of strings, a level, and boolean values for whether the // command can be performed by mobiles, and whether it interrupts actions. PyObject *mudsys_add_cmd(PyObject *self, PyObject *args) { PyObject *func = NULL; char *name = NULL, *sort_by = NULL, *group = NULL; bool interrupts = FALSE; // parse all of the values if (!PyArg_ParseTuple(args, "szOsb", &name, &sort_by, &func, &group, &interrupts)) { PyErr_Format(PyExc_TypeError, "Could not add new command. Improper arguments supplied"); return NULL; } // make sure it's a function object, and check its documentation to see if // we can add it as a helpfile if(PyFunction_Check(func)) { add_py_cmd(name, sort_by, func, group, interrupts); #ifdef MODULE_HELP2 if(get_help(name,FALSE)==NULL && ((PyFunctionObject*)func)->func_doc != NULL && PyString_Check(((PyFunctionObject*)func)->func_doc)){ BUFFER *buf = newBuffer(1); bufferCat(buf, PyString_AsString(((PyFunctionObject*)func)->func_doc)); bufferFormat(buf, SCREEN_WIDTH, 0); if(bufferLength(buf) > 0) add_help(name, bufferString(buf), group, NULL, FALSE); deleteBuffer(buf); } #endif } return Py_BuildValue(""); } // // adds a check prior to the command's execution. If any check returns false, // the command fails. Checks are assumed to tell people why their command // failed. PyObject *mudsys_add_cmd_check(PyObject *self, PyObject *args) { PyObject *func = NULL; char *name = NULL; // parse all of the values if (!PyArg_ParseTuple(args, "sO", &name, &func)) { PyErr_Format(PyExc_TypeError, "Could not add new command check. Improper arguments supplied."); return NULL; } // add the command to the game if(PyFunction_Check(func)) add_py_cmd_check(name, func); return Py_BuildValue(""); } // // removes a command from the game PyObject *mudsys_remove_cmd(PyObject *self, PyObject *args) { char *name = NULL; // parse all of the values if (!PyArg_ParseTuple(args, "s", &name)) { PyErr_Format(PyExc_TypeError, "function requires string argument."); return NULL; } CMD_DATA *cmd = remove_cmd(name); if(cmd != NULL) deleteCmd(cmd); return Py_BuildValue("O", Py_None); } PyObject *mudsys_handle_cmd_input(PyObject *self, PyObject *args) { PyObject *pysock = NULL; SOCKET_DATA *sock = NULL; char *cmd = NULL; if(!PyArg_ParseTuple(args, "Os", &pysock, &cmd)) { PyErr_Format(PyExc_TypeError, "A socket and command must be supplied."); return NULL; } if(!PySocket_Check(pysock)) { PyErr_Format(PyExc_TypeError, "A socket must be supplied."); return NULL; } if( (sock = PySocket_AsSocket(pysock)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to run command for nonexistent socket."); return NULL; } handle_cmd_input(sock, cmd); return Py_BuildValue("i", 1); } PyObject *mudsys_show_prompt(PyObject *self, PyObject *args) { PyObject *pysock = NULL; SOCKET_DATA *sock = NULL; if(!PyArg_ParseTuple(args, "O", &pysock)) { PyErr_Format(PyExc_TypeError, "A socket must be supplied."); return NULL; } if(!PySocket_Check(pysock)) { PyErr_Format(PyExc_TypeError, "A socket must be supplied."); return NULL; } if( (sock = PySocket_AsSocket(pysock)) == NULL) { PyErr_Format(PyExc_StandardError, "Tried to show prompt to nonexistent socket."); return NULL; } show_prompt(sock); return Py_BuildValue("i", 1); } PyObject *mudsys_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; Py_XINCREF(cmd); return Py_BuildValue("i", 1); } PyObject *mudsys_register_dflt_move_cmd(PyObject *self, PyObject *args) { char *cmd = NULL; if(!PyArg_ParseTuple(args, "s", &cmd)) { PyErr_Format(PyExc_TypeError, "a command name must be supplied."); return NULL; } listPut(dflt_move_cmds, strdupsafe(cmd)); // add all of our default movement checks LIST_ITERATOR *chk_i = newListIterator(py_move_checks); PyObject *chk = NULL; ITERATE_LIST(chk, chk_i) { add_py_cmd_check(cmd, chk); } deleteListIterator(chk_i); return Py_BuildValue("i", 1); } PyObject *mudsys_register_move_check(PyObject *self, PyObject *args) { PyObject *check = NULL; if(!PyArg_ParseTuple(args, "O", &check)) { PyErr_Format(PyExc_TypeError, "a check must be suppled"); return NULL; } // make sure it's a function if(!PyFunction_Check(check)) { PyErr_Format(PyExc_TypeError, "a function must be suppled"); return NULL; } // add it to all of our default movement commands LIST_ITERATOR *dir_i = newListIterator(dflt_move_cmds); char *dir = NULL; ITERATE_LIST(dir, dir_i) { add_py_cmd_check(dir, check); } deleteListIterator(dir_i); // now keep track of it for all of our special movement commands Py_XINCREF(check); listQueue(py_move_checks, check); return Py_BuildValue("i", 1); } //***************************************************************************** // functions for adding methods to classes within Python //***************************************************************************** PyObject *mudsys_gen_add_method(PyTypeObject *type, PyObject *args) { PyObject *pyname = NULL; PyObject *method = NULL; char *name = NULL; if(!PyArg_ParseTuple(args, "OO", &pyname, &method)) { PyErr_Format(PyExc_TypeError, "Method takes string and function or property argument."); return NULL; } if(!PyString_Check(pyname)) { PyErr_Format(PyExc_TypeError, "First argument not a string."); return NULL; } if(!PyFunction_Check(method) && !PyObject_TypeCheck(method,&PyProperty_Type)){ PyErr_Format(PyExc_TypeError,"Second argument not a function or property."); return NULL; } name = PyString_AsString(pyname); if(PyDict_Contains(type->tp_dict, pyname)) { PyErr_Format(PyExc_TypeError, "%s already has attribute, %s.", type->tp_name, name); return NULL; } // add our new method PyDict_SetItemString(type->tp_dict, name, method); return Py_BuildValue("i", 1); } PyObject *mudsys_add_sock_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PySocket_Type, args); } PyObject *mudsys_add_room_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PyRoom_Type, args); } PyObject *mudsys_add_exit_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PyExit_Type, args); } PyObject *mudsys_add_obj_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PyObj_Type, args); } PyObject *mudsys_add_char_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PyChar_Type, args); } PyObject *mudsys_add_acct_method(PyObject *self, PyObject *args) { return mudsys_gen_add_method(&PyAccount_Type, args); } LIST *pychar_see_checks = NULL; LIST *pyobj_see_checks = NULL; LIST *pyexit_see_checks = NULL; bool pycan_see_char(CHAR_DATA *ch, CHAR_DATA *target) { bool ret = TRUE; if(pychar_see_checks != NULL) { PyObject *pych = charGetPyFormBorrowed(ch); PyObject *pytarget = charGetPyFormBorrowed(target); LIST_ITERATOR *chk_i = newListIterator(pychar_see_checks); PyObject *chk = NULL; ITERATE_LIST(chk, chk_i) { PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget); if(pyret == NULL) log_pyerr("Error evaluating char cansee check"); else if(!PyObject_IsTrue(pyret)) ret = FALSE; Py_XDECREF(pyret); if(ret == FALSE) break; } deleteListIterator(chk_i); } return ret; } bool pycan_see_obj(CHAR_DATA *ch, OBJ_DATA *target) { bool ret = TRUE; if(pyobj_see_checks != NULL) { PyObject *pych = charGetPyFormBorrowed(ch); PyObject *pytarget = objGetPyFormBorrowed(target); LIST_ITERATOR *chk_i = newListIterator(pyobj_see_checks); PyObject *chk = NULL; ITERATE_LIST(chk, chk_i) { PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget); if(pyret == NULL) log_pyerr("Error evaluating obj cansee check"); else if(!PyObject_IsTrue(pyret)) ret = FALSE; Py_XDECREF(pyret); if(ret == FALSE) break; } deleteListIterator(chk_i); } return ret; } bool pycan_see_exit(CHAR_DATA *ch, EXIT_DATA *target) { bool ret = TRUE; if(pyexit_see_checks != NULL) { PyObject *pych = charGetPyFormBorrowed(ch); PyObject *pytarget = newPyExit(target); LIST_ITERATOR *chk_i = newListIterator(pyexit_see_checks); PyObject *chk = NULL; ITERATE_LIST(chk, chk_i) { PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget); if(pyret == NULL) log_pyerr("Error evaluating exit cansee check"); else if(!PyObject_IsTrue(pyret)) ret = FALSE; Py_XDECREF(pyret); if(ret == FALSE) break; } deleteListIterator(chk_i); Py_DECREF(pytarget); } return ret; } PyObject *mudsys_register_char_cansee(PyObject *self, PyObject *args) { PyObject *check = NULL; if(!PyArg_ParseTuple(args, "O", &check)) { PyErr_Format(PyExc_TypeError, "Error parsing new character see check."); return NULL; } if(pychar_see_checks == NULL) pychar_see_checks = newList(); Py_INCREF(check); listPut(pychar_see_checks, check); return Py_BuildValue("O", Py_None); } PyObject *mudsys_register_obj_cansee(PyObject *self, PyObject *args) { PyObject *check = NULL; if(!PyArg_ParseTuple(args, "O", &check)) { PyErr_Format(PyExc_TypeError, "Error parsing new object see check."); return NULL; } if(pyobj_see_checks == NULL) pyobj_see_checks = newList(); Py_INCREF(check); listPut(pyobj_see_checks, check); return Py_BuildValue("O", Py_None); } PyObject *mudsys_register_exit_cansee(PyObject *self, PyObject *args) { PyObject *check = NULL; if(!PyArg_ParseTuple(args, "O", &check)) { PyErr_Format(PyExc_TypeError, "Error parsing new exit see check."); return NULL; } if(pyexit_see_checks == NULL) pyexit_see_checks = newList(); Py_INCREF(check); listPut(pyexit_see_checks, check); return Py_BuildValue("O", Py_None); } //***************************************************************************** // functions for adding and getting types to the gameworld //***************************************************************************** void pytype_set_key(const char *type, PyObject *thing, const char *key) { PyObject *ret = PyObject_CallMethod(thing, "setKey", "s", key); if(ret == NULL) log_pyerr("error calling setKey for world type, %s", type); Py_XDECREF(ret); } PyObject *pytype_read(const char *type, STORAGE_SET *set) { PyObject *thing = PyDict_GetItemString(worldtypes, type); if(thing == NULL) { log_pyerr("world type, %s, does not exist", type); return NULL; } else { PyObject *pystore = newPyStorageSet(set); PyObject *copy = PyObject_CallFunction(thing, "O", pystore); // did it work? if(copy == NULL) log_pyerr("Error reading world type, %s", type); Py_XDECREF(pystore); return copy; } } STORAGE_SET *pytype_store(const char *type, PyObject *thing) { PyObject *pystore = PyObject_CallMethod(thing, "store", NULL); STORAGE_SET *set = NULL; // make sure it worked if(pystore != NULL) set = PyStorageSet_AsSet(pystore); else { log_pyerr("error calling store for world type, %s", type); set = new_storage_set(); } Py_XDECREF(pystore); return set; } void pytype_delete(const char *type, PyObject *thing) { Py_XDECREF(thing); } PyObject *mudsys_world_add_type(PyObject *self, PyObject *args) { char *type = NULL; PyObject *class = NULL; if(!PyArg_ParseTuple(args, "sO", &type, &class)) { PyErr_Format(PyExc_TypeError, "Error parsing new world type"); return NULL; } // add all of our relevant methods to the game world worldAddForgetfulType(gameworld, type, pytype_read, pytype_store, pytype_delete, pytype_set_key); // now, store us so we can look up the Python Methods PyDict_SetItemString(worldtypes, type, class); return Py_BuildValue("i", 1); } PyObject *mudsys_world_get_type(PyObject *self, PyObject *args) { char *type = NULL; char *key = NULL; if(!PyArg_ParseTuple(args, "ss", &type, &key)) { PyErr_Format(PyExc_TypeError, "Error parsing type and key for world_get_type"); return NULL; } PyObject *entry = worldGetType(gameworld, type, key); return Py_BuildValue("O", (type == NULL ? Py_None : entry)); } PyObject *mudsys_world_put_type(PyObject *self, PyObject *args) { char *type = NULL; char *key = NULL; PyObject *entry = NULL; if(!PyArg_ParseTuple(args, "ssO", &type, &key, &entry)) { PyErr_Format(PyExc_TypeError, "Error parsing arguments for world_put_type"); return NULL; } Py_XINCREF(entry); worldPutType(gameworld, type, key, entry); return Py_BuildValue("i", 1); } PyObject *mudsys_world_save_type(PyObject *self, PyObject *args) { char *type = NULL; char *key = NULL; if(!PyArg_ParseTuple(args, "ss", &type, &key)) { PyErr_Format(PyExc_TypeError, "Error parsing type and key for world_save_type"); return NULL; } worldSaveType(gameworld, type, key); return Py_BuildValue("i", 1); } PyObject *mudsys_world_remove_type(PyObject *self, PyObject *args) { char *type = NULL; char *key = NULL; if(!PyArg_ParseTuple(args, "ss", &type, &key)) { PyErr_Format(PyExc_TypeError, "Error parsing type and key for world_remove_type"); return NULL; } PyObject *entry = worldRemoveType(gameworld, type, key); PyObject *ret = Py_BuildValue("O", (entry == NULL ? Py_None : entry)); Py_XDECREF(entry); return ret; } PyObject *mudsys_create_bitvector(PyObject *self, PyObject *args) { return Py_BuildValue(""); } PyObject *mudsys_get_bitvector(PyObject *self, PyObject *args) { char *vector = NULL; if(!PyArg_ParseTuple(args, "s", &vector)) { PyErr_Format(PyExc_TypeError, "Error parsing vector for get_bitvector."); return NULL; } return Py_BuildValue("s", bitvectorGetBitsStr(vector)); } PyObject *mudsys_create_bit(PyObject *self, PyObject *args) { char *vector = NULL; char *bit = NULL; if(!PyArg_ParseTuple(args, "ss", &vector, &bit)) { PyErr_Format(PyExc_TypeError, "Error parsing vector and bit for create_bit."); return NULL; } bitvectorAddBit(vector, bit); return Py_BuildValue("O", Py_True); } PyObject *mudsys_next_uid(PyObject *self, void *closure) { return Py_BuildValue("i", next_uid()); } PyObject *mudsys_list_zone_contents(PyObject *self, PyObject *args) { char *zonekey = NULL; char *type = NULL; if(!PyArg_ParseTuple(args, "ss", &zonekey, &type)) { PyErr_Format(PyExc_TypeError, "a zone name and content type must be supplied."); return NULL; } // make sure the zone exists ZONE_DATA *zone = worldGetZone(gameworld, zonekey); if(zone == NULL) return PyList_New(0); else { LIST *keys = zoneGetTypeKeys(zone, type); LIST_ITERATOR *key_i = newListIterator(keys); PyObject *ret = PyList_New(0); char *key = NULL; ITERATE_LIST(key, key_i) { PyObject *str = Py_BuildValue("s", key); PyList_Append(ret, str); Py_XDECREF(str); } deleteListIterator(key_i); deleteListWith(keys, free); return ret; } } PyObject *mudsys_can_edit_zone(PyObject *self, PyObject *args) { PyObject *pych = NULL; CHAR_DATA *ch = NULL; ZONE_DATA *zone = NULL; char *zname = NULL; if(!PyArg_ParseTuple(args, "Os", &pych, &zname)) { PyErr_Format(PyExc_TypeError,"can_edit_zone takes ch and zone name."); return NULL; } if(!PyChar_Check(pych)) { PyErr_Format(PyExc_TypeError,"Non-character supplied to can_edit_zone."); return NULL; } else if( (ch = PyChar_AsChar(pych)) == NULL) { PyErr_Format(PyExc_TypeError,"can_edit_zone supplied non-existent zone."); return NULL; } // find the zone and check zone = worldGetZone(gameworld, zname); if(zone == NULL || !canEditZone(zone, ch)) return Py_BuildValue("i", 0); return Py_BuildValue("i", 1); } //***************************************************************************** // MudSys module //***************************************************************************** void PyMudSys_addMethod(const char *name, void *f, int flags, const char *doc) { // make sure our list of methods is created if(pymudsys_methods == NULL) pymudsys_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(pymudsys_methods, def); } PyMODINIT_FUNC init_PyMudSys(void) { // local variables py_move_checks = newList(); dflt_move_cmds = newList(); // add in hooks to the main MUD register_char_see(pycan_see_char); register_obj_see(pycan_see_obj); register_exit_see(pycan_see_exit); // add all of our methods PyMudSys_addMethod("do_shutdown", mudsys_shutdown, METH_VARARGS, "do_shutdown()\n\n" "shuts the mud down."); PyMudSys_addMethod("do_copyover", mudsys_copyover, METH_VARARGS, "do_copyover()\n\n" "performs a copyover on the mud."); PyMudSys_addMethod("sys_setval", mudsys_set_sys_val, METH_VARARGS, "set_sysval(name, val)\n" "\n" "sets a value registered in the system settings."); PyMudSys_addMethod("sys_getval", mudsys_get_sys_val, METH_VARARGS, "sys_getval(name)\n" "\n" "returns a value registered in the system settings."); PyMudSys_addMethod("sys_getvar", mudsys_get_sys_val, METH_VARARGS, "Alias to mudsys.sys_getval"); PyMudSys_addMethod("sys_setvar", mudsys_set_sys_val, METH_VARARGS, "Alias to mudsys.sys_setval"); PyMudSys_addMethod("player_exists", mudsys_player_exists, METH_VARARGS, "player_exists(name)\n" "\n" "Returns whether a player with the name exists."); PyMudSys_addMethod("account_exists", mudsys_account_exists, METH_VARARGS, "account_exists(name)\n" "\n" "Returns whether an account with the name exists."); PyMudSys_addMethod("player_creating", mudsys_player_creating, METH_VARARGS, "player_creating(name)\n" "\n" "returns whether a player with the name is creating."); PyMudSys_addMethod("account_creating", mudsys_account_creating, METH_VARARGS, "account_creating(name)\n" "\n" "returns whether an account with the name is creating."); PyMudSys_addMethod("do_register", mudsys_do_register, METH_VARARGS, "do_register(char_or_account)\n" "\n" "Register a PC or account for the first time. Should be called after creation."); PyMudSys_addMethod("get_player", mudsys_load_char, METH_VARARGS, "get_player(name)\n" "\n" "Return a saved character of specified name, or None."); PyMudSys_addMethod("load_char", mudsys_load_char, METH_VARARGS, "Alias for mudsys.get_player(name)."); PyMudSys_addMethod("load_account", mudsys_load_account, METH_VARARGS, "load_account(name)\n" "\n" "Return a saved account of specified name, or None."); PyMudSys_addMethod("try_enter_game", mudsys_try_enter_game, METH_VARARGS, "try_enter_game(ch)\n" "\n" "Tries to add a character the game world."); PyMudSys_addMethod("do_save", mudsys_do_save, METH_VARARGS, "do_save(char_or_account)\n" "\n" "Save a character or account's information."); PyMudSys_addMethod("do_quit", mudsys_do_quit, METH_VARARGS, "do_quit(ch)\n" "\n" "Extract a character from game."); PyMudSys_addMethod("attach_account_socket",mudsys_attach_account_socket, METH_VARARGS, "attach_account_socket(acct, sock)\n" "\n" "Link a loaded account to a connected socket."); PyMudSys_addMethod("attach_char_socket", mudsys_attach_char_socket, METH_VARARGS, "attach_char_socket(ch, sock)\n" "\n" "Link a loaded character to a connected socket."); PyMudSys_addMethod("detach_char_socket", mudsys_detach_char_socket, METH_VARARGS, "detach_char_socket(ch)\n" "\n" "Unlink a character from its attached socket."); PyMudSys_addMethod("do_disconnect", mudsys_do_disconnect, METH_VARARGS, "do_disconnect(ch)\n" "\n" "call detach_char_socket, then close the socket."); PyMudSys_addMethod("password_matches", mudsys_password_matches, METH_VARARGS, "password_matches(acct, psswd)\n" "\n" "Returns True or False if the given password matches the account's password."); PyMudSys_addMethod("set_password", mudsys_set_password, METH_VARARGS, "set_password(acct, passwd)\n" "\n" "Set an account's password."); PyMudSys_addMethod("add_cmd", mudsys_add_cmd, METH_VARARGS, "add_cmd(name, shorthand, cmd_func, user_group, interrupts_action)\n" "\n" "Add a new command to the master command table. If a preferred shorthand\n" "exists, e.g., 'n' for 'north', it can be specified. Otherwise, shorthand\n" "should be None. Command functions take three arguments: a character\n" "issuing the command, the command name, and a string argument supplied\n" "to the command. Commands must be tied to a specific user group, and they\n" "can optionally interupt character actions."); PyMudSys_addMethod("add_cmd_check", mudsys_add_cmd_check, METH_VARARGS, "add_cmd_check(name, check_func)\n" "\n" "Add a new command check to a registered command. Check functions take\n" "two arguments: the character issuing the command, and the command name.\n" "If a check fails, it should return False and send the character a\n" "message why."); PyMudSys_addMethod("remove_cmd", mudsys_remove_cmd, METH_VARARGS, "remove_cmd(name)\n" "\n" "Removes a command from the master command table."); PyMudSys_addMethod("handle_cmd_input", mudsys_handle_cmd_input, METH_VARARGS, "handle_cmd_input(sock, cmd)\n" "\n" "Equivalent to char.Char.act(cmd)"); PyMudSys_addMethod("show_prompt", mudsys_show_prompt, METH_VARARGS, "show_prompt(sock)\n" "\n" "Display the default game prompt to the socket. Can be replaced in Python\n" "by assigning a new function to show_prompt."); PyMudSys_addMethod("create_account", mudsys_create_account, METH_VARARGS, "create_account(acctname)\n" "\n" "Returns a new account by the specified name, or None if an account by.\n" "the specified name is already registered or creating. After the account" "generation process is complete, mudsys.do_register(acct) must be called."); PyMudSys_addMethod("create_player", mudsys_create_player, METH_VARARGS, "Same as mudsys.create_account for player characters."); PyMudSys_addMethod("add_sock_method", mudsys_add_sock_method, METH_VARARGS, "Same as add_acct_method for sockets."); PyMudSys_addMethod("add_char_method", mudsys_add_char_method, METH_VARARGS, "Same as add_acct_method for characters."); PyMudSys_addMethod("add_room_method", mudsys_add_room_method, METH_VARARGS, "Same as add_acct_method for rooms."); PyMudSys_addMethod("add_exit_method", mudsys_add_exit_method, METH_VARARGS, "Same as add_acct_method for exits."); PyMudSys_addMethod("add_obj_method", mudsys_add_obj_method, METH_VARARGS, "Same as add_acct_method for objects."); PyMudSys_addMethod("add_acct_method", mudsys_add_acct_method, METH_VARARGS, "add_acct_method(name, method)\n" "\n" "Adds a function or property to the Account class."); PyMudSys_addMethod("world_add_type", mudsys_world_add_type, METH_VARARGS, "world_add_type(typename, class_data)\n" "\n" "Registers a new type to the world database. Like, e.g., mob, obj, and\n" "room prototypes. Assumes class has a store and setKey method. Init\n" "method should take one optional argument: a storage set to parse the\n" "type data from, when loaded."); PyMudSys_addMethod("world_get_type", mudsys_world_get_type, METH_VARARGS, "world_get_type(typename, key)\n" "\n" "Returns registered entry of the specified type from the world database.\n" "Assumes it is a python type, and not a C type. If no type exists\n" "return None."); PyMudSys_addMethod("world_put_type", mudsys_world_put_type, METH_VARARGS, "world_put_type(typename, key, data)\n" "\n" "Put and save an entry of the specified type to the world database."); PyMudSys_addMethod("world_save_type", mudsys_world_save_type, METH_VARARGS, "world_save_type(typename, key)\n" "\n" "Saves an entry in the world database if it exists."); PyMudSys_addMethod("world_remove_type", mudsys_world_remove_type,METH_VARARGS, "world_remove_type(typename, key)\n" "\n" "Remove and return an entry from the world database, or None."); PyMudSys_addMethod("register_char_cansee", mudsys_register_char_cansee, METH_VARARGS, "register_char_cansee(check_function(observer, observee))\n" "\n" "Register a new check of whether one character can see another."); PyMudSys_addMethod("register_obj_cansee", mudsys_register_obj_cansee, METH_VARARGS, "Same as register_char_cansee for objects."); PyMudSys_addMethod("register_exit_cansee", mudsys_register_exit_cansee, METH_VARARGS, "Same as register_char_cansee for exits."); PyMudSys_addMethod("set_cmd_move", mudsys_set_cmd_move, METH_VARARGS, "set_cmd_move(cmd_func)\n" "\n" "Register a player command for handling all default movement commands.\n" "See mudsys.add_cmd for information about commands."); PyMudSys_addMethod("register_dflt_move_cmd", mudsys_register_dflt_move_cmd, METH_VARARGS, "register_dflt_move_cmd(cmdname)\n" "\n" "registers a new default movement command, e.g., north"); PyMudSys_addMethod("register_move_check",mudsys_register_move_check, METH_VARARGS, "register_move_check(check_func)\n" "\n" "Register a check to perform movement commands. See mudsys.add_cmd_check\n" "for information about command checks."); PyMudSys_addMethod("create_bitvector", mudsys_create_bitvector, METH_VARARGS, "Not yet implemented."); PyMudSys_addMethod("get_bitvector", mudsys_get_bitvector, METH_VARARGS, "get_bitvector(bitvector)\n" "\n" "Gets a list of the avaliable bits in the specified bitvector."); PyMudSys_addMethod("create_bit", mudsys_create_bit, METH_VARARGS, "create_bit(bitvector, bit)\n" "\n" "Creates a new bit on the specified bitvector."); PyMudSys_addMethod("next_uid", mudsys_next_uid, METH_NOARGS, "next_uid()\n\n" "Returns the next available universal identification number."); PyMudSys_addMethod("list_zone_contents", mudsys_list_zone_contents, METH_VARARGS, "list_zone_contents(zone, type)\n\n" "Returns a list of the content keys of the given type, for the specified\n" "zone."); PyMudSys_addMethod("can_edit_zone", mudsys_can_edit_zone, METH_VARARGS, "can_edit_zone(ch, zone)\n\n" "True or False if a character has permission to edit a zone."); Py_InitModule3("mudsys", makePyMethods(pymudsys_methods), "The mudsys module, for all MUD system utils."); worldtypes = PyDict_New(); Py_INCREF(worldtypes); } void *get_cmd_move(void) { return py_cmd_move; } LIST *get_move_checks(void) { return py_move_checks; }