===================================== Aliases for Envy2.2 version 2 ===================================== Written by Martin Thomson (Symposium) mwt02@uow.edu.au Update: Entering extremely long strings with a #20 at the front caused a buffer to overflow. I have fixed this, and now things are a little smoother, check out the new code to be added to comm.c This code is provided as a bit of fun, don't expect it to work. I take no responsibility for what happens if you use it. I would however like you to keep my name associated with it, if indeed you do use it. There is a fair amount of code here, don't be too surprised of it doesn't work straight out, it may require a bit of work. Note: Saving of aliases is done mainly through the last section: save.c if you find that you have troubles with this, leave it out. This code should work for most Merc derivatives with some small modifications allthough it is designed for Envy I see no reason why other versions should have any problems using it, I have tested it for a while and so far it has shown no serious bugs. ============= merc.h ============= include the definition of the struct and the main functions related to the working of the aliases //in the main block of typedefs typedef struct obj_data OBJ_DATA; typedef struct obj_index_data OBJ_INDEX_DATA; typedef struct pc_data PC_DATA; ADD-> typedef struct alias_data ALIAS_DATA; typedef struct reset_data RESET_DATA; //the highly useful macro: #define CH(descriptor) ((descriptor)->original ? \ (descriptor)->original : (descriptor)->character) //anywhere near the declarations for pc_data and so on ADD-> #define ALIAS_SIMPLE 0 ADD-> #define ALIAS_MULTIPLE 1 ADD-> #define ALIAS_COMPLEX 2 ADD-> struct alias_data ADD-> { ADD-> ALIAS_DATA *next; ADD-> char *name; ADD-> char *command; ADD-> int complex; ADD-> }; // in the declarations of "do" functions DECLARE_DO_FUN( do_afk ); ADD-> DECLARE_DO_FUN( do_aliases ); /* Symposium */ DECLARE_DO_FUN( do_allow ); DECLARE_DO_FUN( do_answer ); //the declarations of functions in their seperate files /* interp.c */ void interpret args( ( CHAR_DATA *ch, char *argument ) ); bool is_number args( ( char *arg ) ); int number_argument args( ( char *argument, char *arg ) ); ADD-> bool perform_alias args( ( DESCRIPTOR_DATA *d, char *command ) ); // in the db.c section OD * new_object args( ( void ) ); ED * new_extra_descr args( ( void ) ); ADD-> ALIAS_DATA * new_alias args( ( void ) ); ADD-> void free_alias args( ( ALIAS_DATA *al ) ); CD * create_mobile args( ( MOB_INDEX_DATA *pMobIndex ) ); ========== comm.c ========== a small modification in the method of calling interpret() in both the game loops //in game_loop_mac_msdos() and game_loop_unix() //declare an integer while( !merc_down ) { DESCRIPTOR_DATA *d; fd_set in_set; fd_set out_set; fd_set exc_set; int maxdesc; ADD-> int i; ... if ( d->showstr_point ) show_string( d, d->incomm ); else if ( d->pString ) /* OLC */ string_add( d->character, d->incomm ); else if ( d->connected == CON_PLAYING ) { ADD-> /* perform the alias 10 times, too many times and ADD-> * there could be problems ie infinite looping ADD-> * --Symposium ADD-> */ ADD-> for( i = 0; i < 10 && perform_alias( d ); ++i ) ADD-> read_from_buffer( d ); if ( !run_olc_editor( d ) ) /* OLC */ interpret( d->character, d->incomm ); } else nanny( d, d->incomm ); -----------Version 2 The following changes should be made to the read_from_buffer function. This is the output from a diff -bBp, '+' means add, '!' means change. * Do '!' substitution. */ if( d->incomm[0] == '!' ) strcpy( d->incomm, d->inlast ); else strcpy( d->inlast, d->incomm ); + j = 0; + + /* + * Deal with multiple command freaks. + */ + if( *d->incomm == '#' ) + { + const char *ptr; + char temp[MAX_INPUT_LENGTH]; + + ptr = one_argument( d->incomm + 1, temp ); + if( is_number( temp ) && *ptr != '#' && temp[0] != '\0' ) + { + k = URANGE( 1, atoi( temp ), 50 ) - 1; + strcpy( d->incomm, ptr ); + if( k > 1 ) + sprintf( d->inbuf, "#%d %s\n", k, d->incomm ); + else + sprintf( d->inbuf, "%s\n", d->incomm ); + j = strchr( d->inbuf, '\0' ) - d->inbuf; + } + else + send_to_char( "Remember to use the '#' properly.\n\r", + d->character ); + } + /* ! * Shift the input buffer. Note that the fill backwards now takes into ! * consideration the command from the '#n command' parsing above. */ while( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' ) i++; ! for( i = i - j; ( d->inbuf[j] = d->inbuf[i + j] ) != '\0'; j++ ) ; return; } ========= interp.c ========= the command table entry and the function for processing an alias into a workable series of commands //in the command table: /* * Miscellaneous commands. */ { "afk", do_afk, POS_SLEEPING, 0, LOG_NORMAL }, ADD-> { "aliases", do_aliases, POS_SLEEPING, 0, LOG_NORMAL }, { "bashdoor", do_bash, POS_STANDING, 0, LOG_NORMAL }, { "bet", do_bet, POS_STANDING, 0, LOG_NORMAL }, add this entire function: /* aliases by Symposium */ bool perform_alias( DESCRIPTOR_DATA * d ) { CHAR_DATA *ch; ALIAS_DATA *jim; char *all_args; char *ptr; char *write_to; char *args[10]; char buf[MAX_INPUT_LENGTH * 4]; char outcomm[MAX_INPUT_LENGTH * 2]; char temp[MAX_INPUT_LENGTH]; int i = 0, num = 0; bool running = TRUE; const char directions[] = "neswudNESWUD"; ch = CH( d ); if( strchr( d->incomm, '|' ) ) { for( i = 0; d->incomm[i] != '\0'; i++ ) { if( d->incomm[i] == '|' ) buf[i] = '\n'; else buf[i] = d->incomm[i]; } buf[i++] = '\n'; strncpy( &buf[i], d->inbuf, MAX_INPUT_LENGTH * 4 - i ); buf[MAX_INPUT_LENGTH * 4 - 1] = '\0'; strcpy( d->inbuf, buf ); d->incomm[0] = '\0'; return TRUE; } /* determine if running */ for( ptr = d->incomm; *ptr; ptr++ ) { if( !strchr( directions, *ptr ) ) { running = FALSE; break; } } /* is it an alias */ outcomm[0] = '\0'; all_args = one_argument( d->incomm, buf ); strcpy( temp, all_args ); args[0] = &temp[0]; for( jim = ch->pcdata->aliases; jim; jim = jim->next ) { if( !str_cmp( buf, jim->name ) ) { strcpy( outcomm, jim->command ); break; } } if( outcomm[0] == '\0' ) /* This is where the most traffic will be */ { if( running && d->incomm[1] != '\0' ) { i = 0; for( ptr = d->incomm; *ptr; ptr++ ) { buf[i++] = *ptr; buf[i++] = '\n'; } strncpy( &buf[i], d->inbuf, MAX_INPUT_LENGTH * 4 - i ); buf[MAX_INPUT_LENGTH * 4 - 1] = '\0'; strcpy( d->inbuf, buf ); d->incomm[0] = '\0'; return TRUE; } return FALSE; } if( jim->complex == ALIAS_SIMPLE ) { if( !strcmp( d->inlast, buf ) && ++d->repeat >= 20 ) { sprintf( temp, "%s input spamming!", d->host ); log_string( temp ); write_to_descriptor( d->descriptor, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0 ); strcpy( d->incomm, "quit" ); } else { strcpy( d->incomm, jim->command ); } return FALSE; } /* fill with carriage returns instead of "="'s */ for( i = 0; outcomm[i] != '\0'; i++ ) { if( outcomm[i] == '=' ) outcomm[i] = '\n'; } outcomm[i++] = '\n'; outcomm[i] = '\0'; if( jim->complex == ALIAS_MULTIPLE ) { strcpy( buf, outcomm ); strncpy( &buf[i], d->inbuf, MAX_INPUT_LENGTH * 4 - i ); buf[MAX_INPUT_LENGTH * 4 - 1] = '\0'; strcpy( d->inbuf, buf ); d->incomm[0] = '\0'; return TRUE; } /* make arguments seperate */ for( i = 1; i < 9; ++i ) { args[i] = one_argument( args[i - 1], temp ); strcpy( args[i - 1], temp ); if( args[i][0] == '\0' ) break; } write_to = &buf[0]; for( ptr = &outcomm[0]; *ptr; ++ptr ) { if( *ptr == '%' ) { num = *(++ptr) - '1'; if( num < i && num >= 0 ) /* parameters, */ /* i will be the number of arguments that are valid */ { strncpy( write_to, args[num], strlen( args[num] ) ); write_to += strlen( args[num] ); } else if( *ptr == '*' ) /* all parameters */ { strcpy( write_to, all_args ); write_to += strlen( all_args ); } else if( *ptr == '%' ) /* %age sign */ { *write_to++ = '%'; } } else /* normal letter */ *write_to++ = *ptr; } i = write_to - &buf[0]; strncpy( &buf[i], d->inbuf, MAX_INPUT_LENGTH * 4 - i ); buf[MAX_INPUT_LENGTH * 4 - 1] = '\0'; strcpy( d->inbuf, buf ); d->incomm[0] = '\0'; return TRUE; } ========= db.c ========= manages the addition and removal of data structures // at the top of the file NOTE_DATA * note_free; OBJ_DATA * obj_free; PC_DATA * pcdata_free; ADD-> ALIAS_DATA * alias_free; char bug_buf [ MAX_INPUT_LENGTH*2 ]; CHAR_DATA * char_list; // in free_char() void free_char( CHAR_DATA *ch ) { OBJ_DATA *obj; OBJ_DATA *obj_next; AFFECT_DATA *paf; ADD-> ALIAS_DATA *al, *next_al; /* * This places the character in a place we know exists and allows for * more controled removal of mud effects and such. Suggested by Erwin. */ ch->in_room = get_room_index( ROOM_VNUM_LIMBO ); for ( obj = ch->carrying; obj; obj = obj_next ) ... free_string( ch->description ); if ( ch->pcdata ) { ADD-> for( al = ch->pcdata->aliases; al; al = next_al ) ADD-> { ADD-> next_al = al->next; ADD-> free_alias( al ); ADD-> } free_string( ch->pcdata->pwd ); free_string( ch->pcdata->bamfin ); free_string( ch->pcdata->bamfout ); // add these functions anywhere in the file, // if you use OLC you might add this to mem.c instead ALIAS_DATA *new_alias( void ) { ALIAS_DATA *al; if( !alias_free ) return alloc_perm( sizeof( ALIAS_DATA ) ); al = alias_free; alias_free = alias_free->next; return al; } void free_alias( ALIAS_DATA *al ) { free_string( al->name ); free_string( al->command ); al->next = alias_free; alias_free = al; return; } =========== act_info.c =========== the do_* function add this entire function: /* * aliases by Symposium */ #define LINE_SEPARATOR "\n\r================================================\n\r\n\r" void do_aliases( CHAR_DATA *ch, char *argument ) { ALIAS_DATA *jim; ALIAS_DATA *prev; char buf[MAX_STRING_LENGTH * 3]; char alname[MAX_INPUT_LENGTH]; char *p, *q; int i; char directions[] = "neswudNESWUD"; if( IS_NPC( ch ) ) { send_to_char( "You cant!\n\r", ch ); return; } /* * standard output of alias list */ if( argument[0] == '\0' ) { send_to_char( "Your aliases:\n\r", ch ); if( !ch->pcdata->aliases ) { send_to_char( "None.\n\r", ch ); return; } send_to_char( LINE_SEPARATOR, ch ); for( jim = ch->pcdata->aliases; jim; jim = jim->next ) { sprintf( buf, " %s : \"%s\"\n\r", jim->name, jim->command ); send_to_char( buf, ch ); } send_to_char( LINE_SEPARATOR, ch ); return; } argument = one_argument( argument, alname ); smash_tilde( alname ); smash_tilde( argument ); /* * remove an alias */ if( argument[0] == '\0' ) { for( jim = ch->pcdata->aliases; jim; jim = jim->next ) { if( !str_cmp( jim->name, alname ) ) { if( jim == ch->pcdata->aliases ) { ch->pcdata->aliases = jim->next; } else { for( prev = ch->pcdata->aliases; prev && prev->next != jim; ) prev = prev->next; prev->next = jim->next; } free_alias( jim ); sprintf( buf, "Alias %s removed.\n\r", alname ); send_to_char( buf, ch ); return; } } sprintf( buf, "No alias %s defined.\n\r", alname ); send_to_char( buf, ch ); return; } /* * check if alias is allready used */ for( jim = ch->pcdata->aliases; jim; jim = jim->next ) { if( !str_cmp( jim->name, alname ) ) { send_to_char( "That alias is allready used.\n\r", ch ); return; } } /* * check for recursion in the alias. */ if( !strcmp( alname, "!" ) || !strcmp( alname, "end_of_aliases" ) ) { send_to_char( "Not allowed.\n\r", ch ); return; } i = strlen( alname ); q = argument; while( ( p = strstr( q, alname ) ) ) { q = p + 1; if( !isspace( p[i] ) && p[i] != '=' ) continue; do p--; while( p >= argument && isspace( *p ) ); if( p < argument || *p == '=' ) { send_to_char( "Watch for recursion in your aliases.\n\r", ch ); return; } } /* * finally make the new alias */ jim = new_alias( ); jim->name = str_dup( alname ); jim->command = str_dup( argument ); if( strchr( argument, '%' ) ) jim->complex = ALIAS_COMPLEX; else if( strchr( argument, '=' ) ) jim->complex = ALIAS_MULTIPLE; else { jim->complex = ALIAS_MULTIPLE; for( p = argument; *p; p++ ) { if( !strchr( directions, *p ) ) { jim->complex = ALIAS_SIMPLE; break; } } } jim->next = ch->pcdata->aliases; ch->pcdata->aliases = jim; sprintf( buf, "Alias %s added successfully.\n\r", jim->name ); send_to_char( buf, ch ); return; } ========= save.c ========= procedures for saving and loading of aliases from pfiles /* * Local functions. */ void fwrite_char args( ( CHAR_DATA *ch, FILE *fp ) ); void fwrite_obj args( ( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest ) ); int fread_char args( ( CHAR_DATA *ch, FILE *fp ) ); int fread_obj args( ( CHAR_DATA *ch, FILE *fp ) ); ADD-> int fread_aliases args( ( CHAR_DATA *ch, FILE *fp ) ); // in fwrite_char() void fwrite_char( CHAR_DATA *ch, FILE *fp ) { ADD-> ALIAS_DATA *jim; AFFECT_DATA *paf; int sn; char buf[ 30 ]; ... fprintf( fp, "End\n\n" ); ADD-> ADD-> /* alias */ ADD-> fprintf( fp, "#ALIAS\n" ); ADD-> for( jim = ch->pcdata->aliases; jim; jim = jim->next ) ADD-> { ADD-> fprintf( fp, "%s~ %s~\n", jim->name, jim->command ); ADD-> } ADD-> fprintf( fp, "end_of_aliases~ ~\n\n" ); return; } //in load_char_obj() ch->pcdata->condition[COND_THIRST] = 48; ch->pcdata->condition[COND_FULL] = 48; ch->pcdata->pagelen = 20; ADD-> ch->pcdata->aliases = NULL; ch->pcdata->switched = FALSE; ... SET_BIT( ch->act, PLR_DENY ); return TRUE; } } ADD-> else if ( !str_cmp( word, "ALIAS" ) ) ADD-> { ADD-> if ( fread_aliases ( ch, fp ) ) ADD-> { ADD-> sprintf( buf, ADD-> "Load_char_obj: %s section ALIAS corrupt.\n\r", ADD-> name ); ADD-> bug( buf, 0 ); ADD-> write_to_buffer( d, sorry_player, 0 ); ADD-> ADD-> return FALSE; ADD-> } ADD-> } else if ( !str_cmp( word, "OBJECT" ) ) { if ( !fread_obj ( ch, fp ) ) { //Add this entire function: /* Aliases by Symposium */ int fread_aliases( CHAR_DATA *ch, FILE *fp ) { char * word; int status; ALIAS_DATA * jim; const char directions[] = "neswudNESWUD"; for( ;; ) { word = fread_string( fp, &status ); if ( !word ) { bug( "fread_aliases: Error reading key. EOF?", 0 ); fread_to_eol( fp ); break; } if( !str_cmp( word, "end_of_aliases" ) ) { fread_to_eol( fp ); return 0; } if( word[0] == '#' ) { break; } jim = new_alias( ); jim->name = str_dup( word ); jim->command = fread_string( fp, &status ); if( strchr( jim->command, '%' ) ) jim->complex = ALIAS_COMPLEX; else if( strchr( jim->command, '=' ) ) jim->complex = ALIAS_MULTIPLE; else { jim->complex = ALIAS_MULTIPLE; for( word = jim->command; *word; word++ ) { if( !strchr( directions, *word ) ) { jim->complex = ALIAS_SIMPLE; break; } } } jim->next = ch->pcdata->aliases; ch->pcdata->aliases = jim; } return 1; } =========================================== That's it! Not exactly simple, but I do believe that it will work. I believe that allthough there is room for improvement, there are no glaring problems, I guess the bugs here will be subtle and dangerous, I can't tell really. --Symposium