This is a modification to the LAST command. It maintains a login database which is used to track logins on a daily basis (current function is to show todays logins and yesterdays logins), another feature is the ability to show a specific characters possible alternate characters. Based on the name you search for you will get a list of possible alternate characters for that character. After which a recurssive search is performed, and pulls a list of possible alternate characters from the main list of alternate characters. It will not be very effective at first, but once your login database fills up I am sure you will find it very useful. The alternate character search portion of this modification relies heavily on the fact that players tend to login to their different characters each day. I decided to do the search based on daily logins instead of on every login the database to avoid a potential that two different players login under the same ip address in the course of time. Open mud.h: After: typedef struct teleport_data TELEPORT_DATA; Add: typedef struct last_data LAST_DATA; typedef struct altsearch_data ALT_SEARCH_DATA; After: #define AREA_LIST "area.lst" Add: #define LAST_LIST SYSTEM_DIR "last.lst" After: #define MAX_IGN 10 Add: struct last_data { LAST_DATA * next; LAST_DATA * prev; char * name; char * ip; char * time; char * dayname; sh_int day; sh_int month; sh_int year; }; #define MAX_IP_TRACK 25 #define MAX_ALT 100 #define MAX_RECORDED_LOGONS 20000 struct altsearch_data { char * name; char * ip[MAX_IP_TRACK]; char * recurse_of; // only used in recurssive searching int ip_track[MAX_IP_TRACK]; int dif_ips; int total_matches; }; After: extern PROJECT_DATA * first_project; extern PROJECT_DATA * last_project; Add: extern LAST_DATA * first_on; extern LAST_DATA * last_on; Open comm.c: In void nanny(): After: char *p; Add: LAST_DATA *newlogon; int day = 0, month = 0, year = 0, count; char *strtime; char dayname[MAX_STRING_LENGTH]; char ltime[MAX_STRING_LENGTH]; char buf3[MAX_STRING_LENGTH]; After: do_look( ch, "auto" ); Add: strtime = ctime(¤t_time); count = 0; while ( strtime[0] != '\0' ) { strtime = one_argument(strtime,buf3); switch ( count ) { case 0: //Dayname sprintf( dayname, "%s", buf3 ); case 1: //Month if ( !str_cmp(buf3,"Jan") ) month = 1; else if ( !str_cmp(buf3,"Feb") ) month = 2; else if ( !str_cmp(buf3,"Mar") ) month = 3; else if ( !str_cmp(buf3,"Apr") ) month = 4; else if ( !str_cmp(buf3,"May") ) month = 5; else if ( !str_cmp(buf3,"Jun") ) month = 6; else if ( !str_cmp(buf3,"Jul") ) month = 7; else if ( !str_cmp(buf3,"Aug") ) month = 8; else if ( !str_cmp(buf3,"Sep") ) month = 9; else if ( !str_cmp(buf3,"Oct") ) month = 10; else if ( !str_cmp(buf3,"Nov") ) month = 11; else month = 12; break; case 2: //Day day = atoi(buf3); break; case 3: //Time sprintf( ltime, "%s", buf3 ); break; case 4: //Year year = atoi(buf3); break; } count++; } CREATE( newlogon, LAST_DATA, 1 ); newlogon->dayname = str_dup(capitalize(dayname)); newlogon->day = day; newlogon->month = month; newlogon->year = year; newlogon->time = str_dup(ltime); newlogon->name = str_dup(ch->pcdata->filename); newlogon->ip = str_dup(ch->desc->host); LINK( newlogon, first_on, last_on, next, prev ); write_last_file(); Open db.c: After: PROJECT_DATA * first_project; PROJECT_DATA * last_project; Add: LAST_DATA * first_on; LAST_DATA * last_on; After: void load_projects args( ( void ) ); Add: void load_logons args( ( void ) ); After: log_string ("Loading Projects"); load_projects( ); Add: log_string( "Loading Logons" ); load_logons( ); *** Add the following function to the db.c file: void load_logons( void ) { FILE *fp; LAST_DATA *logon; char filename[256]; char *word; char *line; int month; int day; int year; char name[MAX_STRING_LENGTH]; char ip[MAX_STRING_LENGTH]; char time[MAX_STRING_LENGTH]; char dayname[MAX_STRING_LENGTH]; sprintf( filename, "%s", LAST_LIST ); fp = fopen( filename, "r" ); if ( !fp ) { bug( "ERROR: cannot open last.lst for reading!\n\r", 0 ); return; } fread_word( fp ); //Logins for ( ; ; ) { if ( feof(fp) ) { fclose(fp); return; } word = fread_word( fp ); if ( !str_cmp(word, "END")) { fclose(fp); return; } if ( !str_cmp( word, "ENTRY")) { day=month=year=0; line = fread_line( fp ); sscanf( line, "%s %s %d %d %d %s %s", name, dayname, &day, &month, &year, time, ip ); CREATE( logon, LAST_DATA, 1 ); logon->name = str_dup(name); logon->dayname = str_dup(dayname); logon->day = day; logon->month = month; logon->year = year; logon->time = str_dup(time); logon->ip = str_dup(ip); LINK( logon, first_on, last_on, next, prev ); } } fclose( fp ); } Open save.c: At the top of save.c in the header file declarations add: #include <sys/stat.h> At the top of save.c in the function declarations add: /* from comm.c */ bool check_parse_name args( ( char *name, bool newchar ) ); *** Replace the do_last function in save.c with the following function void do_last( CHAR_DATA *ch, char *argument ) { ALT_SEARCH_DATA altsearch_table [MAX_ALT]; LAST_DATA *logon; char buf [MAX_STRING_LENGTH]; char arg [MAX_INPUT_LENGTH]; char name[MAX_INPUT_LENGTH]; char ipstring[MAX_STRING_LENGTH]; struct stat fst; int cnt, count, start = 0; int day, month, year; day = month = year = 0; argument = one_argument( argument, arg ); if ( arg[0] == '\0' ) { send_to_char( "Usage: last <playername>\n\r", ch ); send_to_char( " : last 'today' or last #\n\r", ch ); return; } if (isdigit(arg[0]) || atoi(arg) == -1 || !str_cmp(arg, "today") || !str_cmp(arg,"yesterday") || !str_cmp(arg,"fromip") || !str_cmp(arg,"searchname") || !str_cmp(arg,"month") ) //View list instead of players { char *todayips[1000]; int todaycnt, x; bool found; todaycnt = -1; send_to_char("Name Time Host/Ip\n\r", ch ); send_to_char("---------------------------------------------------------------------------\n\r", ch); if (!str_cmp(arg, "today")) { day = get_day(); month = get_month(); year = get_year(); cnt = 0; x = 0; for ( x = 0; x < 200; x++ ) todayips[x] = STRALLOC( "" ); todaycnt = 0; x = 0; for ( logon = first_on; logon; logon = logon->next ) { found = FALSE; if ( (logon->day == day) && (logon->month == month) && (logon->year == year) ) { pager_printf( ch, "%-20s %-21s %s\n\r", logon->name, logon->time, logon->ip ); for ( x = 0; x < todaycnt; x++ ) { if ( !str_cmp(todayips[x],logon->ip) ) { found = TRUE; break; } } if ( !found ) { todayips[todaycnt] = STRALLOC( logon->ip ); todaycnt++; } cnt++; } } } else if (!str_cmp(arg,"yesterday")) { day = get_day(); month = get_month(); year = get_year(); cnt = 0; x = 0; for ( x = 0; x < 200; x++ ) todayips[x] = STRALLOC( "" ); todaycnt = 0; x = 0; if ( day == 1 ) { --month; if ( month == 2 ) { if ( year % 4 == 0 ) day = 29; else if ( month == 2 ) day = 28; } else if ( month == 0 ) { // If month was 1, --month would make it 0, change it back to 12 month = 12; day = 31; --year; } else if ( month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 ) day = 31; else day = 30; } else --day; for ( logon = first_on; logon; logon = logon->next ) { found = FALSE; if ( (logon->day == day) && (logon->month == month) && (logon->year == year) ) { pager_printf( ch, "%-20s %-21s %s\n\r", logon->name, logon->time, logon->ip ); for ( x = 0; x < todaycnt; x++ ) { if ( !str_cmp(todayips[x],logon->ip) ) { found = TRUE; break; } } if ( !found ) { todayips[todaycnt] = STRALLOC( logon->ip ); todaycnt++; } cnt++; } } } else if ( !str_cmp(arg,"fromip") ) { cnt = 0; if ( argument[0] == '\0' || !isdigit(argument[0]) ) { send_to_char( "You must specify an ip address to search for.\n\r", ch ); return; } for ( logon = first_on; logon; logon = logon->next ) { start = 0; if ( !str_infix( logon->ip, argument ) ) start++; } start = 50 - start; for ( logon = first_on; logon; logon = logon->next ) { if ( !str_infix( argument, logon->ip ) ) { start++; if ( start >= 0 && cnt < 50 ) { pager_printf( ch, "%-20s %-3s. %2.2d%-2s at %-8s %s\n\r", logon->name, get_monthname(logon->month), logon->day,get_daysuffix(logon->day), logon->time, logon->ip ); cnt++; } } } pager_printf( ch, "There were %d logons from %s.\n\r", cnt, argument ); return; } else if (!str_cmp(arg, "month")) { month = argument[0] == '\0' ? get_month() : atoi(argument); cnt = 0; x = 0; for ( x = 0; x < 200; x++ ) todayips[x] = STRALLOC( "" ); x = 0; for ( logon = first_on; logon; logon = logon->next ) { found = FALSE; if ( logon->month == month ) { cnt++; } } } else if ( !str_cmp(arg,"searchname") ) { cnt = 0; if ( argument[0] == '\0' ) { send_to_char( "You must specify a name to search for.\n\r", ch ); return; } start = 0; for ( logon = first_on; logon; logon = logon->next ) { if ( !str_cmp(argument,logon->name) ) start++; } for ( logon = first_on; logon; logon = logon->next ) { if ( !str_cmp(argument,logon->name) ) { cnt++; if ( cnt >= start-50 ) { pager_printf( ch, "%-20s %-3s. %2.2d%-2s at %-8s %s\n\r", logon->name, get_monthname(logon->month), logon->day, get_daysuffix(logon->day), logon->time, logon->ip ); } } } pager_printf( ch, "There were %d logons from %s.\n\r", cnt, capitalize(argument) ); return; } else { int max = 0; count = atoi(arg); cnt = 0; x = 0; for ( x = 0; x < 1000; x++ ) todayips[x] = STRALLOC( "" ); todaycnt = 0; x = 0; for ( logon = first_on; logon; logon = logon->next ) max++; for ( logon = first_on; logon; logon = logon->next ) { found = FALSE; if ( cnt >= (max-count) ) { pager_printf( ch, "%-20s %-3s. %2.2d%-2s at %-8s %s\n\r", logon->name, get_monthname(logon->month), logon->day, get_daysuffix(logon->day), logon->time, logon->ip ); for ( x = 0; x < todaycnt; x++ ) { if ( !str_cmp(todayips[x],logon->ip) ) { found = TRUE; break; } } if ( !found ) { todayips[todaycnt] = STRALLOC( logon->ip ); todaycnt++; } } cnt++; } } send_to_char("---------------------------------------------------------------------------\n\r", ch ); if ( !isdigit(arg[0]) ) pager_printf( ch, "There were %d logons (%d Unique IPs) %s, %d/%d/%d.\n\r", cnt, todaycnt, arg, month, day, year ); else pager_printf( ch, "There were %d unique IP addresses of the %s logons.\n\r", todaycnt, arg ); return; } if ( !str_cmp(arg, "countips") ) { char *ipcount[2000]; bool recorded = FALSE; int tmp, ips; cnt = 0; ips = -1; for ( tmp = 0; tmp < 2000; tmp++ ) { ipcount[tmp] = STRALLOC( "" ); } for ( logon = first_on; logon; logon = logon->next ) { for ( tmp = 0; tmp < ips; tmp++ ) { if ( !str_cmp(ipcount[tmp],logon->ip) ) { recorded = TRUE; } } if ( !recorded ) { ips++; ipcount[ips] = STRALLOC( logon->ip ); } recorded = FALSE; } ch_printf( ch, "There were %d different IP addresses in the database.\n\r", ips ); return; } if ( !str_cmp(arg,"findalts") ) { LAST_DATA *logon_temp; int alts, alts2, tmp, tmp2; bool recorded; int total, existing, deleted, recursestart; if ( argument[0] == '\0' ) { send_to_char( "You must specify a persons name to find their alts.\n\r", ch ); return; } for ( tmp = 0; tmp < MAX_ALT; tmp++ ) { //CREATE( altsearch_table[tmp], ALT_SEARCH_DATA, 1 ); altsearch_table[tmp].name = STRALLOC( "" ); altsearch_table[tmp].recurse_of = STRALLOC( "" ); for ( tmp2 = 0; tmp2 < MAX_IP_TRACK; tmp2++ ) { altsearch_table[tmp].ip[tmp2] = STRALLOC( "" ); altsearch_table[tmp].ip_track[tmp2] = 0; } altsearch_table[tmp].dif_ips = 0; altsearch_table[tmp].total_matches = 0; } ch_printf( ch, "Possible Alts of %s:\n\r----------------------------------------------------------\n\r", capitalize(argument) ); /* * First loop through every single logon in the database, then re-loop again * and compare ip address of all the other logins from that day * Hierarchal Structure: * *Search Character * --Possible Alt * --IP Address, Record Max 25 Seperate IP addresses * --Record times individual IP address has been recorded * --Record total IP matches */ alts = -1; for ( logon = first_on; logon; logon = logon->next ) { if ( alts >= (MAX_ALT-1) ) break; if ( !str_cmp( logon->name, argument ) ) { // Have we already done a search for the argument on this day? if ( day == logon->day && month == logon->month && year == logon->year ) { continue; } day = logon->day; month = logon->month; year = logon->year; sprintf( ipstring, "%s", logon->ip ); for ( logon_temp = first_on; logon_temp; logon_temp = logon_temp->next ) { if ( (logon_temp->day == day) && (logon_temp->month == month) && (logon_temp->year == year) && str_cmp( logon_temp->name, argument ) ) { if ( !str_cmp(ipstring,logon_temp->ip) ) { recorded = FALSE; for ( tmp = 0; tmp <= alts && tmp < MAX_ALT; tmp++ ) { if ( !str_cmp( altsearch_table[tmp].name, logon_temp->name ) ) { bool ip_found = FALSE; for ( tmp2 = 0; tmp2 < altsearch_table[tmp].dif_ips && tmp2 < MAX_IP_TRACK; tmp2++ ) { if ( !str_cmp( altsearch_table[tmp].ip[tmp2], logon_temp->ip ) ) { altsearch_table[tmp].ip_track[tmp2]++; ip_found = TRUE; } } if ( !ip_found ) { altsearch_table[tmp].ip[altsearch_table[tmp].dif_ips] = STRALLOC(ipstring); altsearch_table[tmp].dif_ips++; } altsearch_table[tmp].total_matches++; recorded = TRUE; } } if ( !recorded ) { alts++; altsearch_table[alts].name = STRALLOC(logon_temp->name); altsearch_table[alts].ip[0] = STRALLOC(ipstring); altsearch_table[alts].dif_ips++; altsearch_table[alts].total_matches++; } } } } } } /* * Recursive searching added, basically the first for loop finds all the alts * for the name given, lets say for example you type 'last findalts fuma' and * two alts come up: Behrditz and Sark, now we are gonna redo the search based * on the names that came up, and see if any new names pop up, but make sure * not to include the original name used. */ day = month = year = 0; recursestart = alts; if ( alts != -1 ) { alts2 = alts; for ( tmp2 = 0; tmp2 <= alts2 && tmp2 <= MAX_ALT; tmp2++ ) { for ( logon = first_on; logon; logon = logon->next ) { if ( alts >= (MAX_ALT-1) ) break; // Have we already done a search for the argument on this day? if ( day == logon->day && month == logon->month && year == logon->year ) { continue; } if ( !str_cmp( logon->name, altsearch_table[tmp2].name ) ) { day = logon->day; month = logon->month; year = logon->year; sprintf( ipstring, "%s", logon->ip ); for ( logon_temp = first_on; logon_temp; logon_temp = logon_temp->next ) { if ( (logon_temp->day == day) && (logon_temp->month == month) && (logon_temp->year == year) && str_cmp( logon_temp->name, altsearch_table[tmp2].name ) ) { if ( !str_cmp(ipstring,logon_temp->ip) ) { recorded = FALSE; for ( tmp = 0; tmp <= alts && tmp < MAX_ALT; tmp++ ) { if ( !str_cmp(altsearch_table[tmp].name, logon_temp->name) ) recorded = TRUE; } if ( !recorded ) { if ( str_cmp( logon_temp->name, argument ) ) { int track; bool ipfound = FALSE; alts++; altsearch_table[alts].name = STRALLOC(logon_temp->name); altsearch_table[alts].recurse_of = STRALLOC(altsearch_table[tmp2].name); for ( track = 0; track < MAX_IP_TRACK; track++ ) { if ( !str_cmp( altsearch_table[alts].ip[track], ipstring ) ) { ipfound = TRUE; altsearch_table[alts].ip_track[track]++; } } if ( !ipfound ) { altsearch_table[alts].ip[altsearch_table[alts].dif_ips] = STRALLOC(ipstring); altsearch_table[alts].dif_ips++; } } } } } } } } } } if ( alts != -1 ) { bool exists, recurse = FALSE; total = 0; existing = 0; deleted = 0; for ( tmp = 0; tmp <= alts; tmp++ ) { exists = exists_player(altsearch_table[tmp].name); if ( exists ) existing++; else deleted++; if ( tmp != 0 && tmp == recursestart ) { recurse = TRUE; pager_printf( ch, "------------Recurssive Matches------------\n\r" ); } pager_printf( ch, "->%-24s %4d %4d %10s\n\r", format( "%s %s%s%s", altsearch_table[tmp].name, recurse ? "[" : "", recurse ? altsearch_table[tmp].recurse_of : "", recurse ? "]" : "" ), altsearch_table[tmp].total_matches, altsearch_table[tmp].dif_ips, exists ? "[Exists]" : "[Deleted]" ); total++; } ch_printf( ch, "----------------------------------------------------------\n\r" ); ch_printf( ch, "%d Total Possible Alts: %d still exist, %d are deleted.\n\r", total, existing, deleted ); } else { ch_printf( ch, "No possible alternate characters found.\n\r" ); } return; } strcpy( name, capitalize(arg) ); sprintf( buf, "%s%c/%s", PLAYER_DIR, tolower(arg[0]), name ); if ( stat( buf, &fst ) != -1 && check_parse_name( capitalize(name), FALSE )) sprintf( buf, "%s was last on: %s\r", name, ctime( &fst.st_mtime ) ); else sprintf( buf, "%s was not found.\n\r", name ); send_to_char( buf, ch ); } Add the following functions to your file of choice: * Whichever file you choose insert the appropriate global declarations in mud.h eg. void write_last_file args( ( void ) ); int get_day args( ( void ) ); int get_month args( ( void ) ); int get_year args( ( void ) ); char * get_monthname args( ( int month ) ); char * get_daysuffix args( ( int day ) ); bool exists_player args( ( char *name ) ); char * format args( ( char *fmt, ... ) ); void write_last_file( ) { FILE *fp; char filename[MAX_INPUT_LENGTH]; LAST_DATA *logon; int count; sprintf(filename, "%s", LAST_LIST); fp = fopen( filename, "w" ); if ( !fp ) { bug("Cannot open: %s for writing", filename); return; } count = 0; for ( logon = first_on; logon; logon = logon->next ) count++; while ( count > MAX_RECORDED_LOGONS ) { logon = first_on; logon->name = str_dup(""); logon->ip = str_dup(""); logon->time = str_dup(""); logon->dayname = str_dup(""); logon->day = 0; logon->month = 0; logon->year = 0; UNLINK(logon, first_on, last_on, next, prev ); DISPOSE(logon); count--; } fprintf( fp, "LOGINS\n" ); for ( logon = first_on; logon; logon = logon->next ) { fprintf( fp, "ENTRY %s %s %d %d %d %s %s\n", logon->name, logon->dayname, logon->day, logon->month, logon->year, logon->time, logon->ip ); } fprintf( fp, "END\n" ); fclose( fp ); return; } int get_day() { char buf[MAX_STRING_LENGTH]; char *timestr; int count, day; timestr = ctime(¤t_time); day = 0; count = 0; while ( timestr[0] != '\0' ) { timestr = one_argument(timestr,buf); switch ( count ) { case 2: day = atoi(buf); break; } count++; } return day; } int get_month() { char buf[MAX_STRING_LENGTH]; char *timestr; int count, month; timestr = ctime(¤t_time); month = 0; count = 0; while ( timestr[0] != '\0' ) { timestr = one_argument(timestr,buf); switch ( count ) { case 1: //Month if ( !str_cmp(buf,"Jan") ) month = 1; else if ( !str_cmp(buf,"Feb") ) month = 2; else if ( !str_cmp(buf,"Mar") ) month = 3; else if ( !str_cmp(buf,"Apr") ) month = 4; else if ( !str_cmp(buf,"May") ) month = 5; else if ( !str_cmp(buf,"Jun") ) month = 6; else if ( !str_cmp(buf,"Jul") ) month = 7; else if ( !str_cmp(buf,"Aug") ) month = 8; else if ( !str_cmp(buf,"Sep") ) month = 9; else if ( !str_cmp(buf,"Oct") ) month = 10; else if ( !str_cmp(buf,"Nov") ) month = 11; else month = 12; break; } count++; } return month; } int get_year() { char buf[MAX_STRING_LENGTH]; char *timestr; int count, year; timestr = ctime(¤t_time); year = 0; count = 0; while ( timestr[0] != '\0' ) { timestr = one_argument(timestr,buf); switch ( count ) { case 4: year = atoi(buf); break; } count++; } return year; } char * get_monthname( int month ) { switch ( month ) { case 1: return "Jan"; break; case 2: return "Feb"; break; case 3: return "Mar"; break; case 4: return "Apr"; break; case 5: return "May"; break; case 6: return "Jun"; break; case 7: return "Jul"; break; case 8: return "Aug"; break; case 9: return "Sep"; break; case 10: return "Oct"; break; case 11: return "Nov"; break; case 12: return "Dec"; break; default: return "Err"; break; } } char * get_daysuffix( int day ) { if ( day > 4 && day < 20 ) return "th"; else if ( day % 10 == 1 ) return "st"; else if ( day % 10 == 2 ) return "nd"; else if ( day % 10 == 3 ) return "rd"; else return "th"; } bool exists_player( char *name ) { struct stat fst; char buf[MAX_STRING_LENGTH]; CHAR_DATA *victim; /* Stands to reason that if there ain't a name to look at, they damn well don't exist! */ if( !name || !str_cmp( name, "" ) ) return FALSE; sprintf( buf, "%s%c/%s", PLAYER_DIR, tolower( name[0] ), capitalize( name ) ); if( stat( buf, &fst ) != -1 ) return TRUE; else if( ( victim = get_char_world( supermob, name ) ) != NULL ) return TRUE; return FALSE; } char *format( char *fmt, ... ) { static char newstring[MAX_INPUT_LENGTH]; char buf[MAX_STRING_LENGTH*2]; /* better safe than sorry */ va_list args; newstring[0] = '\0'; if (fmt[0] == '\0') return " "; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); if (buf[0] == '\0') return " "; strcpy( newstring, buf); return newstring; }