nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// 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;
}