//***************************************************************************** // // olc_extender.c // // A structure for helping in the extension of OLC functions. Allows for the // registration of new commands, menu displays, parsers, and execution // functions. // //***************************************************************************** #include "../mud.h" #include "../socket.h" #include "olc.h" #include "olc_extender.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "../scripts/scripts.h" #include "../scripts/pyplugs.h" //***************************************************************************** // local variables, structures, and functions //***************************************************************************** // what kind of extension is this? #define OLCEXT_C 0 #define OLCEXT_PY 1 // what is the minimum ID we use? #define MIN_CHOICE_ID 10000 typedef struct olc_extender_data { // what type of extension is this? int type; // C functions void (* menu)(SOCKET_DATA *sock, void *data); int (* choose_exec)(SOCKET_DATA *sock, void *data); bool (* parse_exec)(SOCKET_DATA *sock, void *data, const char *arg); void (* from_proto)(void *data); void (* to_proto)(void *data, BUFFER *buf); // Python functions PyObject *pymenu; PyObject *pychoose; PyObject *pyparse; PyObject *pyfrompr; PyObject *pytopr; } OLC_EXT_DATA; struct olc_extender { HASHTABLE *opt_hash; void *(* borrow_py)(void *data); }; OLC_EXT_DATA *newOLCExt(void *menu, void *choose_exec, void *parse_exec, void *from_proto, void *to_proto) { OLC_EXT_DATA *ext = calloc(1, sizeof(OLC_EXT_DATA)); ext->menu = menu; ext->choose_exec = choose_exec; ext->parse_exec = parse_exec; ext->from_proto = from_proto; ext->to_proto = to_proto; ext->type = OLCEXT_C; return ext; } OLC_EXT_DATA *newPyOLCExt(PyObject *menu, PyObject *choose_exec, PyObject *parse_exec, PyObject *from_proto, PyObject *to_proto) { OLC_EXT_DATA *ext = calloc(1, sizeof(OLC_EXT_DATA)); ext->pymenu = menu; Py_XINCREF(menu); ext->pychoose = choose_exec; Py_XINCREF(choose_exec); ext->pyparse = parse_exec; Py_XINCREF(parse_exec); ext->pyfrompr = from_proto; Py_XINCREF(from_proto); ext->pytopr = to_proto; Py_XINCREF(to_proto); ext->type = OLCEXT_PY; return ext; } void deleteOLCExt(OLC_EXT_DATA *ext) { Py_XDECREF(ext->pymenu); Py_XDECREF(ext->pychoose); Py_XDECREF(ext->pyparse); Py_XDECREF(ext->pyfrompr); Py_XDECREF(ext->pytopr); free(ext); } //***************************************************************************** // implementation of olc_extender.h //***************************************************************************** OLC_EXTENDER *newExtender(void) { OLC_EXTENDER *data = calloc(1, sizeof(OLC_EXTENDER)); data->opt_hash = newHashtable(); return data; } void extenderSetPyFunc(OLC_EXTENDER *ext, void *borrow_py) { ext->borrow_py = borrow_py; } void extenderDoMenu(SOCKET_DATA *sock, OLC_EXTENDER *ext, void *data) { LIST *keys = hashCollect(ext->opt_hash); char *key = NULL; OLC_EXT_DATA *edata = NULL; // display each menu item alphabetically listSortWith(keys, strcasecmp); LIST_ITERATOR *key_i = newListIterator(keys); ITERATE_LIST(key, key_i) { // display the menu option send_to_socket(sock, "{g%s) ", key); // then display the information edata = hashGet(ext->opt_hash, key); if(edata->type == OLCEXT_C) edata->menu(sock, data); else if(ext->borrow_py != NULL) { PyObject *ret = PyObject_CallFunction(edata->pymenu, "OO", socketGetPyFormBorrowed(sock), ext->borrow_py(data)); if(ret == NULL) log_pyerr("Error running Python OLC exention menu function: %s", key); Py_XDECREF(ret); } } deleteListIterator(key_i); // free up our garbage deleteListWith(keys, free); } int extenderDoOptChoice(SOCKET_DATA *sock, OLC_EXTENDER *ext, void *data, char opt) { char key[2] = { opt, '\0' }; // does it exist? if(!hashIn(ext->opt_hash, key)) return MENU_CHOICE_INVALID; // pull out the data OLC_EXT_DATA *edata = hashGet(ext->opt_hash, key); int retval = MENU_CHOICE_INVALID; // is it C data or Python data? if(edata->type == OLCEXT_C) retval = edata->choose_exec(sock, data); else if(ext->borrow_py != NULL) { PyObject *ret = PyObject_CallFunction(edata->pychoose, "OO", socketGetPyFormBorrowed(sock), ext->borrow_py(data)); if(ret == NULL) log_pyerr("Error running Python OLC exention choice function: %s", key); else if(PyInt_Check(ret)) retval = (int)PyInt_AsLong(ret); Py_XDECREF(ret); } // did we do a toggle, or enter a submenu? if(retval == MENU_NOCHOICE || retval == MENU_CHOICE_INVALID) return retval; return MIN_CHOICE_ID + opt; } bool extenderDoParse(SOCKET_DATA *sock, OLC_EXTENDER *ext, void *data, int choice, const char *arg) { char key[2] = { (char)(choice - MIN_CHOICE_ID), '\0' }; // pull out the data OLC_EXT_DATA *edata = hashGet(ext->opt_hash, key); int retval = FALSE; // is it C data or Python data? if(edata->type == OLCEXT_C) retval = edata->parse_exec(sock, data, arg); else if(ext->borrow_py != NULL) { PyObject *pyret = PyObject_CallFunction(edata->pyparse, "Os", ext->borrow_py(data), arg); if(pyret == NULL) log_pyerr("Error running Python OLC exention parse function: %s", key); else if(PyObject_IsTrue(pyret)) retval = TRUE; Py_XDECREF(pyret); } // was it valid? return retval; } void extenderToProto(OLC_EXTENDER *ext, void *data, BUFFER *buf) { LIST *keys = hashCollect(ext->opt_hash); char *key = NULL; OLC_EXT_DATA *edata = NULL; // display each menu item alphabetically listSortWith(keys, strcasecmp); LIST_ITERATOR *key_i = newListIterator(keys); ITERATE_LIST(key, key_i) { edata = hashGet(ext->opt_hash, key); // is it C data? if(edata->type == OLCEXT_C) { if(edata->to_proto != NULL) edata->to_proto(data, buf); } // is it Python data? else if(ext->borrow_py != NULL) { if(edata->pytopr != NULL && edata->pytopr != Py_None) { PyObject *ret = PyObject_CallFunction(edata->pytopr, "O", ext->borrow_py(data)); if(ret == NULL) log_pyerr("Error running Python OLC to_proto function: %s", key); else bufferCat(buf, PyString_AsString(ret)); Py_XDECREF(ret); } } } deleteListIterator(key_i); // free up our garbage deleteListWith(keys, free); } void extenderFromProto(OLC_EXTENDER *ext, void *data) { LIST *keys = hashCollect(ext->opt_hash); char *key = NULL; OLC_EXT_DATA *edata = NULL; // display each menu item alphabetically listSortWith(keys, strcasecmp); LIST_ITERATOR *key_i = newListIterator(keys); ITERATE_LIST(key, key_i) { edata = hashGet(ext->opt_hash, key); // is it C data? if(edata->type == OLCEXT_C) { if(edata->from_proto != NULL) edata->from_proto(data); } // is it Python data? else if(ext->borrow_py != NULL) { if(edata->pyfrompr != NULL && edata->pyfrompr != Py_None) { PyObject *ret = PyObject_CallFunction(edata->pyfrompr, "O", ext->borrow_py(data)); if(ret == NULL) log_pyerr("Error running Python OLC from_proto function: %s", key); Py_XDECREF(ret); } } } deleteListIterator(key_i); // free up our garbage deleteListWith(keys, free); } void gen_register_opt(OLC_EXTENDER *ext, char opt, OLC_EXT_DATA *data) { char key[2] = { opt, '\0' }; // do we already have this registered? If so, clear it if(hashIn(ext->opt_hash, key)) deleteOLCExt(hashRemove(ext->opt_hash, key)); hashPut(ext->opt_hash, key, data); } void extenderRegisterOpt(OLC_EXTENDER *ext, char opt, void *menu, void *choice, void *parse, void *from_proto, void *to_proto) { gen_register_opt(ext, opt, newOLCExt(menu,choice,parse,from_proto,to_proto)); } // // The same, but takes Python functions instead of C function void extenderRegisterPyOpt(OLC_EXTENDER *ext, char opt, void *menu, void *choice, void *parse, void *from_proto, void *to_proto) { gen_register_opt(ext,opt,newPyOLCExt(menu,choice,parse,from_proto,to_proto)); }