//***************************************************************************** // // hooks.c // // Hooks are chunks of code that attach on to parts of game code, but aren't // really parts of the game code. Hooks can be used for a number of things. For // instance: processing room descriptions by outside modules before they are // displayed to a character, running triggers when certain events happen, or // perhaps logging how many time a room is entered or exited. We would probably // not want to hard-code any of these things into the core of the mud if they // are fairly stand-alone. So instead, we write hooks that attach into the game // and execute when certain events happen. // // Often events that will execute hooks are set off by someone or something // taking an action. Thus, all hooks take 3 arguments (actor, acted, arg) to // make it easy to handle these cases. These 3 arguments do not need to be used // for all hooks, however. // //***************************************************************************** #include "mud.h" #include "utils.h" #include "character.h" #include "object.h" #include "room.h" #include "exit.h" #include "account.h" #include "socket.h" #include "hooks.h" //***************************************************************************** // local functions, variables, and definitions //***************************************************************************** // the table of all our installed hooks HASHTABLE *hook_table = NULL; // the list of functions called whenever a hook is run LIST *monitors = NULL; // a buffer for building hook info on BUFFER *info_buf = NULL; //***************************************************************************** // implementation of hooks.h //***************************************************************************** void init_hooks(void) { // make our required variables hook_table = newHashtable(); info_buf = newBuffer(1); monitors = newList(); } void hookRemove(const char *type, void (* func)(const char *)) { LIST *list = hashGet(hook_table, type); if(list != NULL) listRemove(list, func); } void hookAdd(const char *type, void (* func)(const char *)) { LIST *list = hashGet(hook_table, type); if(list == NULL) { list = newList(); hashPut(hook_table, type, list); } listQueue(list, func); } void hookAddMonitor(void (* func)(const char *, const char *)) { listQueue(monitors, func); } void hookRun(const char *type, const char *info) { LIST *list = hashGet(hook_table, type); char *info_dup = strdup(info); if(list != NULL) { LIST_ITERATOR *list_i = newListIterator(list); void (* func)(const char *) = NULL; ITERATE_LIST(func, list_i) { func(info_dup); } deleteListIterator(list_i); } // run our monitors LIST_ITERATOR *mon_i = newListIterator(monitors); void (* mon)(const char *, const char *) = NULL; ITERATE_LIST(mon, mon_i) { mon(type, info_dup); } deleteListIterator(mon_i); free(info_dup); } const char *hookBuildInfo(const char *format, ...) { // clear our workspace bufferClear(info_buf); // parse out all of our tokens LIST *tokens = parse_strings(format, ' '); LIST_ITERATOR *token_i = newListIterator(tokens); char *token = NULL; int len = listSize(tokens); int count = 0; // go through all of our tokens va_list vargs; va_start(vargs, format); ITERATE_LIST(token, token_i) { if(!strcasecmp(token, "ch")) bprintf(info_buf, "ch.%d", charGetUID(va_arg(vargs, CHAR_DATA *))); else if(!strcasecmp(token, "obj")) bprintf(info_buf, "obj.%d", objGetUID(va_arg(vargs, OBJ_DATA *))); else if(!strcasecmp(token, "rm") || !strcasecmp(token, "room")) bprintf(info_buf, "rm.%d", roomGetUID(va_arg(vargs, ROOM_DATA *))); else if(!strcasecmp(token, "ex") || !strcasecmp(token, "exit")) bprintf(info_buf, "ex.%d", exitGetUID(va_arg(vargs, EXIT_DATA *))); else if(!strcasecmp(token, "sk") || !strcasecmp(token, "sock")) bprintf(info_buf, "sk.%d", socketGetUID(va_arg(vargs, SOCKET_DATA *))); else if(!strcasecmp(token, "str")) bprintf(info_buf, "%c%s%c", HOOK_STR_MARKER, va_arg(vargs, char *), HOOK_STR_MARKER); else if(!strcasecmp(token, "int")) bprintf(info_buf, "%d", va_arg(vargs, int)); else if(!strcasecmp(token, "dbl")) bprintf(info_buf, "%lf", va_arg(vargs, double)); // unknown type -- abort! else break; // add a space for the next token to be printed if(count < len - 1) bprintf(info_buf, " "); count++; } deleteListIterator(token_i); deleteListWith(tokens, free); va_end(vargs); return bufferString(info_buf); } // // parses up info tokens LIST *parse_hook_info_tokens(const char *info) { LIST *tokens = newList(); BUFFER *buf = newBuffer(1); while(*info) { // skip leading spaces while(isspace(*info)) info++; char marker = ' '; bufferClear(buf); // are we parsing a string or something else? if(*info == HOOK_STR_MARKER) { marker = HOOK_STR_MARKER; bprintf(buf, "%c", HOOK_STR_MARKER); info++; } // fill up to the end marker for(;*info && *info != marker; info++) bprintf(buf, "%c", *info); // were we parsing a string? if(marker == HOOK_STR_MARKER) bprintf(buf, "%c", HOOK_STR_MARKER); // skip past our marker if(*info) info++; // append our token listQueue(tokens, strdup(bufferString(buf))); } deleteBuffer(buf); return tokens; } void hookParseInfo(const char *info, ...) { // parse out all of our tokens LIST *tokens = parse_hook_info_tokens(info); LIST_ITERATOR *token_i = newListIterator(tokens); char *token = NULL; // id number we'll need for parsing some values int id = 0; // go through all of our tokens va_list vargs; va_start(vargs, info); ITERATE_LIST(token, token_i) { if(startswith(token, "ch")) { sscanf(token, "ch.%d", &id); *va_arg(vargs, CHAR_DATA **) = propertyTableGet(mob_table, id); } else if(startswith(token, "obj")) { sscanf(token, "obj.%d", &id); *va_arg(vargs, OBJ_DATA **) = propertyTableGet(obj_table, id); } else if(startswith(token, "rm")) { sscanf(token, "rm.%d", &id); *va_arg(vargs, ROOM_DATA **) = propertyTableGet(room_table, id); } else if(startswith(token, "room")) { sscanf(token, "room.%d", &id); *va_arg(vargs, ROOM_DATA **) = propertyTableGet(room_table, id); } else if(startswith(token, "ex")) { sscanf(token, "ex.%d", &id); *va_arg(vargs, EXIT_DATA **) = propertyTableGet(exit_table, id); } else if(startswith(token, "exit")) { sscanf(token, "exit.%d", &id); *va_arg(vargs, EXIT_DATA **) = propertyTableGet(exit_table, id); } else if(startswith(token, "sk")) { sscanf(token, "sk.%d", &id); *va_arg(vargs, SOCKET_DATA **) = propertyTableGet(sock_table, id); } else if(startswith(token, "sock")) { sscanf(token, "sk.%d", &id); *va_arg(vargs, SOCKET_DATA **) = propertyTableGet(sock_table, id); } else if(*token == HOOK_STR_MARKER) { char *str = strdup(token + 1); str[strlen(str)-1] = '\0'; *va_arg(vargs, char **) = str; } else if(isdigit(*token)) { // integer or double? if(next_letter_in(token, '.') > -1) *va_arg(vargs, double *) = atof(token); else *va_arg(vargs, int *) = atoi(token); } } deleteListIterator(token_i); deleteListWith(tokens, free); va_end(vargs); }