/**************************************************************************/ // comm.cpp - main(), IO loops, lots of OS related code. /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with all the licenses * * in licenses.txt... In particular, you may not remove this copyright * * notice. * *************************************************************************** * >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe. * * >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to * * you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com), * * Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) * * >> Oblivion 1.2 is copyright 1996 Wes Wagner * **************************************************************************/ #include "network.h" #include "comm.h" #include "colour.h" #include "nanny.h" #include "hreboot.h" #include "dynamics.h" // create_class #include "help.h" #include "channels.h" #ifdef WIN32 #include "process.h" #endif #ifdef IMC #include "imc.h" #endif int main_argc; char **main_argv; const unsigned char echo_off_str [] = { IAC, WILL, TELOPT_ECHO, '\0' }; const unsigned char echo_on_str [] = { IAC, WONT, TELOPT_ECHO, '\0' }; const unsigned char go_ahead_str [] = { IAC, GA, '\0' }; void ispell_init(); void ispell_done(); void bust_a_group_prompt( char_data *ch); void bust_a_prompt( connection_data *d ); void flush_cached_write_to_buffer(connection_data *c); bool listen_on_specified_on_commandline=false; // local prototypes void process_input(connection_data *c); void examine_last_command(); #ifndef FNDELAY #define FNDELAY O_NDELAY #endif extern char current_logfile_name[MSL]; /**************************************************************************/ // visual debug variables char *visual_debug_next_connection_autoon_ip=NULL; int visual_debug_next_connection_column_width; bool visual_debug_next_connection_hexoutput; /**************************************************************************/ // netio.cpp related protocols and variables extern bool listen_on_source_text_set; void netio_parse_listen_on(const char * listen_on); void netio_allocate_bind_ports(int main_port); void netio_parsed_listen_on_output(); void netio_binded_listen_on_output(); void netio_bind_connections(); void netio_init_fd_set_groups(); void netio_check_for_and_accept_new_connections(); void netio_poll_connections(); void netio_process_exceptions_from_polled_connections(); void netio_process_input_from_polled_connections(); void netio_process_output_from_polled_connections(); void resolver_poll_and_process(); // resolver.cpp void hotreboot_poll_and_process(); // hreboot.cpp /**************************************************************************/ void update_alarm() { #ifdef unix alarm_update(); #endif } /**************************************************************************/ // used to update the current time while the mud is booting void update_currenttime(void) { struct timeval last_time; gettimeofday( &last_time, NULL ); current_time = (time_t) last_time.tv_sec; } /************************************************************************/ // returns true if the file exists bool file_exists(const char * fmt, ...) { char buf[1024]; bool exists=false; FILE *fp; va_list args; va_start (args, fmt); vsnprintf(buf, 1024, fmt, args); va_end (args); if(fpReserveFileExists){ fclose( fpReserveFileExists); } if( ( fp = fopen( buf, "r" ) ) != NULL ) { exists=true; fclose( fp ); } fpReserveFileExists= fopen( NULL_FILE, "r" ); return exists; } /**************************************************************************/ bool check_directories(int argc, char **argv) { char current_dir[MSL]; bool problem= false; int i; // Get the current working directory if( getcwd( current_dir, MSL ) == NULL ){ bugf("check_directories(): can't get current directory - error %d (%s)", errno, strerror( errno)); } for (i=0; str_len(directories_table[i].directory)+ str_len(directories_table[i].text) >0; i++) { if( chdir( directories_table[i].directory ) ) { if(str_len(directories_table[i].directory)>0) { logf( "no directory %-16s - used for '%s'", directories_table[i].directory, directories_table[i].text); problem= true; } } if( chdir( current_dir ) ) { logf( "UNABLE TO CHANGE BACK TO THE DEFAULT DIRECTORY!!!\n%s\n", current_dir); problem= true; } } if(problem){ logf("\nTo create all these directories start the mud as:\n" "'%s --createdirs'\nfrom within '%s'\n\n", argv[0], current_dir); } return problem; } /**************************************************************************/ // return true if there was a problem bool create_directories(void) { char current_dir[MSL]; bool problem= false; int i; log_string("=== starting in create directories mode:"); // Get the current working directory if( getcwd( current_dir, MSL ) == NULL ){ bugf("create_directories(): can't get current directory - error %d (%s)", errno, strerror( errno)); } for (i=0; str_len(directories_table[i].directory)+ str_len(directories_table[i].text) >0; i++) { if(chdir( directories_table[i].directory )){ if(str_len(directories_table[i].directory)>0) { char dbuf[MIL]; sprintf(dbuf, "mkdir %s", directories_table[i].directory); logf( "creating dir %-15s - used for '%s'", directories_table[i].directory, directories_table[i].text); #ifdef WIN32 if( mkdir( directories_table[i].directory ) ) #else if(system (dbuf)) #endif { logf( "could not create dir %-8s - used for '%s'", directories_table[i].directory, directories_table[i].text); problem= true; } } } if( chdir( current_dir ) ) { bugf( "UNABLE TO CHANGE BACK TO THE DEFAULT DIRECTORY!!!\n%s\n", current_dir); problem= true; } } if(problem) { logf("\nThe code couldn't create the above directory/directories for you...\n" "see if you can fix the problem manually.\n"); } return problem; } /**************************************************************************/ char *get_current_working_directory() { static char cwdbuf[MSL]; getcwd(cwdbuf, MSL); return cwdbuf; }; /**************************************************************************/ void init_reserved_files() { // Reserve three channels for our use. if( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL ) { bugf("init_reserved_files():1 fopen '%s' for read failed - error %d (%s)", NULL_FILE, errno, strerror( errno)); exit_error( 1 , "init_reserved_files", "fopen for read failed"); } if(!fpAppend2FilReserve){ if( ( fpAppend2FilReserve = fopen( ANULL_FILE, "r" ) ) == NULL ) { bugf("init_reserved_files():2 fopen '%s' for read failed - error %d (%s)", ANULL_FILE, errno, strerror( errno)); exit_error( 1 , "init_reserved_files", "fopen for read 2 failed"); } } if(!fpReserveFileExists){ if( ( fpReserveFileExists = fopen( NULL_FILE, "r" ) ) == NULL ) { fprintf(stderr, "init_reserved_files():3 fopen '%s' for read failed - error %d (%s)", NULL_FILE, errno, strerror( errno)); bugf("init_reserved_files():3 fopen '%s' for read failed - error %d (%s)", NULL_FILE, errno, strerror( errno)); exit_error( 1 , "init_reserved_files", "fopen for read 3 failed"); } } } #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif #ifdef HAVE_SYS_UTSNAME_H #include <sys/utsname.h> #endif #ifdef HAVE_SYS_SYSCTL_H #include <sys/sysctl.h> #endif /**************************************************************************/ void display_host_info() { logf("\n%s", fix_string(get_compile_time(true))); logf("We are running on %s.", MACHINE_NAME); logf( "The current working directory is %s.", get_current_working_directory() ); if(!resolver_running){ logf( "The hostname/ident resolver is not currently running."); }else{ if(resolver_version==0){ resolver_poll_and_process(); } logf( "The hostname/ident resolver is currently running."); logf("Resolver version = %d.%03d", resolver_version/1000, resolver_version%1000); if(resolver_version<1500){ log_notef("RESOLVER VERSION 1.500 OR HIGHER IS REQUIRED FOR DNS " "RESOLUTION OR NO RESOLVER AT ALL! Please delete the " "existing resolver binary and recompile the version which " "came with the source (in src/extras/resolver.*). If you " "are unable to do this, the mud will actually bootup without " "a resolver at all (no resolver is better than an old one)."); exit_error( 1 , "display_host_info", "resolver version too old"); } } // obtain the platform information PLATFORM_INFO[0]='\0'; #ifdef WIN32 OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); strncpy(PLATFORM_INFO, FORMATF("PlatformID: %s v%d.%d.%d [%s]", ( osvi.dwPlatformId==VER_PLATFORM_WIN32s ? "Win32s": osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS ? "Windows9x": osvi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "WindowsNT": FORMATF("Unknown(%d)",osvi.dwPlatformId) ), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, osvi.szCSDVersion), 512); #elif defined(HAVE_SYS_UTSNAME_H) { struct utsname name; if(uname(&name)==-1){ bugf("display_host_info(): couldn't retrieve the system details."); sprintf(PLATFORM_INFO, "PlatformID: Unknown-uname_error%d", errno); }else{ sprintf(PLATFORM_INFO, "PlatformID: sysname='%s' nodename='%s' " "release='%s' version='%s' machine='%s'", name.sysname, name.nodename, name.release, name.version, name.machine); } } # if defined HAVE_SYS_SYSCTL_H && defined(CTL_KERN) && defined(KERN_VERSION) { // tag on the KERN_VERSION info if available int mib[2]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_VERSION; char kernver[512]; len=sizeof(kernver); if(sysctl(mib, 2, &kernver, &len, NULL, 0)==0){ kernver[sizeof(kernver) - 1] = '\0'; char *tempstring=str_dup(kernver); tempstring=string_replace_all(tempstring, "\r", ""); tempstring=string_replace_all(tempstring, "\n", ""); strcat(PLATFORM_INFO," kernver='"); strcat(PLATFORM_INFO, tempstring); strcat(PLATFORM_INFO,"'"); free_string(tempstring); } } # endif #elif defined(__CYGWIN__) strcpy(PLATFORM_INFO, "PlatformID: Cygwin."); #elif defined(__APPLE__) && defined(__MACH__) strcpy(PLATFORM_INFO, "PlatformID: MacOSX."); #else strcpy(PLATFORM_INFO, "PlatformID: Unknown."); #endif PLATFORM_INFO[2047]='\0'; log_string(PLATFORM_INFO); } /**************************************************************************/ // prototypes void init_string_space(); void init_mm( ); void do_load_gamesettings(char_data *ch, char *); char max_count_ip_buf[MIL]; int max_count_ip; /**************************************************************************/ void deallocate_all_memory(); extern char *top_string; extern char *string_space; char *dawnstat_url_encode_post_data(char *postdata); void dawnstat_update(); /**************************************************************************/ void prevent_mud_running_as_root() { #ifdef unix // code to prevent the mud running as root if(getuid()==0){ bugf("DO NOT RUN THE MUD AS ROOT!!! THIS IS A SECURITY RISK!!!\r\n" "====CREATE A USER ACCOUNT TO RUN THE MUD UNDER."); log_release_held_logs(); exit_error( 1 , "prevent_mud_running_as_root", "trying to run mud as root"); } #endif } /**************************************************************************/ void display_commandline_options(int argc, char **argv ) { log_string("======= Command-Line Options:"); log_string("=== General Commands:"); log_string("-?, /?, /h, -h, --help display this message then exit."); log_string("-v, /v or --version just the version header then exit."); log_string("--testboot start the mud up in testboot mode."); log_string("--createdirs create the required directory structure."); log_string("-q, --quiet don't display any text to stdout during startup."); log_string("-nb, --nobackground don't fork to run as a background process."); log_string("-nl, --nologfile don't write to a logfile."); log_string("-lc, --logconsole always log to the console (win32 default)."); #ifdef WIN32 log_string("-nolc, --nologconsole turn off console after logging is established."); #endif log_string("-f, --foreground --nobackground and --logconsole."); logf("'%s -f' IS RECOMMENDED TO TEST STARTUP PROBLEMS.", argv[0]); // undocumented: log_string("--create_empty_class start with no classes, and create 'classless' class structure."); log_string(""); log_string("=== other options relating to startup:"); logf("syntax: %s [primaryport_number [listen_on_setting]] ", argv[0]); // undocumented: log_string("--hotreboot=# we are hotrebooting, talk via the IPC pipe #."); log_string(""); log_string("Where the primary port number is in the range 1024 to 65535"); log_string("The listen_on_setting if unspecified will use the listen_on entry from"); logf("within: %s", GAMESETTINGS_FILE); log_string("This listen_on entry defaults to 'telnet://:+0,http://:+1'"); } /**************************************************************************/ extern bool log_hold_log_string_core_stdout_restore_value; extern bool commandlineoption_no_background_process; extern bool commandlineoption_no_logfile; extern bool commandlineoption_log_console; bool commandlineoption_quiet=false; /**************************************************************************/ // return true if booting should continue bool parse_commandline_options(int argc, char **argv ) { fBootTestOnly = false; bool mainport_found=false; mainport = 0; parsed_mainport=0; // display the program header logf("\n%s", fix_string(get_compile_time(true))); int i; for(i=1; i<argc; i++) { // -v, /V, --version if( !str_cmp("-v", argv[i]) || !str_cmp("/v", argv[i]) || !str_cmp("--version", argv[i]) ) { // the header is always displayed by the top of this function return false; } // --help, -h, /h, -?, /? if( !str_cmp("-?", argv[i]) || !str_cmp("/?", argv[i]) || !str_cmp("/h", argv[i]) || !str_cmp("-h", argv[i]) || !str_cmp("--help", argv[i]) ) { display_commandline_options(argc, argv); return false; } // --testboot if(!str_cmp("--testboot", argv[i]) ) { fBootTestOnly=true; log_string("starting in testboot mode."); continue; } // -q, --quiet if( !str_cmp("-q", argv[i]) || !str_cmp("--quiet", argv[i]) ) { commandlineoption_quiet=true; log_hold_log_string_core_stdout_restore_value=false; log_string("starting in quiet mode, no logging to stdout."); if(commandlineoption_log_console){ log_string("The quiet mode doesn't make much sense if the log console option has been selected."); return false; } continue; } // -nb, --nobackground if( !str_cmp("-nb", argv[i]) || !str_cmp("--nobackground", argv[i]) ) { #ifdef unix logf("%s option specified - the mud will remain as a foreground process.",argv[i]); #else logf("%s option specified - this option has no effect on this platform.",argv[i]); #endif commandlineoption_no_background_process=true; continue; } // -nl, --nologfile if( !str_cmp("-nl", argv[i]) || !str_cmp("--nologfile", argv[i]) ) { logf("%s option specified - no logfile will be generated.",argv[i]); commandlineoption_no_logfile=true; continue; } // -lc, --logconsole if( !str_cmp("-lc", argv[i]) || !str_cmp("--logconsole", argv[i]) ) { logf("%s option specified - logging will continue to the console.",argv[i]); commandlineoption_log_console=true; if(commandlineoption_quiet){ log_string("The log console option doesn't make much sense if the quiet option has been selected."); log_hold_log_string_core_stdout_restore_value=true; return false; } continue; } // -nolc, --nologconsole if( !str_cmp("-nolc", argv[i]) || !str_cmp("--nologconsole", argv[i]) ) { logf("%s option specified - logging wont continue to the console.",argv[i]); commandlineoption_log_console=false; continue; } // -f, --foreground if( !str_cmp("-f", argv[i]) || !str_cmp("--foreground", argv[i]) ) { logf("%s option specified - setting no background and log console options.",argv[i]); commandlineoption_no_background_process=true; commandlineoption_log_console=true; if(commandlineoption_quiet){ log_string("The foreground option doesn't make much sense if the quiet option has been selected."); log_string("Try using -q -nb if you want the mud to run in the foreground with no console logging."); log_hold_log_string_core_stdout_restore_value=true; return false; } continue; } // --createdirs if(!str_cmp("--createdirs", argv[i]) ){ if(create_directories()){ log_string("There may have been problems creating the directories...\n" "create them manually then try again."); }else{ log_string("Directory creation completed successfully...\n" "Start the mud normally to continue."); } return false; } if(!str_cmp("--create_empty_class", argv[i]) ){ // first do a safety check if(file_exists(CLASSES_LIST)){ logf("Creation of sample "CLASSES_LIST" file option selected - file already exists!\n" "******* delete/rename file first if you really want to do this, then try again."); return false; } logf("Creating sample "CLASSES_LIST" file..."); create_class("classless"); do_write_classes(NULL,""); return false; } // --hotreboot=# if(!strncmp("--hotreboot=", argv[i], str_len("--hotreboot="))){ char *num=argv[i]; num+=str_len("--hotreboot="); if(!is_number(num)){ logf("%s startup error with hotreboot parameter '%s'", argv[0], argv[i]); return false; } int ipcvalue=atoi(num); hotreboot_reassign_child_pipe(ipcvalue); hotreboot_in_progress = true; // with hotreboot, we ignore all subsequent parameters // since the pipe tells us what port we are bound to etc return true; } // if we get here, we must be reading the port number, and possibly followed by // a listen_on setting if(mainport_found){ // check for a listen on setting netio_parse_listen_on(argv[i]); listen_on_specified_on_commandline=true; continue; }else{ // parse the port number if(!is_number(argv[i])){ logf("Invalid parameter '%s'... expecting port number.", argv[i]); logf("Type '%s --help' for syntax documentation.", argv[0]); return false; } parsed_mainport=atoi(argv[i]); if(parsed_mainport<1024 || parsed_mainport>65535){ logf("Invalid port value '%s'... expecting value in range 1024 to 65535.", argv[i]); logf("Type '%s --help' for syntax documentation.", argv[0]); return false; } mainport_found=true; continue; } } return true; } /**************************************************************************/ int main( int argc, char **argv ) { log_hold_till_commandline_options_parsed(); // necessary to make --quiet work main_argc=argc; main_argv=argv; runlevel=RUNLEVEL_BOOTING; // we are starting up update_currenttime(); // don't let anyone host a mud as root prevent_mud_running_as_root(); if(!parse_commandline_options(argc, argv)){ mainport=parsed_mainport; log_release_held_logs(); // necessary to make --quiet work // if the function returns false, we need to exit // this can be because invalid options were provided // or a parameter like --version was used. return 0; } // check all the directories we expect to see exist if(check_directories(argc, argv)){ log_release_held_logs(); // necessary to make --quiet work exit_clean(1, "main", "check_directories() wants an exit"); } mainport=parsed_mainport; log_release_held_logs(); // necessary to make --quiet work // reserve the file descriptors we will to ensure don't get used up init_reserved_files(); // read in the game settings do_load_gamesettings(NULL,""); // if the mainport wasn't specified earlier on the command line, use the default value if(mainport==0){ mainport = default_mud_port; logf("no mainport value specified on command line, using default value of %d", default_mud_port); } logf("mainport set to %d", mainport); init_network(); // startup winsock on WIN32 etc // initialise global variables - in global.c init_globals(argv[0]); if(hotreboot_in_progress){ sleep_seconds(1); hotreboot_init_receive(); } netio_init_fd_set_groups(); if(!hotreboot_in_progress){ if(!listen_on_source_text_set){ netio_parse_listen_on(game_settings->listen_on); } netio_allocate_bind_ports(mainport); netio_parsed_listen_on_output(); } // bind the ports unless we have already // (in the case of a hotreboot etc) if(!hotreboot_in_progress && !fBootTestOnly){ netio_bind_connections(); } // init the resolver if( !hotreboot_in_progress && !resolver_running && !fBootTestOnly){ resolver_init( argv[0] ); resolver_poll_and_process(); } init_alarm_handler(); ispell_init(); boot_db(); update_alarm(); // get the response to the version query etc resolver_poll_and_process(); display_host_info(); if(fBootTestOnly) { log_string( "Boot test completed OK!!!"); last_command[0] = '\0'; last_input[0] = '\0'; fclose(fpReserve); fclose(fpAppend2FilReserve); fclose(fpReserveFileExists); exit_clean(0, "main", "successful boot test"); return 0; } logf("Free stringspace =%d.", (int)(&string_space[MAX_STRING - MSL]-top_string )); logf("%s is ready to rock.", MUD_NAME); logf("Logging to %s", current_logfile_name); logf("Mud is running in the %s with a process id of %d", commandlineoption_no_background_process?"foreground":"background", getpid()); if(commandlineoption_no_background_process){ logf("Pressing ctrl+c will terminate the mud process (unless you have hotrebooted)"); } examine_last_command(); install_other_handlers(); // lastcommand debugging update_alarm(); if(hotreboot_in_progress){ hotreboot_game_environment_transfer(); } update_alarm(); netio_binded_listen_on_output(); dawnlog_write_index(FORMATF("dawn bindings %s", netio_return_binded_sockets())); runlevel=RUNLEVEL_MAIN_IO_LOOP; #ifdef IMC imc_startup( false, -1, false ); #endif game_loop(); // closesocket(control); // closesocket(irc_control); close_network(); #ifdef IMC imc_shutdown( false ); #endif // now clean up the memory deallocate_all_memory(); // That's all, folks. log_string( "Normal termination of game." ); exit_clean(0, "main", "normal game termination"); return 0; } /**************************************************************************/ // write the file to indicate a shutdown void write_shutdown_file(char_data *ch) { char buf[MSL]; // Setup the filename to shutdown to sprintf( shutdown_filename, ADMIN_LOGS_DIR"sd%d.txt", mainport ); // record shutdown details sprintf( buf, "at %s %s was shutdown by %s.\n===========================", (char *) ctime( ¤t_time ), shutdown_filename, ch?ch->name:"(NULL)"); append_file( ch, SHUTDOWN_FILE, buf ); append_file( ch, shutdown_filename, buf ); } /**************************************************************************/ extern bool log_string_core_stdout_enabled; bool caught_exit_in_progress=false; /**************************************************************************/ void exit_clean(int exitcode, char *function, char *message) { last_command[0] = '\0'; last_input[0] = '\0'; caught_exit_in_progress=true; signal(SIGSEGV, SIG_DFL); // disable the use of the nasty signal handler exit(exitcode); } /**************************************************************************/ extern char last_bug[MSL*4+1]; /**************************************************************************/ void exit_error(int exitcode, char *function, char *message) { caught_exit_in_progress=true; signal(SIGSEGV, SIG_DFL); // disable the use of the nasty signal handler if(runlevel==RUNLEVEL_BOOTING){ if(!commandlineoption_log_console && !log_string_core_stdout_enabled) { fprintf(stderr, "Mud failed to complete the startup process\n" "The startup process was terminated by %s()\n" "With a message of: %s\n", function, message); fprintf(stderr, "The last bug text in the log reads:\n%s\n", last_bug); fprintf(stderr, "This may or may not be related to why the mud didn't complete starting up.\n"); if(commandlineoption_no_logfile){ fprintf(stderr, "try starting the mud up with the -f switch to see more details.\n"); }else{ fprintf(stderr, "==================================================\n" " For more details, either review the logfile or \n" " start the mud with the -f commandline option. \n" "==================================================\n"); } // update the dawnlog dawnlog_write_index(FORMATF("Mud failed to complete the startup process.\n" "The startup process was terminated by %s()\n" "With a message of: %s", function, message)); dawnlog_write_index(FORMATF("The last bug text in the log reads:\n%s", last_bug)); dawnlog_write_index(FORMATF("This may or may not be related to why the mud didn't complete " "starting up, review the main log for more details.")); } } exit(exitcode); } /**************************************************************************/ void write_last_command(void) { static int callcount; int i; if(caught_exit_in_progress){ return; } if(runlevel==RUNLEVEL_BOOTING){ if(!commandlineoption_log_console && !log_string_core_stdout_enabled) { if(++callcount>1){ fprintf(stderr, "[%d] write_last_command(): callcount=%d", getpid(), callcount); } if(callcount>5){ fprintf(stderr, "[%d] write_last_command(): callcount>5!\n",getpid()); caught_exit_in_progress=true; exit(5); } } return; } // Return if no last command - set before normal exit if(IS_NULLSTR(last_command) && IS_NULLSTR(last_input)) return; logf("[%d] write_last_command(): callcount=%d", getpid(), callcount); callcount++; if(callcount>5){ bugf("[%d] write_last_command(): callcount>5!\n", getpid()); exit(5); } if(!IS_NULLSTR(last_command)){ logf("last_command: %s", last_command); append_string_to_file( LASTCMD_FILE, last_command, true); } if(!IS_NULLSTR(last_input)){ logf("last_input: %s", last_input); append_string_to_file( LASTCMD_FILE, last_input, true); } // output the inputtail { bool found=false; logf("======INPUTTAIL LOG"); append_string_to_file( LASTCMD_FILE, "======INPUTTAIL LOG", true); for(i=(inputtail_index+1)%MAX_INPUTTAIL; i!=inputtail_index; i= (i+1)%MAX_INPUTTAIL){ if(!IS_NULLSTR(inputtail[i])){ append_string_to_file( LASTCMD_FILE, inputtail[i], true); log_string(inputtail[i]); found=true; } } if(!IS_NULLSTR(inputtail[inputtail_index])){ append_string_to_file( LASTCMD_FILE, inputtail[i], true); log_string(inputtail[i]); found=true; } if(!found){ append_string_to_file( LASTCMD_FILE, "No inputtail data to dump", true); log_string("No inputtail data to dump"); }else{ append_string_to_file( LASTCMD_FILE, "R = Room vnum, C = Connected state, E = olc editor mode... inputtail does not include force or ordered commands.", true); log_string("R = Room vnum, C = Connected state, E = olc editor mode... inputtail does not include force or ordered commands."); } logf("%s", fix_string(get_compile_time(false))); append_string_to_file( LASTCMD_FILE, fix_string(get_compile_time(false)), true); } } /**************************************************************************/ void netio_close_all_binded_sockets(); /**************************************************************************/ void nasty_signal_handler(int i) { logf("starting nasty_signal_handler (%d)", i); write_last_command(); #ifdef WIN32 WSACleanup(); #endif signal(i, SIG_DFL); netio_close_all_binded_sockets(); if(game_settings->config_create_coredump_at_end_of_nasty_signal_handler){ do_abort(); } return; } /**************************************************************************/ // Called before starting the game_loop void install_other_handlers() { last_command [0] = '\0'; last_input [0] = '\0'; logf("installing atexit and signal handlers"); if(atexit(write_last_command) != 0) { bugf("install_other_handlers:atexit - error %d (%s)", errno, strerror( errno)); exit_error( 1 , "install_other_handlers", "atexit installation failed"); } // should probably check return code here signal(SIGSEGV, nasty_signal_handler); // Possibly other signals could be caught? } /**************************************************************************/ // Called after booting the database // Find out the last thing that happened before a crash void examine_last_command() { FILE *fp; char buf[MSL]; char buf2[MSL]; char working_buf[MSL]; char fullfile_buf[MSL+5]; fp = fopen(LASTCMD_FILE, "r"); if(!fp) return; logf("examine_last_command(): reading details of lastcmd.txt into notes system"); fscanf(fp, "%[^\n]\n", buf); logf("from lastcmd.txt: '%s'", buf); fscanf(fp, "%[^\n]\n", buf2); logf("from lastcmd.txt: '%s'", buf2); // fullfile_buf strcpy(fullfile_buf, buf); strcat(fullfile_buf, "`1"); strcat(fullfile_buf, buf2); strcat(fullfile_buf, "`1"); while(!feof(fp)){ fscanf (fp, "%[^\n]\n", working_buf); logf("from lastcmd.txt: '%s'", working_buf); if(str_len(fullfile_buf) + str_len(working_buf)>MSL){ strcat(fullfile_buf, "...`x"); break; // buffer full } strcat(fullfile_buf, working_buf); strcat(fullfile_buf, "`1`x"); } fclose(fp); unlink(LASTCMD_FILE); strcat(buf,"\r\n"); strcat(buf,buf2); // do autonotes autonote(NOTE_SNOTE, "examine_last_command()", "Last recorded command before crash", "imm", buf, true); autonote(NOTE_SNOTE, "examine_last_command()", "recorded details before crash", "admin", fullfile_buf, true); if(GAMESETTING5(GAMESET5_DEDICATED_PKILL_STYLE_MUD)){ // do an autonote autonote(NOTE_NOTE, "examine_last_command()", "Last recorded command before crash", "all", buf, true); } } /**************************************************************************/ void syncronise_to_pulse() { static bool initial_values_required_setup=true; static struct timeval last_time; if(initial_values_required_setup){ gettimeofday( &last_time, NULL ); current_time = (time_t) last_time.tv_sec; initial_values_required_setup=false; } // Synchronize to a clock. // Sleep( last_time + 1/PULSE_PER_SECOND - now ). { struct timeval now_time; long secDelta; long usecDelta; gettimeofday( &now_time, NULL ); usecDelta = ((int) last_time.tv_usec) - ((int) now_time.tv_usec) + 1000000 / PULSE_PER_SECOND; secDelta = ((int) last_time.tv_sec ) - ((int) now_time.tv_sec ); while ( usecDelta < 0 ) { usecDelta += 1000000; secDelta -= 1; } while ( usecDelta >= 1000000 ) { usecDelta -= 1000000; secDelta += 1; } if( secDelta > 0 || ( secDelta == 0 && usecDelta > 0 ) ) { struct timeval stall_time; stall_time.tv_usec = usecDelta; stall_time.tv_sec = secDelta; #ifdef WIN32 Sleep( (stall_time.tv_sec * 1000) | (stall_time.tv_usec/1000) ); #else if( select( 0, NULL, NULL, NULL, &stall_time ) < 0 ){ socket_error( "Game_loop: select: stall" ); exit_error( 1 , "syncronise_to_pulse", "select stall"); } #endif } } gettimeofday( &last_time, NULL ); current_time = (time_t) last_time.tv_sec; } /**************************************************************************/ void game_loop() { hotreboot_in_progress = false; struct timeval last_time; #ifdef unix signal( SIGPIPE, SIG_IGN ); #endif gettimeofday( &last_time, NULL ); current_time = (time_t) last_time.tv_sec; // Main game loop while ( runlevel==RUNLEVEL_MAIN_IO_LOOP) { // sync to the clock so we get PULSE_PER_SECOND happening // if we didn't have this, the mud would do as many pulses // as the hosting server could perform. syncronise_to_pulse(); // check if the dns resolver has anything for us resolver_poll_and_process(); // if we are doing a hotreboot, poll the hotreboot_ipc_pipe if(hotreboot_in_progress){ hotreboot_poll_and_process(); } // check for new connections on any of the ports we are listening // if any are found to be waiting, accept them netio_check_for_and_accept_new_connections(); // accept all the new connections /* accept_new(control); if(irc_control>-1){ accept_new(irc_control); } if(ftp_control>-1){ accept_new(ftp_control); } */ // poll all the connections for input, output and exceptions // the descriptor sets are stored within netio.cpp netio_poll_connections(); // process any exceptions for the polled connections netio_process_exceptions_from_polled_connections(); // process input from the polled connections netio_process_input_from_polled_connections(); #ifdef IMC imc_loop(); #endif // Autonomous game motion. update_handler( ); netio_process_output_from_polled_connections(); update_alarm(); resolverlocal_execute_queued_commands(); } return; } /**************************************************************************/ struct local_tcp_pair_list { char *local_tcp_pair; int count; local_tcp_pair_list *next; }; /**************************************************************************/ void max_count_ip_calc() { local_tcp_pair_list* iplist, *node; // find the most common IP used by connections iplist=NULL; // start with an empty list of ip addresses // start by looping thru and adding them all up for(connection_data *c=connection_list; c; c=c->next){ // see if we already have the ip address recorded for(node=iplist; node; node=node->next){ if(!strcmp(node->local_tcp_pair, c->local_tcp_pair)){ break; } } if(node){ node->count++; }else{ node=new local_tcp_pair_list; node->count=1; node->local_tcp_pair=str_dup(c->local_tcp_pair); node->next=iplist; iplist=node; } } // next find the highest char *most_common_local_tcp_pair=str_dup(""); max_count_ip=0; for(node=iplist; node; node=iplist){ if(node->count>max_count_ip){ max_count_ip=node->count; replace_string(most_common_local_tcp_pair,node->local_tcp_pair); } iplist=node->next; free_string(node->local_tcp_pair); delete node; // deallocate the nodes - don't need them any more } strcpy(max_count_ip_buf, most_common_local_tcp_pair); free_string(most_common_local_tcp_pair); } /**************************************************************************/ void greet_new_connection(connection_data *c) { // Send the greeting. help_data *pHelp; if(IS_IRCCON(c)){ wiznet(FORMATF("`B***IRC connection starting***(socket = %d)`x", c->connected_socket), NULL,NULL,WIZ_SITES,0,0); pHelp=help_get_by_keyword("irc-greeting", NULL, true); if(!pHelp){ pHelp=help_get_by_keyword("greeting", NULL, true); if(!pHelp){ write_to_buffer( c, FORMATF("Welcome to %s\r\n\r\n", MUD_NAME), 0); }else{ write_to_buffer(c, pHelp->text, 0 ); } }else{ write_to_buffer(c, pHelp->text, 0 ); } write_to_buffer(c, "\r\n", 0 ); }else{ pHelp=help_get_by_keyword("greeting", NULL, true); if(!pHelp){ write_to_buffer( c, FORMATF("Welcome to %s\r\n\r\n", MUD_NAME), 0); }else{ write_to_buffer(c, pHelp->text, 0 ); } } if(HAS_MXPDESC(c)){ write_to_buffer(c, FORMATF("%s%s", LOGIN_PROMPT, mxp_tagify("<USER>")), 0); SET_BIT(c->flags, CONNECTFLAG_USER_TAG_SENT); }else{ write_to_buffer(c, LOGIN_PROMPT, 0); } if(IS_IRCCON(c)){ write_to_buffer(c, "\r\n", 0 ); } } /**************************************************************************/ void connection_close( connection_data *cclose ) { char_data *ch; connection_data *c; if( cclose->outtop > 0 ){ process_output( cclose, false ); } if( cclose->snoop_by != NULL ){ write_to_buffer( cclose->snoop_by, "Your snoop victim has left the game.\r\n", 0 ); } { // cancel snoops for ( c = connection_list; c; c = c->next ) { if( c->snoop_by == cclose ){ c->snoop_by = NULL; } } } if( cclose->command_snoop != NULL ) { write_to_buffer( cclose->command_snoop, "Your command snoop victim has left the game.\r\n", 0 ); } { // cancel command snoops for ( c = connection_list; c; c = c->next ) { if( c->command_snoop == cclose ){ c->command_snoop = NULL; } } } if( ( ch = cclose->character ) != NULL ) { logf("Closing link to %s. (socket=%d)", ch->name, ch->desc->connected_socket); // record last login site if(ch->pcdata){ free_string(ch->pcdata->last_logout_site); if(IS_NULLSTR(ch->desc->remote_hostname)){ ch->pcdata->last_logout_site = str_dup("???"); }else{ ch->pcdata->last_logout_site = str_dup(ch->desc->remote_hostname); } } // avoid situation where a non connected players // duplicate their pets if(cclose->connected_state!=CON_PLAYING && ch->pet){ ch->pet->in_room=NULL; } // gets rid of pets in NULL rooms from players that get their password wrong if( ch->pet && ch->pet->in_room == NULL ){ char_to_room( ch->pet, get_room_index(ROOM_VNUM_LIMBO) ); extract_char( ch->pet, true ); } if( cclose->connected_state == CON_PLAYING ) { act( "$n has lost $s link.", ch, NULL, NULL, TO_ROOM ); wiznet("Net death has claimed $N.",ch,NULL,WIZ_LINKS,0,0); ch->desc = NULL; } else { free_char( cclose->original ? cclose->original : cclose->character ); } } free_speedwalk( cclose ); // remove them from the linked list if( c_next == cclose ){ c_next = c_next->next; } if( cclose == connection_list ){ connection_list = connection_list->next; }else{ for ( c = connection_list; c && c->next != cclose; c = c->next ) ; if( c ){ c->next = cclose->next; }else{ bug("connection_close: cclose not found."); } } #ifdef MCCP_ENABLED // end MCCP if it is being used if(cclose->out_compress){ cclose->end_compression(); } #endif // close the actual socket connection cclose->close_socket(); // recycle the connection connection_deallocate(cclose); return; } /**************************************************************************/ // return true if everything is okay - i.e. we read something or are // waiting for something. bool read_from_connection( connection_data *c ) { unsigned int iStart; // Hold horses if pending command already. if( c->incomm[0] != '\0' ){ return true; } // Check for overflow. iStart = str_len(c->inbuf); if( iStart >= sizeof(c->inbuf) - 10 ) { logf( "%s input overflow! (socket=%d)", c->remote_tcp_pair, c->connected_socket); c->write("\r\n*** PUT A LID ON IT!!! ***\r\n", 0 ); return false; } // Snarf input. for ( ; ; ) { int nRead; // There is no more space in the input buffer for now if(sizeof(c->inbuf) - 10 - iStart == 0){ break; } nRead = recv( c->connected_socket, c->inbuf + iStart, sizeof(c->inbuf) - 10 - iStart, 0 ); if( nRead > 0 ) { iStart += nRead; if( c->inbuf[iStart-1] == '\n' || c->inbuf[iStart-1] == '\r' ){ break; } } else { if(nRead==0){ logf( "EOF encountered on read from %s. (socket %d)", c->remote_tcp_pair, c->connected_socket); return false; } else { #ifdef WIN32 if(WSAGetLastError() == WSAEWOULDBLOCK){ break; }else{ socket_error( "Read_from_connection" ); return false; } #else if(errno==EWOULDBLOCK){ break; }else{ socket_error( "Read_from_connection" ); return false; } #endif } } } c->inbuf[iStart] = '\0'; // mark end of text c->inbuf[iStart+1] = '\0'; // marked one past, so the telnet suboption code can detect the end return true; } /**************************************************************************/ // Transfer one line from input buffer to input line. void read_from_buffer( connection_data *d ) { int i, j, k; bool got_n, got_r; // Hold horses if pending command already. if( d->incomm[0] != '\0' ){ return; } // handle speedwalking if( d->speedwalk_buf ) { char *s, *e; while ( is_digit( *d->speedwalk_head ) && *d->speedwalk_head != '\0' ) { s = d->speedwalk_head; while( is_digit( *s )) s++; e =s; while(*(--s) == '0' && s != d->speedwalk_head ); if( is_digit( *s ) && *s != '0' && *e != 'o' ) { d->incomm[0] = *e; d->incomm[1] = '\0'; s[0]--; while ( is_digit(*(++s))) *s = '9'; return; } if( *e == 'o' ){ d->speedwalk_head = e; }else{ d->speedwalk_head = ++e; } } if( *d->speedwalk_head != '\0' ) { if( *d->speedwalk_head != 'o' ) { d->incomm[0] = *d->speedwalk_head++; d->incomm[1] = '\0'; return; } else { char buf[MIL]; d->speedwalk_head++; sprintf( buf, "open " ); switch ( *d->speedwalk_head ) { case 'n' : sprintf( buf+str_len(buf), "north" ); break; case 'e' : sprintf( buf+str_len(buf), "east" ); break; case 's' : sprintf( buf+str_len(buf), "south" ); break; case 'w' : sprintf( buf+str_len(buf), "west" ); break; case 'r' : sprintf( buf+str_len(buf), "northwest" ); break; case 't' : sprintf( buf+str_len(buf), "northeast" ); break; case 'g' : sprintf( buf+str_len(buf), "southeast" ); break; case 'f' : sprintf( buf+str_len(buf), "southwest" ); break; case 'u' : sprintf( buf+str_len(buf), "up" ); break; case 'd' : sprintf( buf+str_len(buf), "down" ); break; default: return; } strcpy( d->incomm, buf ); d->speedwalk_head++; return; } } free_speedwalk( d ); } #ifdef DEBUG_PACKET_LOGGING_IN_READ_FROM_BUFFER logf("read_from_buffer point1:'%s' %d %d %d %d", d->inbuf, (unsigned char)*d->inbuf, (unsigned char)*(d->inbuf+1), (unsigned char)*(d->inbuf+2), (unsigned char)*(d->inbuf+3)); // debug code #endif // Look for at least one new line for ( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ ) { // check for telnet IAC options if((unsigned char)d->inbuf[i]==IAC){ d->process_telnet_options(i); } if( d->inbuf[i] == '\0' ){ return; } } #ifdef DEBUG_PACKET_LOGGING_IN_READ_FROM_BUFFER logf("read_from_buffer point2: %d %d %d %d", *d->inbuf, (unsigned char)*(d->inbuf+1), (unsigned char)*(d->inbuf+2), (unsigned char)*(d->inbuf+3)); // debug code #endif // the only time it will get this far is if there is a complete line (ending with \r or \n) // process any mxp options // NOTE: There is no limit on the length of the input at this stage if(d->inbuf[0]=='\033' && !memcmp(MXP_CLIENT_TO_SERVER_PREFIX, d->inbuf, str_len(MXP_CLIENT_TO_SERVER_PREFIX))){ d->process_client2server_mxp_message(i); return; } // Canonical input processing for ( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ ) { if( k >= MIL - 4 ) { d->write("Line too long.\r\n", 0 ); // skip the rest of the line for ( ; d->inbuf[i] != '\0'; i++ ) { if( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' ){ break; } } d->inbuf[i] = '\n'; d->inbuf[i+1] = '\0'; break; } // safety code to always prevent MXP tags being sent by the client if(d->inbuf[i]==MXP_BEGIN_TAG){ continue; } if( d->inbuf[i] == '\b' && k > 0 ){ --k; // support backspace }else if (!GAMESETTING5(GAMESET5_DISABLE_TILDE_CONVERSION) && d->inbuf[i] == '~') { // convert ~ into `- // this is done to prevent connections getting the ~ // character into files which can accidentially or // intentionally cause corruption/security issues. d->incomm[k++] = '`'; d->incomm[k++] = '-'; }else if(GAMESETTING3(GAMESET3_DISABLE_EXTENDED_ASCII_CHARACTERS)){ if (is_ascii(d->inbuf[i]) && is_print(d->inbuf[i])) { d->incomm[k++] = d->inbuf[i]; } }else{ unsigned char c=d->inbuf[i]; if(c>0x1F && c!=0x7F && c!=0xFF) // accept anything but control characters { d->incomm[k++] = d->inbuf[i]; } } } // Finish off the line. if( k == 0 ){ d->incomm[k++] = ' '; } d->incomm[k] = '\0'; // Deal with bozos with #repeat 1000 ... if( k > 1 || d->incomm[0] == '!' ) { if( d->incomm[0] != '!' && strcmp( d->incomm, d->inlast ) ) { d->repeat = 0; } else { if( ++d->repeat >= 25 ) { sprintf( log_buf, "%d (%s) input spamming!", d->connected_socket, d->remote_tcp_pair); log_string( log_buf ); if(d->connected_state==CON_PLAYING) { wiznet("Spam spam spam $N spam spam spam spam spam!", d->character,NULL,WIZ_SPAM,0,get_trust(d->character)); if(d->incomm[0] == '!'){ wiznet(d->inlast,d->character,NULL,WIZ_SPAM,0, get_trust(d->character)); }else{ wiznet(d->incomm,d->character,NULL,WIZ_SPAM,0, get_trust(d->character)); } } d->repeat = 0; } } } // Do '!' substitution. if( d->incomm[0] == '!' && (d->connected_state!=CON_DETECT_CLIENT_SETTINGS || d->connected_state!=CON_GET_NAME) ) { strcpy( d->incomm, d->inlast ); }else{ strcpy( d->inlast, d->incomm ); } //Shift the input buffer. // mudftp: Do not compress multiple lines into just one. got_n = got_r = false; for (;d->inbuf[i] == '\r' || d->inbuf[i] == '\n';i++) { if( (d->inbuf[i] == '\r' && got_r++) || (d->inbuf[i] == '\n' && got_n++)){ break; } } for ( j = 0; ( d->inbuf[j] = d->inbuf[i+j] ) != '\0'; j++ ){ }; return; } /**************************************************************************/ // This should only be called by write_into_buffer_thru_process_colour() // or the visual debugging system static void write_into_buffer( connection_data *c, const char *txt, int length ) { // Expand the buffer as needed while ( c->outtop + length >= c->outsize ) { char *outbuf; if(c->outsize >= 64000) { bug("write_into_buffer(): Buffer overflow. Closing."); c->write("Buffer overflow (too much data generated from what you just did most likely),\r\n" "Closing your connection.\r\n", 0); // mark it as if the buffer has nothing to be written to // avoid endless loop of // connection_close()->process_output()-> // flush_cached_write_to_buffer() -> // write_into_buffer_thru_process_colour() -> // write_into_buffer() // c->outtop=0; c->outsize=0; connection_close(c); return; } if(c->outsize<1){ return; } outbuf = (char *)alloc_mem( 2 * c->outsize ); strncpy( outbuf, c->outbuf, c->outtop ); free_mem( c->outbuf, c->outsize ); c->outbuf = outbuf; c->outsize *= 2; } // Copy. strncpy( c->outbuf + c->outtop, txt, length ); c->outtop += length; } /**************************************************************************/ static void write_into_buffer_thru_process_colour( connection_data *d, const char *txt, int length ); #define VISUAL_DEBUG_COLUMN_WIDTH (25) #define VISUAL_DEBUG_BUFFER_SIZE (4000) #define VDBS (VISUAL_DEBUG_BUFFER_SIZE ) /**************************************************************************/ static void visual_debug_write( connection_data *d, const char *txt, int length ) { // Visual debugger system if(d->visual_debug_buffer==NULL){ // allocate a buffer if it is the first time to be used d->visual_debug_buffer=(char*)malloc(VDBS+1); d->visual_debug_buffer[0]='\0'; } int space_free=VDBS - str_len(d->visual_debug_buffer); if(length< space_free){ strcat(d->visual_debug_buffer, txt); return; } // fill up visual_debug_buffer with space_free characters int copied=VDBS-str_len(d->visual_debug_buffer); strncat(d->visual_debug_buffer, txt, copied); d->visual_debug_buffer[VDBS]='\0'; { char hex_block[VISUAL_DEBUG_COLUMN_WIDTH*5]; char visual_debug_format_buffer[VDBS*5]; char hex[10]; int character; const char *p=d->visual_debug_buffer; // set the colour to white and MXP to locked mode d->visual_debugging_enabled=false; write_into_buffer_thru_process_colour( d, FORMATF("`x\r\n%s=======VISUAL DEBUG====== Sent to your Connection:\r\n", MXP_LOCKED_MODE), 0); d->visual_debugging_enabled=true; // setup our visual debugging format buffer visual_debug_format_buffer[0]='\0'; char *w=visual_debug_format_buffer; character=0; hex_block[0]='\0'; for(;*p; p++){ if(is_print(*p)){ *w++=*p; }else{ *w++='.'; } if(d->visual_debug_hexoutput){ sprintf(hex," %02x", ((unsigned char)*p)); }else{ sprintf(hex," %03d", ((unsigned char)*p)); } strcat(hex_block, hex); character++; if(character>d->visual_debug_column_width-1){ character=0; *w='\0'; strcat(w, " "); strcat(w, hex_block); strcat(w, "\r\n"); hex_block[0]='\0'; w+=str_len(w); } } *w='\0'; strcat(w, " "); strcat(w, hex_block); if(d->mxp_enabled){ strcat(w,MXP_SECURE_MODE); } strcat(w, "\r\n"); write_into_buffer( d, visual_debug_format_buffer, str_len(visual_debug_format_buffer)); d->visual_debug_buffer[0]='\0'; } // recurse to handle the next block visual_debug_write( d, txt+copied, str_len(txt+copied)); } /**************************************************************************/ void visual_debug_flush( connection_data *d) { if(IS_NULLSTR(d->visual_debug_buffer)){ return; } char hex_block[VISUAL_DEBUG_COLUMN_WIDTH*5]; char visual_debug_format_buffer[VDBS*5]; char hex[10]; int character; const char *p=d->visual_debug_buffer; // set the colour to white and MXP to locked mode d->visual_debugging_enabled=false; write_into_buffer_thru_process_colour( d, FORMATF("`x\r\n%s=======VISUAL DEBUG====== Sent to your Connection:\r\n", HAS_MXPDESC(d)?MXP_LOCKED_MODE:""), 0); d->visual_debugging_enabled=true; // setup our visual debugging format buffer visual_debug_format_buffer[0]='\0'; char *w=visual_debug_format_buffer; character=0; hex_block[0]='\0'; for(;*p; p++){ if(is_print(*p)){ *w++=*p; }else{ *w++='.'; } if(d->visual_debug_hexoutput){ sprintf(hex," %02x", ((unsigned char)*p)); }else{ sprintf(hex," %03d", ((unsigned char)*p)); } strcat(hex_block, hex); character++; if(character>d->visual_debug_column_width-1){ character=0; *w='\0'; strcat(w, " "); strcat(w, hex_block); strcat(w, "\r\n"); hex_block[0]='\0'; w+=str_len(w); } } *w='\0'; // insert extra spaces if necessary to make the final characters line up if(character>0 && character<=d->visual_debug_column_width-1){ strcat(w, FORMATF("%*c", (d->visual_debug_column_width-1) - character,' ')); } strcat(w, " "); strcat(w, hex_block); strcat(w, HAS_MXPDESC(d)?MXP_SECURE_MODE:""); strcat(w, "\r\n"); write_into_buffer( d, visual_debug_format_buffer, str_len(visual_debug_format_buffer)); d->visual_debug_buffer[0]='\0'; } /**************************************************************************/ void do_visualdebug(char_data *ch, char *argument) { // check we have a descriptor connection_data *c=ch->desc; if(!c){ ch->println("Sorry, you can't use the visualdebug command as you don't have a connection"); return; } char arg[MIL], arg2[MIL]; argument=one_argument(argument, arg); if(IS_NULLSTR(arg)){ ch->titlebar("THE VISUAL DEBUGGER"); ch->wrapln("The visual debugger is used to see the raw information being sent " "to your mud/telnet client, it really only has a use for debugging the mud code " "and for programmers working on a mud client. You can't do any damage " "by turning on or playing with the debugger, just things may look a little " "weird."); ch->println("syntax: visualdebug on"); ch->println("syntax: visualdebug off"); ch->println("syntax: visualdebug hexoutput on"); ch->println("syntax: visualdebug hexoutput off"); ch->println("syntax: visualdebug flush_before_prompt on"); ch->println("syntax: visualdebug flush_before_prompt off"); ch->println("syntax: visualdebug strip_prompt on"); ch->println("syntax: visualdebug strip_prompt off"); ch->println("syntax: visualdebug column_width #"); ch->println(" Where # is 10->30"); ch->println("syntax: visualdebug next_connection_autoon"); ch->wrapln("next_connection_autoon turns on debugging for the next connection from " "your ip address."); ch->titlebar("YOUR CURRENT VISUAL DEBUG SETTINGS"); ch->printlnf("Visual debugging is currently %s for your connection.", c->visual_debugging_enabled?"on":"off"); ch->printlnf("Visual debugging column width is set to %d.", c->visual_debug_column_width); ch->printlnf("The visual debug display numeric values in %s format.", c->visual_debug_hexoutput?"hexidecimal":"decimal"); ch->printlnf("The visual debug is%s flushed before displaying a prompt.", c->visual_debug_flush_before_prompt?"":" not"); ch->printlnf("The prompt is%s being stripped out of the visual debug.", c->visual_debug_strip_prompt?"":" not"); ch->titlebar(""); return; } if(!str_cmp(arg, "on")){ if(c->visual_debugging_enabled){ ch->println("You already have visual debugging enabled!"); return; } c->visual_debugging_enabled=true; ch->println("Visual debugging turned on."); return; } if(!str_cmp(arg, "off")){ if(!c->visual_debugging_enabled){ ch->println("You already have visual debugging disabled!"); return; } c->visual_debugging_enabled=false; ch->println("Visual debugging turned off."); return; } // all the other options require an additional argument argument=one_argument(argument, arg2); // Column width if(!str_prefix(arg, "column_width")){ int value=URANGE(10,atoi(arg2),30); c->visual_debug_column_width=value; ch->printlnf("Visual debug column width set to %d", value); return; } // Hex output if(!str_prefix(arg, "hexoutput")){ if(!str_cmp(arg2, "on")){ if(c->visual_debug_hexoutput){ ch->println("You already have hexoutput enabled!"); return; } c->visual_debug_hexoutput=true; ch->println("hexoutput turned on."); return; } if(!str_cmp(arg2, "off")){ if(!c->visual_debug_hexoutput){ ch->println("You already have hexoutput off!"); return; } c->visual_debug_hexoutput=false; ch->println("hexoutput turned off."); return; } } // Strip Prompt if(!str_prefix(arg, "strip_prompt")){ if(!str_cmp(arg2, "on")){ if(c->visual_debug_strip_prompt){ ch->println("You already have strip_prompt enabled!"); return; } c->visual_debug_strip_prompt=true; ch->println("strip_prompt turned on."); ch->println("Note: This has no effect while flush_before_prompt is off"); return; } if(!str_cmp(arg2, "off")){ if(!c->visual_debug_strip_prompt){ ch->println("You already have strip_prompt off!"); return; } c->visual_debug_strip_prompt=false; ch->println("strip_prompt turned off."); return; } } // Flush before prompt if(!str_prefix(arg, "flush_before_prompt")){ if(!str_cmp(arg2, "on")){ if(c->visual_debug_flush_before_prompt){ ch->println("You already have flush_before_prompt enabled!"); return; } c->visual_debug_flush_before_prompt=true; ch->println("flush_before_prompt turned on."); return; } if(!str_cmp(arg2, "off")){ if(!c->visual_debug_flush_before_prompt){ ch->println("You already have flush_before_prompt off!"); return; } c->visual_debug_flush_before_prompt=false; ch->println("flush_before_prompt turned off."); return; } } // next connection autoon if(!str_prefix(arg, "next_connection_autoon")){ if(visual_debug_next_connection_autoon_ip==NULL){ visual_debug_next_connection_autoon_ip=str_dup(""); } replace_string(visual_debug_next_connection_autoon_ip, c->remote_ip); visual_debug_next_connection_column_width=c->visual_debug_column_width; visual_debug_next_connection_hexoutput=c->visual_debug_hexoutput; ch->wrapln("Next time you connect, visual debugging will be automatically " "turned on (with a column with of your current column width)... " "This enables visual debugging of the bootup sequence."); return; } ch->printlnf("VisualDebug: Unrecognised option '%s %s'.", arg, arg2); do_visualdebug(ch, ""); } /**************************************************************************/ // should only be called from flush_cache, process_output for snoop, and write_to_buffer static void write_into_buffer_thru_process_colour( connection_data *c, const char *txt, int length ) { if(!c){ bug("write_into_buffer_thru_process_colour(): Being called with NULL connection!"); return; } // make sure we are working with a valid connection if(!IS_VALID(c)){ //IS_VALID ensures c!=NULL bugf("write_into_buffer_thru_process_colour(): Being called with invalid connection %d (%s).", c->connected_socket, CH(c)?CH(c)->name:"no name"); return; } // Find length incase caller didn't. if( length <= 0 ){ length = str_len(txt); } if(length==0){ return; } // Initial \r\n if needed. if( c->outtop == 0 && !c->fcommand ) { c->outbuf[0] = '\r'; c->outbuf[1] = '\n'; c->outtop = 2; } // colour code parsing - will trim binary buffers with embedded nul's if(c->parse_colour){ txt=process_colour(txt, c); length = str_len(txt); } write_into_buffer( c, txt, length ); if(c->visual_debugging_enabled){ visual_debug_write( c, txt, length ); } return; } /**************************************************************************/ // Low level output function. bool process_output( connection_data *c, bool fPrompt ) { // make sure we are working with a valid descriptor if(!IS_VALID(c)){ //IS_VALID ensures d!=NULL bugf("process_output(): Being called with invalid connection (socket %d) (character=%s).", c->connected_socket, CH(c)?CH(c)->name:"no name"); return true; // return true so the mud doesn' try to close the socket } if(runlevel!=RUNLEVEL_SHUTING_DOWN){ flush_cached_write_to_buffer(c); // flush cache first // Bust a prompt. if( c->showstr_point ){ if(c->mxp_enabled){ write_to_buffer( c, FORMATF("`#`=\xaa%s`^\r\n", mxp_tagify("<send href=\"continue\">[Hit Return to continue]</send>")), 0 ); }else{ write_to_buffer( c, "`#`=\xaa""[Hit Return to continue]`^\r\n", 0 ); } }else{ if( fPrompt && c->pString && c->connected_state == CON_PLAYING ){ if(HAS_MXP(CH(c))){ c->character->printf( "`x%s>", mxp_create_send(CH(c), "@") ); }else{ c->character->print( "`x> "); // string editor } }else{ if( fPrompt && c->connected_state == CON_PLAYING ){ char_data *ch; char_data *victim; ch = c->character; // battle prompt if((victim = ch->fighting)!= NULL && can_see(ch,victim)) { int percent; char wound[100]; char buf[MSL]; if(victim->max_hit > 0){ percent = victim->hit * 100 / victim->max_hit; }else{ percent = -1; } if(percent >= 100){ sprintf(wound,"is in excellent condition."); }else if(percent >= 90){ sprintf(wound,"has a few scratches."); }else if(percent >= 75){ sprintf(wound,"has some small wounds and bruises."); }else if(percent >= 50){ sprintf(wound,"has quite a few wounds."); }else if(percent >= 30){ sprintf(wound,"has some big nasty wounds and scratches."); }else if(percent >= 15){ sprintf(wound,"looks pretty hurt."); }else if(percent >= 0){ sprintf(wound,"is in awful condition."); }else{ sprintf(wound,"is bleeding to death."); } sprintf(buf,"%s %s \r\n", PERS(victim, ch), wound); buf[0] = UPPER( buf[0] ); write_to_buffer( c, buf, 0); } ch = c->original ? c->original : c->character; if(!IS_SET(ch->comm, COMM_COMPACT)){ write_to_buffer( c, "\r\n", 2 ); } // send the mxp reset command just before where the prompt is displayed // assuming the player is using their prompt. if(HAS_MXP(ch)){ ch->print(MXP_RESET); } // send the group prompt to the player/connection if they want it. if( !IS_SET(ch->comm, COMM_NOGPROMPT)){ bust_a_group_prompt(ch); } // battle lag prompt if( ch->wait>=PULSE_VIOLENCE-2 && ch->wait>0 && ch->fighting && !HAS_CONFIG2(ch, CONFIG2_NO_BATTLELAG_PROMPT)) { if(TRUE_CH_PCDATA(ch) && !IS_NULLSTR(TRUE_CH_PCDATA(ch)->battlelag)){ ch->print(TRUE_CH_PCDATA(ch)->battlelag); }else{ ch->print(game_settings->mud_default_battlelag_text); } } // send the prompt to the player/connection if they want it. if( IS_SET(ch->comm, COMM_PROMPT)){ bust_a_prompt( c ); } } } } // Short-circuit if nothing to write. flush_cached_write_to_buffer(c); // flush cache first if( c->outtop == 0){ return true; } // Snoop-o-rama. if( c->snoop_by != NULL ){ flush_cached_write_to_buffer(c->snoop_by); bool parse_normally=c->snoop_by->parse_colour; write_into_buffer_thru_process_colour( c->snoop_by, "`_", 0 ); // underline on (\033[4m) flush_cached_write_to_buffer(c->snoop_by); c->snoop_by->parse_colour=false; if(c->character){ write_into_buffer_thru_process_colour( c->snoop_by, c->character->name,0); } write_into_buffer_thru_process_colour( c->snoop_by, " >\r\n", 3 ); // if we have an MXP snooper, snooping a non MXP user, // we need to lock the mxp mode for the buffer if(!HAS_MXPDESC(c) && HAS_MXPDESC(c->snoop_by)){ write_into_buffer_thru_process_colour(c->snoop_by, MXP_LOCKED_MODE, 4); } // send the output text write_into_buffer_thru_process_colour( c->snoop_by, c->outbuf, c->outtop ); // put MXP back into secure mode if appropriate if(!HAS_MXPDESC(c) && HAS_MXPDESC(c->snoop_by)){ write_into_buffer_thru_process_colour( c->snoop_by, "\r\n", 2 ); write_into_buffer_thru_process_colour(c->snoop_by, MXP_SECURE_MODE, 4); } flush_cached_write_to_buffer(c->snoop_by); c->snoop_by->parse_colour=parse_normally; write_into_buffer_thru_process_colour( c->snoop_by, "\033[0m", 0 ); // underline off flush_cached_write_to_buffer(c->snoop_by); } }// runlevel!=RUNLEVEL_SHUTING_DOWN // OS-dependent output. return c->send_outbuf(); } /**************************************************************************/ // flush a characters output bool flush_char_outbuffer(char_data *ch) { return ch?ch->desc->flush_output():false; } /**************************************************************************/ // Group prompt system - Kal, Dec 2001 // group prompts only work on pets and other players - not charmies // codes: // g - begin group section // G - end group section // h - lowest hitpoints % for group members in the room // m - lowest mana % for group members in the room // v - lowest move % for group members in the room // p - begin pet section // P - end pet section // q - pet hitpoints % // r - pet mana % // s - pet move % // N - number of group members in the current room // c - carriage return // C - carriage return only if there is preceeding text // x - number of charmies in the current room (excluding pet) void bust_a_group_prompt( char_data *ch) { int group_count=0; int charmies_count=0; bool pet_found=false; bool complete; char_data *gch; char_data *gvictim; char *group_prompt; char *src; char *dest; char *i; char result[MSL*3]; char buf[MSL]; // no group prompt for those with afk turned on if(IS_SET(TRUE_CH(ch)->comm,COMM_AFK)){ return; } // first check if we are going to be displaying this prompt for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && is_same_group( gch, ch )){ if(ch->pet==gch){ pet_found=true; }else if(!IS_NPC(gch)){ group_count++; }else{ charmies_count++; } } } if(!pet_found && !group_count){ return; } // by here, we know we have a pet and/or group members in the room // get the players custom group prompt into group_prompt group_prompt=ch->gprompt; if(IS_NULLSTR(group_prompt)){ ch->gprompt=str_dup("`#%g[`xgrp `R%hhp `B%mm `M%vmv`&]%G%p[`spet `r%qhp `b%rm `m%smv`&>%P%C"); group_prompt=ch->gprompt; } dest=result; int lowest_percent; for(src=group_prompt; !IS_NULLSTR(src); ){ if( *src!= '%' ){ *dest++ = *src++; continue; } // we have a % code, skip the % symbol src++; if(*src=='\0'){ // if a nul follows a %, tell them to fix their group prompt ch->println("Your group prompt can't end with a single %"); return; } // process the % character lowest_percent=101; i=""; switch( *src ) { default : // unrecognised option, just use a space *dest++=' '; continue; src++; break; case '%': // % character itself *dest++='%'; src++; continue; break; case 'c': // carriage return *dest++='\r'; *dest++='\n'; src++; continue; break; case 'C': // carriage return if there is preceeding text in the prompt { *dest='\0'; if(str_len(result)>0){ *dest++='\r'; *dest++='\n'; } src++; } continue; break; case 'g': // start of the group section if(!group_count){ // we don't have a group section, therefore // fast forward to the closing %G complete=false; for(src++; !complete; src++){ if(*src=='\0'){ // if we find we reached the end of the string, give instructions // about needing a %G after a %g in a prompt ch->println("Your group prompt needs a %G after the %g before it will be displayed."); return; } if(*src=='%'){ // found a %, could be start of %G src++; if(*src=='G'){ complete=true; continue; } if(*src=='\0'){ // if we find we reached the end of the string, give instructions // about needing a %G after a %g in a prompt ch->println("Your group prompt needs a %G after the %g before it will be displayed."); return; } // otherwise ignore it } } continue; } break; case 'G':{ // end of the group section - silently ignore it } break; case 'p': // start of the group section if(!pet_found){ // we don't have a group section, therefore // fast forward to the closing %G complete=false; for(src++; !complete; src++){ if(*src=='\0'){ // if we find we reached the end of the string, give instructions // about needing a %P after a %p in a prompt ch->println("Your group prompt needs a %P after the %p before it will be displayed."); return; } if(*src=='%'){ // found a %, could be start of %P src++; if(*src=='P'){ complete=true; continue; } if(*src=='\0'){ // if we find we reached the end of the string, give instructions // about needing a %P after a %p in a prompt ch->println("Your group prompt needs a %P after the %p before it will be displayed."); return; } // otherwise ignore it } } continue; } break; case 'P':{ // end of the pet section - silently ignore it } break; case 'h':{ // - lowest hitpoints % for group members in the room if(group_count){ // find the group member in the room with the lowest HP for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && !IS_NPC(gch) && ch->pet!=gch && is_same_group( gch, ch ) && gch->max_hit!=0){ if((gch->hit*100/gch->max_hit)<lowest_percent){ gvictim=gch; lowest_percent=(gch->hit*100/gch->max_hit); } } } if(lowest_percent<101){ sprintf( buf, "%3d%%", lowest_percent); i = buf; } } } break; case 'm':{ // - lowest mana % for group members in the room if(group_count){ // find the group member in the room with the lowest MANA for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && !IS_NPC(gch) && ch->pet!=gch && is_same_group( gch, ch ) && gch->max_mana!=0){ if((gch->mana*100/gch->max_mana)<lowest_percent){ gvictim=gch; lowest_percent=(gch->mana*100/gch->max_mana); } } } if(lowest_percent<101){ sprintf( buf, "%3d%%", lowest_percent); i = buf; } } } break; case 'v':{ // - lowest movement % for group members in the room if(group_count){ // find the group member in the room with the lowest movement % for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && !IS_NPC(gch) && ch->pet!=gch && is_same_group( gch, ch ) && gch->max_move!=0){ if((gch->move*100/gch->max_move)<lowest_percent){ gvictim=gch; lowest_percent=(gch->move*100/gch->max_move); } } } if(lowest_percent<101){ sprintf( buf, "%3d%%", lowest_percent); i = buf; } } } break; case 'q':{ // - hitpoints % for pet if(pet_found && ch->pet->max_hit){ sprintf( buf, "%3d%%", ch->pet->hit*100/ch->pet->max_hit); i = buf; } } break; case 'r':{ // - mana % for pet if(pet_found && ch->pet->max_mana){ sprintf( buf, "%3d%%", ch->pet->mana*100/ch->pet->max_mana); i = buf; } } break; case 's':{ // - move % for pet if(pet_found && ch->pet->max_move){ sprintf( buf, "%3d%%", ch->pet->move*100/ch->pet->max_move); i = buf; } } break; case 'N':{ // - number of group members in room sprintf( buf, "%d", group_count); i = buf; } break; case 'x':{ // - number of charmies in room sprintf( buf, "%d", charmies_count); i = buf; } break; } src++; while( (*dest= *i) != '\0' ){ dest++; i++; } } *dest='\0'; // send the prompt to the player if(ch->fighting){ ch->print("`=q"); }else{ ch->print("`=P"); } ch->print(result); ch->print("`x"); if(IS_IRC(ch)){ ch->println(""); } } /**************************************************************************/ /* * Bust a prompt (player settable prompt) * coded by Morgenes for Aldara Mud * - Modified by Kalahn many times * * Allows player to customize their prompt valid settings are * a - alignment * b - position - p was already in use :( * c - carriage return * d - misc data about your character status * e - exits for the room * E - exits for the room as MXP links if(mxp is enabled) * g - gold * h - hit points * H - maximum hitpoints * l - language you are speaking * m - mana (disabled with switched) * M - maximum mana * o - olc edit name * O - olc edit vnum * p - role playing score (total) * P - the hp% of your pet if you have one * r - room description * R - immortal only - Room Vnum * s - silver * S - immortal only - short description of switched mob * t - game time * T - server time * v - movement points * V - max movement points * x - experience total (disabled when switched) * X - experience required to level (disabled when switched) * z - immortal only - zone you are in * * -== Immortal only codes ==- * R - room vnum * S - short description of switched mob * Z - filename of zone you are in * z - zone you are in * ! - CRASHES MUD WHEN IMM SWITCHES * (Use only for testing automatic debugging scripts) */ void visual_debug_flush( connection_data *d); void bust_a_prompt( connection_data *d ) { if(d && d->visual_debugging_enabled && d->visual_debug_flush_before_prompt){ visual_debug_flush( d ); } char_data *ch = d->character; char buf[MSL*3]; char buf2[MSL]; char blank_string[] = ""; const char *str; const char *i; char *point; char doors[MIL]; char hdoors[MIL]; // hidden doors EXIT_DATA *pexit= NULL; bool found, hfound=false; char *position_name[] = { "Dead", "Mort", "Incap", "Stunned", "Asleep", "Resting", "Sitting", "Kneeling", "Fighting", "Standing" }; int door; char *prompt; char_data *gch=NULL; char_data *gvictim=NULL; bool group_shown=false; // group stat shown if(IS_SWITCHED(ch)) { if(IS_IMMORTAL(ch)){ if(!IS_SET(TRUE_CH(ch)->dyn, DYN_NO_PROMPT_SWITCHED_PREFIX)){ ch->printlnf("`=\x8c""***** %s - %s, %s, saycol=`%c%c`=\x8c"", motecol=`%c%c `=\x8c""*****", ch->short_descr, ch->language->name, HAS_HOLYSPEECH(ch)?"`#`=\x8d""holyspeech on`&":"holyspeech off", ch->saycolour, ch->saycolour, ch->motecolour, ch->motecolour); } }else{ ch->println("`S** Spirit Walking **"); } } if(IS_SET(ch->comm,COMM_TELNET_GA)) { unsigned char b[3]; b[0]=255; b[1]=130; b[2]=0; write_to_buffer(ch->desc,(char*)&b[0],2); } if(ch->desc->editor){ // working in olc ch->print("`=p"); prompt = ch->olcprompt; if(IS_NULLSTR(prompt)){ // use the default olc prompt prompt ="[`#`m%e`^ in `R%o`mv`R%O`g%Z`^ - %T`^ - %t]"; } }else if(ch->fighting){ ch->print("`=q"); prompt = ch->prompt; }else{ ch->print("`=P"); prompt = ch->prompt; } if(IS_NULLSTR(prompt)){ prompt = game_settings->default_prompt; } point = buf; str = prompt; if(IS_SET(TRUE_CH(ch)->comm,COMM_AFK)) { ch->print("<AFK> "); return; } while( *str != '\0' ) { if( *str != '%' ) { *point++ = *str++; continue; } ++str; switch( *str ) { default : i = " "; break; case '%' : sprintf( buf2, "%%" ); i = buf2; break; case 'a' : if( ch->level > 9 ) sprintf( buf2, "%+d.%+d", ch->tendency, ch->alliance ); else sprintf( buf2, "%s", IS_GOOD(ch) ? "good" : IS_EVIL(ch) ? "evil" : "neutral" ); i = buf2; break; case 'b' : sprintf( buf2, "%s%s", position_name[ch->position], IS_AFFECTED(ch, AFF_FLYING)?"(airborne)":""); i = buf2; break; case 'c' : sprintf(buf2,"%s","\r\n"); i = buf2; break; case 'd': // misc data about your character status - Balo & Kal May 2002 { buf2[0]='\0'; if(is_affected(ch,gsn_sneak)){ strcat( buf2, " `#(`ms`&)"); } if( is_affected( ch, gsn_invisibility)){ strcat( buf2, " `#(`Bi`&)"); } if ( IS_AFFECTED(ch, AFF_HIDE)){ strcat( buf2, " `#(`Gh`&)"); } if( INVIS_LEVEL(ch)){ strcat( buf2, FORMATF(" `#(`Yv%d`&)", INVIS_LEVEL(ch))); } if( ch->incog_level){ strcat( buf2, FORMATF(" `#(`CI%d`&)", ch->incog_level)); } if(HAS_CHANNELOFF(ch, CHANNEL_QUIET)){ strcat( buf2, " `#`W[`MQUIET`W]`&"); } i = buf2; } break; case 'e': case 'E': found = false; doors[0] = '\0'; for (door = 0; door<MAX_DIR; door++) { if(ch->in_room && (pexit = ch->in_room->exit[door]) != NULL && pexit ->u1.to_room != NULL && (can_see_room(ch,pexit->u1.to_room) || (IS_AFFECTED(ch,AFF_INFRARED) && !IS_AFFECTED(ch,AFF_BLIND))) && !IS_SET(pexit->exit_info,EX_CLOSED)) { found = true; if(*str=='E'){ strcat(doors, mxp_create_tag(ch, "Ex", dir_shortname[door]) ); }else{ strcat(doors,dir_shortname[door]); } } } // hidden exits for those with holylight if(IS_SET(ch->act, PLR_HOLYLIGHT)) { hfound = false; hdoors[0] = '('; hdoors[1] = '\0'; for (door = 0; door < MAX_DIR; door++) { if((pexit = ch->in_room->exit[door]) != NULL && pexit ->u1.to_room != NULL && (can_see_room(ch,pexit->u1.to_room) || (IS_AFFECTED(ch,AFF_INFRARED) && !IS_AFFECTED(ch,AFF_BLIND))) && IS_SET(pexit->exit_info,EX_CLOSED)) { hfound = true; if(*str=='E'){ strcat(hdoors, mxp_create_tag(ch, "Ex", dir_shortname[door]) ); }else{ strcat(hdoors,dir_shortname[door]); } } } strcat(hdoors,")"); if(hfound) strcat(doors,hdoors); } if(!found && !hfound){ strcat(doors,"none"); } strcpy(buf2,doors); i = buf2; break; case 'g' : sprintf( buf2, "%ld", ch->gold); i = buf2; break; case 'G' : // group templates following character has template 0->9, A->Z etc if(*(str+1)!='\0'){ // can't have %G by itself ++str; if(!ch->in_room || !ch->in_room->people){ i="BUG, !ch->in_room || !ch->in_room->people!!!"; break; } int lowest_percent=101; gvictim=ch; switch(*str){ // lowest movement in group - dont show self case '0': // in format 'lowestpercent% ' case '1': // in format 'lowestpercent%mv ' case '2': // in format 'lowestpercent%move ' { for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && is_same_group( gch, ch ) && gch->max_move!=0){ if((gch->move*100/gch->max_move)<lowest_percent){ gvictim=gch; lowest_percent=(gch->move*100/gch->max_move); } } } if(lowest_percent<101){ if(*str=='2'){ sprintf( buf2, "%d%%move ", lowest_percent); i = buf2; }else if(*str=='1'){ sprintf( buf2, "%d%%mv ", lowest_percent); i = buf2; }else{ sprintf( buf2, "%d%% ", lowest_percent); i = buf2; } group_shown=true; }else{ i = ""; } break; } // lowest mana in group - dont show self case '3': // in format 'lowestpercent% ' case '4': // in format 'lowestpercent%m ' case '5': // in format 'lowestpercent%mn ' case '6': // in format 'lowestpercent%mana ' { for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && is_same_group( gch, ch ) && gch->max_mana!=0){ if((gch->mana*100/gch->max_mana)<lowest_percent){ gvictim=gch; lowest_percent=(gch->mana*100/gch->max_mana); } } } if(lowest_percent<101){ if(*str=='6'){ sprintf( buf2, "%d%%mana ", lowest_percent); i = buf2; }else if(*str=='5'){ sprintf( buf2, "%d%%mn ", lowest_percent); i = buf2; }else if(*str=='4'){ sprintf( buf2, "%d%%m ", lowest_percent); i = buf2; }else{ sprintf( buf2, "%d%% ", lowest_percent); i = buf2; } group_shown=true; }else{ i = ""; } break; } // lowest mana in group - dont show self case '7': // in format 'lowestpercent% ' case '8': // in format 'lowestpercent%h ' case '9': // in format 'lowestpercent%hp ' { for ( gch = ch->in_room->people; gch; gch = gch->next_in_room) { if( ch!=gch && is_same_group( gch, ch ) && gch->max_hit!=0){ if((gch->hit*100/gch->max_hit)<lowest_percent){ gvictim=gch; lowest_percent=(gch->hit*100/gch->max_hit); } } } if(lowest_percent<101){ if(*str=='9'){ sprintf( buf2, "%d%%hp ", lowest_percent); i = buf2; }else if(*str=='8'){ sprintf( buf2, "%d%%h ", lowest_percent); i = buf2; }else if(*str=='7'){ sprintf( buf2, "%d%% ", lowest_percent); i = buf2; }else{ sprintf( buf2, "%d%% ", lowest_percent); i = buf2; } group_shown=true; }else{ i = ""; } break; } case 'C': // return if group_shown is true; { if(group_shown){ i = "\r\n"; }else{ i = ""; } break; }; case 'G': // close bracket than if group_shown is true; { if(group_shown){ i = "]"; }else{ i = ""; } break; }; case 'L': // open bracket than if group_shown is true; { if(group_shown){ i = "["; }else{ i = ""; } break; }; default: i = "?unknown group code in use!?"; break; } }else{ i = "missing tailing code for %G at end of prompt!"; } break; case 'h' : sprintf( buf2, "%d", ch->hit ); i = buf2; break; case 'H' : sprintf( buf2, "%d", ch->max_hit ); i = buf2; break; case 'l' : sprintf( buf2, "%s", ch->language->name); i = buf2; break; case 'm' : if( ch->desc->original == NULL ) sprintf( buf2, "%d", ch->mana ); else sprintf( buf2, blank_string); i = buf2; break; case 'M' : sprintf( buf2, "%d", ch->max_mana ); i = buf2; break; case 'o' : sprintf( buf2, "%s", olc_ed_name(ch) ); i = buf2; break; case 'O' : sprintf( buf2, "%s", olc_ed_vnum(ch) ); i = buf2; break; case 'p' : if( ch->desc->original == NULL ) sprintf( buf2, "%ld", ch->pcdata->rp_points); else sprintf( buf2, blank_string); i = buf2; break; case 'P' : if(ch->pet) { if(ch->pet->in_room == ch->in_room) sprintf( buf2,"%3d%%",(int) ch->pet->hit*100/ch->pet->max_hit); else sprintf( buf2,"???%%"); i = buf2; } else { i = blank_string; } break; case 'r' : if( ch->in_room != NULL ) sprintf( buf2, "%s", ((!IS_NPC(ch) && IS_SET(ch->act,PLR_HOLYLIGHT)) || (!IS_AFFECTED(ch,AFF_BLIND) && !room_is_dark( ch->in_room ))) ? ch->in_room->name : "darkness"); else sprintf( buf2, blank_string ); i = buf2; break; case 's' : sprintf( buf2, "%ld", ch->silver); i = buf2; break; case 't' : if(IS_IMMORTAL(ch)){ sprintf( buf2, "%d:%02d%s", (time_info.hour % 12 == 0) ? 12 : time_info.hour % 12, time_info.minute, time_info.hour >= 12 ? "pm" : "am"); }else{ sprintf( buf2, "%d:%02d%s", (time_info.hour % 12 == 0) ? 12 : time_info.hour % 12, ((int)time_info.minute/5)*5, time_info.hour >= 12 ? "pm" : "am"); } i=buf2; break; case 'T' : sprintf( buf2, "%s", shorttime(NULL)); i = buf2; break; case 'v' : if(ch->mounted_on!=NULL) { sprintf( buf2, "MOUNT %d", ch->mounted_on->move ); } else { sprintf( buf2, "%d", ch->move ); } i = buf2; break; case 'V' : if(ch->mounted_on!=NULL) { sprintf( buf2, "%d", ch->mounted_on->max_move ); } else { sprintf( buf2, "%d", ch->max_move ); } i = buf2; break; case 'W' : // word version of hitpoints { int percent=ch->hit*100/(ch->max_hit?ch->max_hit:1); if(percent >= 100) i="excellent"; else if(percent >= 90) i="a few scratches"; else if(percent >= 75) i="small wounds and bruises"; else if(percent >= 50) i="quite a few wounds"; else if(percent >= 30) i="big nasty wounds and scratches"; else if(percent >= 15) i="pretty hurt"; else if(percent >= 0 ) i="`#`Rawful condition`&"; else i="`#`Rbleeding`& to death!"; } break; case 'x' : sprintf( buf2, "%d", ch->exp ); i = buf2; break; case 'X' : if(!IS_NPC(ch)) { sprintf(buf2, "%d", (ch->level + 1) * exp_per_level(ch,ch->pcdata->points) - ch->exp); } else sprintf( buf2, blank_string); i = buf2; break; case 'z' : if( ch->in_room != NULL ){ sprintf( buf2, "%s", ch->in_room->area->name ); }else{ sprintf( buf2, blank_string ); } i = buf2; break; // Immortal Prompt codes case 'R' : if( IS_IMMORTAL( ch ) && ch->in_room != NULL ) sprintf( buf2, "%d", ch->in_room->vnum ); else sprintf( buf2, blank_string ); i = buf2; break; case 'S' : if( IS_IMMORTAL( ch ) && ch->desc->original != NULL ) sprintf( buf2, "%s", ch->short_descr); else sprintf( buf2, blank_string ); i = buf2; break; case 'Z' : if( IS_IMMORTAL( ch ) && ch->in_room != NULL ) sprintf( buf2, "%s", area_fname(ch->in_room->area)); else sprintf( buf2, blank_string ); i = buf2; break; case '!' : // this code is used for crash testing the mud //- test debugging systems etc if(IS_IMMORTAL(ch) && ch->desc->original != NULL ) { logf("debug prompt code '%%!' in immortals prompt - immortal has switched, " "crashing the mud intentionally (use for auto debug testing)"); sprintf(buf2, "%d", (ch->level + 1) * exp_per_level(ch,ch->pcdata->points) - ch->exp); }else{ sprintf( buf2, "!!!"); // tell imm/morts they have the debugging code showing } i = buf2; break; } ++str; while( (*point = *i) != '\0' ){ ++point, ++i; } } *point='\0'; ch->print(buf); if(IS_SET(ch->comm,COMM_TELNET_GA)) { unsigned char b[3]={IAC, GA, '\0'}; write_to_buffer(ch->desc,(char*)&b[0],2); } ch->print("`x"); if(!IS_NULLSTR(ch->prefix)){ ch->print(ch->prefix); } if(IS_IRC(ch)){ ch->println(""); } if(d && d->visual_debugging_enabled && d->visual_debug_flush_before_prompt && d->visual_debug_strip_prompt){ d->visual_debug_buffer[0]='\0'; } return; } /**************************************************************************/ void flush_cached_write_to_buffer(connection_data *c) { // if data in the previous buffer flush it thru if(c->condense_count){ if(c->condense_count>2){ char buf[15]; sprintf(buf,"(x%d) ",c->condense_count); write_into_buffer_thru_process_colour( c, buf, str_len(buf)); } if(c->condense_count==2){ write_into_buffer_thru_process_colour( c, c->condense_buffer, c->condense_lastlen); } write_into_buffer_thru_process_colour( c, c->condense_buffer, c->condense_lastlen); c->condense_count=0; } } /**************************************************************************/ // Append onto an output buffer. void write_to_buffer( connection_data *c, const char *txt, int length ) { // make sure we are working with a valid descriptor if(!c){ bugf("write_to_buffer(): Being called with a NULL connection! - text = '%s'.", txt); return; } if(!IS_VALID(c)){ //IS_VALID ensures c!=NULL bugf("write_to_buffer(): Being called with invalid connection (socket %d) (%s).", c->connected_socket, CH(c)?CH(c)->name:"no name"); return; } // Find length incase caller didn't. if( length <= 0 ){ length = str_len(txt); } if(length==0){ return; } // condensing system makes no difference in combat // nor any non playing state if( c->connected_state==CON_PLAYING && c->character && !c->character->fighting) { if(length<MAX_CONDENSE_LENGTH && length>2) // cache it { if(c->condense_count) { if(c->condense_lastlen==length && !strcmp(c->condense_buffer, txt)) { c->condense_count++; return; } // different - flush the previous flush_cached_write_to_buffer(c); } // adding a new one strncpy(c->condense_buffer, txt, length+1); c->condense_buffer[length]='\0'; c->condense_count=1; c->condense_lastlen=length; return; } } // too big to cache flush_cached_write_to_buffer(c); write_into_buffer_thru_process_colour( c, txt, length ); } /**************************************************************************/ // Append onto an output buffer, in black and white void write_to_buffer_bw( connection_data *c, const char *txt, int length) { // make sure we are working with a valid descriptor if(!IS_VALID(c)){ //IS_VALID ensures d!=NULL bugf("write_to_buffer_bw(): Being called with invalid connection (socket %d) (%s).", c->connected_socket, CH(c)?CH(c)->name:"no name"); return; } flush_cached_write_to_buffer(c); if(c->parse_colour){ c->parse_colour=false; write_to_buffer( c, txt, length); flush_cached_write_to_buffer(c); c->parse_colour=true; }else{ write_to_buffer( c, txt, length); flush_cached_write_to_buffer(c); } } /**************************************************************************/ // Look for link-dead player to reconnect. bool check_reconnect( connection_data *c, char *, bool fConn ) { char_data *ch; for ( ch = char_list; ch != NULL; ch = ch->next ) { if( !IS_NPC(ch) && (!fConn || !ch->desc) && !str_cmp( c->character->name, ch->name ) ) { logf("Socket %d is reconnecting (%s)", c->connected_socket, ch->name ); if( fConn == false ) { replace_string( c->character->pcdata->pwd, ch->pcdata->pwd ); } else // ditch the duplicates that were just loaded { // first ditch the newly loaded pet if one exists if(c->character->pet){ c->character->pet->in_room=NULL; char_to_room( c->character->pet, get_room_index(ROOM_VNUM_LIMBO) ); extract_char( c->character->pet, true ); } // now ditch the loaded duplicate character c->character->in_room = NULL; free_char( c->character ); // now attach to original character c->character = ch; ch->desc = c; ch->timer = 0; ch->idle = 0; // reset mxp if(ch->pcdata){ ch->pcdata->mxp_enabled=false; } ch->mxp_send_init(); ch->println("Reconnecting. Use `=Creplay`x, `=Creplayroom`x, and `=Creplaychan`x to see missed events."); act( "$n has reconnected.", ch, NULL, NULL, TO_ROOM ); logf("%s@%s reconnected. (sock=%d)",ch->name, c->remote_hostname, c->connected_socket); wiznet("$N reconnects to $S linkdead character.", ch,NULL,WIZ_LINKS,0,0); c->connected_state = CON_PLAYING; if(!GAMESETTING2(GAMESET2_DONT_DISPLAY_WHO_4_LOGIN)){ // give autowho to everyone // - doesn't show imms automatically though for morts do_who( ch, "-noimm4morts" ); } } return true; } } return false; } /**************************************************************************/ // Check if already playing. bool check_playing( connection_data *c, char *name ) { connection_data *cold; for ( cold = connection_list; cold; cold = cold->next ) { if( cold != c && CH(cold) && cold->connected_state != CON_GET_NAME && cold->connected_state != CON_GET_OLD_PASSWORD && !str_cmp( name, CH(cold)->name) ) { logf( "check_playing() socket %d(%s) found that %d(%s) is already playing character '%s'!", c->connected_socket, c->remote_hostname, cold->connected_socket, cold->remote_hostname, CH(cold)->name); write_to_buffer( c, "That character is already playing.\r\n",0); write_to_buffer( c, "Do you wish to connect anyway (Y/N)?",0); if(IS_IRCCON(c)) write_to_buffer( c, "\r\n", 0 ); c->connected_state = CON_BREAK_CONNECT; return true; } } return false; } /**************************************************************************/ void stop_idling( char_data *ch, char *command ) { if(ch == NULL || ch->desc == NULL || ch->desc->connected_state != CON_PLAYING){ return; } ch->timer = 0; ch->idle = 0; // check if we remove their autoafk status if(!IS_NPC(ch) && IS_SET(ch->comm,COMM_AFK) && !strcmp(ch->pcdata->afk_message,"Auto AFK")) { if(str_cmp(command, "afk")){ if(str_len(command)<=2){ ch->println("Auto AFK not taken off until you type more than 2 characters."); }else{ REMOVE_BIT(TRUE_CH(ch)->comm,COMM_AFK); ch->println("Auto AFK mode removed."); ch->println(" Type 'replay' to see any tells you may have received."); } } } // check if we want to remove players from limbo if( ch->was_in_room == NULL || ch->in_room != get_room_index(ROOM_VNUM_LIMBO)) return; char_from_room( ch ); char_to_room( ch, ch->was_in_room ); ch->was_in_room=NULL; act( "$n has returned from the void.", ch, NULL, NULL, TO_ROOM ); return; } /**************************************************************************/ // string pager void show_string(connection_data *d, char *input) { static char *buffer; // permanantly allocated buffer if(buffer==NULL){ // dont use the stack for the pager memory buffer=new char[HSL + MSL*2]; assertp(buffer); } char buf[MIL]; register char *scan, *chk, *pad; int lines = 0, toggle = 1; int show_lines; one_argument(input,buf); // support typing 'continue', and not break out of the pager // used for MXP if(buf[0]=='c' && !str_cmp(buf, "continue")){ buf[0]='\0'; } if(buf[0] != '\0') { if(d->showstr_head) { free_string(d->showstr_head); d->showstr_head = 0; } d->showstr_point = 0; return; } if(d->character && !IS_IRCCON(d)){ show_lines = d->character->lines; }else{ show_lines = 0; } for (scan = buffer, pad=buffer+(MSL*20)-5; ; scan++, d->showstr_point++) { if(scan<pad && ( (((*scan = *d->showstr_point) == '\r' || *scan == '\n') && (toggle = -toggle) < 0) || (*scan== '1' && *(scan-1)=='`') ) // support for `1 newline colour codes ) { lines++; } else { if(!*scan || (show_lines > 0 && lines >= show_lines) || scan>=pad) { if(*scan=='\n'){ // budget hack till rewritten *(++scan) = '\0'; }else{ *(scan) = '\0'; } write_to_buffer(d,buffer,str_len(buffer)); for (chk = d->showstr_point; is_space(*chk); chk++); { if(!*chk) { if(d->showstr_head) { free_string(d->showstr_head); d->showstr_head = 0; } d->showstr_point = 0; } } return; } } } // buffer overrun checks if(scan==pad){ scan--; *scan='\0'; } return; } /**************************************************************************/ void act (const char *format, char_data *ch, const void *arg1, const void *arg2, ACTTO_TYPE type) { // to be compatible with older code act_new(format,ch,arg1,arg2,type,POS_RESTING); } /**************************************************************************/ // this function appends autodamage information... always directed to char void act_with_autodam_to_char(const char *format, char_data *ch, const void *arg1, const void *arg2, int damage_result) // always TO_CHAR { char str[MSL]; sprintf(str, "%s%s", format, autodamtext(ch, damage_result)); // to be compatible with older code act_new(str,ch,arg1,arg2,TO_CHAR,POS_RESTING); } /**************************************************************************/ char *act_new( const char *format, char_data *ch, const void *arg1, const void *arg2, ACTTO_TYPE type, int min_pos) { const char *colour_stripped_format=""; if(EXECUTING_SOCIAL){ char nocolour_format[MSL]; assert(str_len(format)<MSL); // if this is a colour social, we strip out the colour from the // format before we start... then we only show it to those who // have socials with stripped colour. // if EXECUTING_SOCIAL_IN_COLOUR is set, we only show those acts // to those who have socials in colour enabled strcpy(nocolour_format,strip_colour(format)); colour_stripped_format=nocolour_format; } char_data *to; char_data *vch = ( char_data * ) arg2; char_data *vch2 = ( char_data * ) arg1; OBJ_DATA *obj1 = ( OBJ_DATA * ) arg1; OBJ_DATA *obj2 = ( OBJ_DATA * ) arg2; const char *str; char *i = NULL; char *point; static char buf[MSL]; char fname[MIL]; int wizi_level=0; // Discard null and zero-length messages if(IS_NULLSTR(format)){ return ""; } // discard null chars and rooms if( !ch || !ch->in_room ) return ""; to = ch->in_room->people; int number_in_room=ch->in_room->number_in_room; if( type == TO_VICT ) { if( !vch ){ bug("Act: null vch with TO_VICT."); return ""; } if( !vch->in_room ){ return ""; } to = vch->in_room->people; number_in_room=vch->in_room->number_in_room; } if( type == TO_WORLD ){ if( !vch2 ){ bug( "Act: null vch2 with TO_WORLD." ); return ""; } if (vch2->in_room == NULL){ return ""; } to = vch2->in_room->people; number_in_room=vch2->in_room->number_in_room; } // note: number_in_room is used to prevent a mobprog on two mobs in // a room creating an endless loop by removing themself from // the room and putting themselves back in the room - Kal, June 01 for( ; to && --number_in_room>=0; to = to->next_in_room ) { if((!IS_NPC(to) && to->desc == NULL) || (IS_UNSWITCHED_MOB(to) && !HAS_TRIGGER(to, TRIG_ACT) && !IS_SET(to->act,ACT_MOBLOG)) || to->position < min_pos) { continue; } if( ( type == TO_CHAR ) && to != ch ) continue; if( type == TO_VICT && ( to != vch || to == ch ) ) continue; if( type == TO_ROOM && to == ch ) continue; if( type == TO_NOTVICT && (to == ch || to == vch) ) continue; if ( type == TO_WORLD && (to == ch || to == vch || to != vch2) ) continue; point = buf; // set the input format to a colour free version if it is a // social and this is for a mob or player opting to not have colour if(EXECUTING_SOCIAL){ if(to->pcdata){ if(to->pcdata->preference_colour_in_socials==PREF_OFF ||(to->pcdata->preference_colour_in_socials==PREF_AUTOSENSE && !GAMESETTING4(GAMESET4_GAMEDEFAULT_COLOUR_IN_SOCIALS_ON)) ){ // use opted to have socials without colour // or they used mud wide setting, which is off str = colour_stripped_format; }else{ // format potentially with colour in it str = format; } }else{ // mobs don't get colour in their socials str = colour_stripped_format; } }else{ str = format; } while ( *str != '\0' ) { if( *str != '$' ){ *point++ = *str++; continue; } ++str; i = " <@@@> "; if( !arg2 && *str >= 'A' && *str <= 'Z' ){ bugf( "Act: missing arg2 for code %d (%c).", *str, *str); i = " <@@@> "; }else{ switch ( *str ) { default: bugf( "Act: bad code %d (%c).", *str, *str); i = " <@@@> "; break; // Thx alex for 't' idea case 't': i = (char *) arg1; break; case 'T': i = (char *) arg2; break; case '$': i ="$"; break; // $$ = $ case 'n': i = PERS( ch, to ); wizi_level=UMAX(wizi_level,INVIS_LEVEL(ch)); break; case 'N': i = PERS( vch, to ); wizi_level=UMAX(wizi_level,INVIS_LEVEL(vch)); break; case 'e': i = he_she [URANGE(0, ch ->sex, 2)]; break; case 'E': i = he_she [URANGE(0, vch ->sex, 2)]; break; case 'm': i = him_her [URANGE(0, ch ->sex, 2)]; break; case 'M': i = him_her [URANGE(0, vch ->sex, 2)]; break; case 's': i = his_her [URANGE(0, ch ->sex, 2)]; break; case 'S': i = his_her [URANGE(0, vch ->sex, 2)]; break; case 'p': i = (can_see_obj( to, obj1 ) ? (obj1->short_descr?obj1->short_descr:(char*)""):(char*)"something"); if(IS_NULLSTR(i)){ i = "NULL OBJECT"; bug("Act: bad code with $p."); } break; case 'P': i = (can_see_obj( to, obj2 ) ? (obj2->short_descr?obj2->short_descr:(char*)""):(char*)"something"); if(IS_NULLSTR(i)){ i = "NULL OBJECT"; bug("Act: bad code with $P."); } break; case 'd': if( arg2 == NULL || ((char *) arg2)[0] == '\0' ) { i = "door"; } else { one_argument( (char *) arg2, fname ); i = fname; } break; } } ++str; while ( ( *point = *i ) != '\0' ){ ++point, ++i; } } *point = '\0'; // uppercase the start of the act if(buf[0]=='`'){ if(buf[2]=='`'){ if(buf[4]=='`'){ buf[6] = UPPER(buf[6]); }else{ buf[4] = UPPER(buf[4]); } }else{ buf[2] = UPPER(buf[2]); } }else{ buf[0] = UPPER(buf[0]); } // display it to the player/mob with a moblog/act trigger { if(wizi_level) { if(IS_TRUSTED(to, wizi_level)) { to->printlnf("%s%s", (IS_IMMORTAL(to)?FORMATF("[Wizi %d] ", wizi_level):""), buf); if(RECORD_TO_REPLAYROOM){ to->record_replayroom_event( FORMATF("%s%s", (IS_IMMORTAL(to)?FORMATF("[Wizi %d] ", wizi_level):""), buf)); } } }else{ to->println(buf); if(RECORD_TO_REPLAYROOM){ to->record_replayroom_event(buf); } } if(!to->desc && IS_NPC( to ) && (HAS_TRIGGER(to, TRIG_ACT) || IS_SET(to->act,ACT_MOBLOG))){ process_moblog(to, buf); if( !IS_NPC( ch )){ mp_act_trigger( buf, to, ch, arg1, arg2, TRIG_ACT ); } } } } return buf; } /**************************************************************************/ // Abort the mud and make a corefile void do_abort() { logf("do_abort()"); write_last_command(); abort(); } /**************************************************************************/ char * get_piperesult( char *cmd ); /**************************************************************************/ // make a corefile but dont stop the mud - unix only void make_corefile() { #ifdef unix if(!fork()){ if(!fork()){ abort(); }else{ // debug the core get_piperesult("sleep 15"); get_piperesult("processcore"); exit_error(1, "make_corefile", "creating corefile"); } } #else do_abort(); #endif } /**************************************************************************/ #ifdef WIN32 // win32 doesn't have this natively void gettimeofday( struct timeval *tp, void *tzp ) { struct _timeb temp_time; _ftime( &temp_time ); tp->tv_sec=(long)temp_time.time; // in winsock.h tv_sec is defined as a long tp->tv_usec=temp_time.millitm; } #endif /**************************************************************************/ void colour_convert_prefix(char colcode, char *text); /**************************************************************************/ void process_input(connection_data *c) { // make sure we are working with a valid descriptor if(!IS_VALID(c)){ //IS_VALID ensures c!=NULL bugf("process_input(): Being called with invalid connection (socket %d) (%s).", c->connected_socket, CH(c)?CH(c)->name:"no name"); return; } //handle daze and waits if(c->character && c->character->daze > 0){ --c->character->daze; } if( c->character && c->character->wait > 0 ){ --c->character->wait; return; } // get input from a descriptor read_from_buffer( c ); if( c->incomm[0] ) { // logf("process_input():'%s'", c->incomm); // debug code c->idle_since = current_time; // update idle timer c->fcommand = true; stop_idling( c->character, c->incomm ); // ### handle snooping here - system moved from interp ### // so can snoop olc for training purposes... // only unsnoopable command from interp.c tables // was password, // I haven't bothered writing checks for that, // if you are really worried about it, // you can write it yourself :) // // - Kal, Jan 98. if( c->snoop_by) { flush_cached_write_to_buffer(c->snoop_by); bool parse_normally=c->snoop_by->parse_colour; c->snoop_by->parse_colour=false; write_to_buffer( c->snoop_by, "##", 2 ); if(c->original){ write_to_buffer( c->snoop_by, c->original->name, 0); }else{ write_to_buffer( c->snoop_by, c->character?c->character->name:"unknown name?", 0); } write_to_buffer( c->snoop_by, "##>", 3 ); if( c->connected_state == CON_GET_OLD_PASSWORD || c->connected_state == CON_GET_NEW_PASSWORD || c->connected_state == CON_CONFIRM_NEW_PASSWORD) { write_to_buffer( c->snoop_by, (char *)"-=hidden password=-", 0 ); }else{ write_to_buffer( c->snoop_by, (char *) c->incomm, 0 ); } write_to_buffer( c->snoop_by, "\r\n", 2 ); flush_cached_write_to_buffer(c->snoop_by); c->snoop_by->parse_colour=parse_normally; } if( c->command_snoop) { flush_cached_write_to_buffer(c->command_snoop); bool parse_normally=c->command_snoop->parse_colour; c->command_snoop->parse_colour=false; write_to_buffer( c->command_snoop, "!", 2 ); write_to_buffer( c->command_snoop, shorttime(NULL), 0); write_to_buffer( c->command_snoop, "!", 2 ); if(c->original){ write_to_buffer( c->command_snoop, c->original->name, 0); }else{ write_to_buffer( c->command_snoop, c->character->name, 0); } write_to_buffer( c->command_snoop, "!!>", 3 ); write_to_buffer( c->command_snoop, (char *) c->incomm, 0 ); write_to_buffer( c->command_snoop, "\r\n", 2 ); flush_cached_write_to_buffer(c->command_snoop); c->command_snoop->parse_colour=parse_normally; } // Record the input tail ++inputtail_index%=MAX_INPUTTAIL; // rotate the buffer if(CH(c)){ if(c->connected_state==CON_GET_OLD_PASSWORD || c->connected_state==CON_GET_NEW_PASSWORD || c->connected_state==CON_FTP_AUTH || c->connected_state==CON_CONFIRM_NEW_PASSWORD){ // time name/short <descriptor/vnum of mob> Room%vnum sprintf(inputtail[inputtail_index],"%s %s<%d> (%d) R%d C%d E%d '%s'", shorttime(NULL), IS_NPC(CH(c)) ? CH(c)->short_descr : CH(c)->name, c->character && c->character->pIndexData ? c->character->pIndexData->vnum : 0, c->connected_socket, CH(c)->in_room ? CH(c)->in_room->vnum : 0, c->connected_state, c->editor, "<a password of some kind - hidden>"); }else{ // time name/short <descriptor/vnum of mob> Room%vnum sprintf(inputtail[inputtail_index],"%s %s<%d> (%d) R%d C%d E%d '%s'", shorttime(NULL), IS_NPC(CH(c)) ? CH(c)->short_descr : CH(c)->name, c->character && IS_NPC(c->character) ? c->character->pIndexData->vnum : 0, c->connected_socket, CH(c)->in_room ? CH(c)->in_room->vnum : 0, c->connected_state, c->editor, (char *) c->incomm); } }else{ if(c->connected_state==CON_FTP_AUTH){ sprintf (inputtail[inputtail_index], "%s input from socket %d - no character attached: dawnftp login", shorttime(NULL), c->connected_socket); }else{ sprintf (inputtail[inputtail_index], "%s input from socket %d - no character attached: %s", shorttime(NULL), c->connected_socket,(char *) c->incomm); } } // Record the input if(CH(c)){ sprintf (last_input, "Start input %d> [%5d] %s in [%5d] %s: %s", c->connected_socket, IS_NPC(CH(c)) ? CH(c)->pIndexData->vnum : 0, IS_NPC(CH(c)) ? CH(c)->short_descr : CH(c)->name, CH(c)->in_room ? CH(c)->in_room->vnum : 0, CH(c)->in_room ? CH(c)->in_room->name : "(not in a room)", (char *) c->incomm); }else{ sprintf(last_input, "Start input %d> no character or original: %s", c->connected_socket,(char *) c->incomm); } // translate a players choice of colour prefix to the internal version if(c->character && c->character->colour_prefix!=COLOURCODE){ // colour_convert_prefix() below writes directly back to c->incomm // assuming there is room for twice the size of c->incomm colour_convert_prefix(c->character->colour_prefix, c->incomm); } // OLC if( c->showstr_point ){ show_string( c, c->incomm ); }else if( c->pString ){ string_add( c->character, c->incomm ); }else{ switch ( c->connected_state ){ case CON_PLAYING: if( !run_olc_editor( c ) ){ interpret(c->character,substitute_alias( c, c->incomm )); } break; default: // while loop here so we can snarf // all mudftp data in one go if(c->incomm[0]){ sh_int last_connected_state=0; while(c->incomm[0]){ last_connected_state=c->connected_state; nanny(c, c->incomm); if(c->connected_state != CON_FTP_DATA) break; c->incomm[0] = '\0'; read_from_buffer( c ); } if(last_connected_state!= CON_FTP_DATA){ logf("End nanny state %2d, con state->%d (socket=%d)", last_connected_state, c->connected_state, c->connected_socket); } } break; } } // Record the input finished if(CH(c)){ sprintf (last_input, "End input %d> [%5d] %s in [%5d] %s: %s", c->connected_socket, IS_NPC(CH(c)) ? CH(c)->pIndexData->vnum : 0, IS_NPC(CH(c)) ? CH(c)->short_descr : CH(c)->name, CH(c)->in_room ? CH(c)->in_room->vnum : 0, CH(c)->in_room ? CH(c)->in_room->name : "(not in a room)", (char *) c->incomm); } else { sprintf (last_input, "End input %d> no character or original: %s", c->connected_socket,(char *) c->incomm); } c->incomm[0] = '\0'; } } /**************************************************************************/ void do_localecho ( char_data *ch, char * argument) { if( IS_NULLSTR(argument) ) // show instructions { ch->println("`xThis commands turns on and off your local telnet echo."); ch->println("Type `=Clocalecho on`x or `=Clocalecho off`x to use it."); return; } if(!str_prefix(argument,"on")) { ch->printf("Sending telnet local echo on sequence\r\n%s",echo_on_str ); return; } if(!str_prefix(argument,"off")) { ch->printf("Sending telnet local echo off sequence\r\n%s",echo_off_str ); return; } } /**************************************************************************/ void do_relookup( char_data *ch, char *argument) { char_data *victim; char arg[MIL]; one_argument( argument, arg ); { if( ( victim = get_whovis_player_world( ch, arg ) ) == NULL ) { ch->printlnf("There is no '%s' in the game.", arg); return; } if( IS_NPC(victim) ) { ch->println("Not on NPC's."); return; } if( !victim->desc ) { ch->printlnf("%s appears to be linkdead.", victim->name); return; } } ch->printlnf("Relooking up the host name on %s.", victim->name); ch->printlnf("BEFORE LOOKUP> Host: %s IP: %s Local: %d Remote: %d", victim->desc->remote_hostname, victim->desc->remote_ip, victim->desc->local_port, victim->desc->remote_port); resolver_query( victim->desc); } /**************************************************************************/ void sleep_seconds(int seconds) { #ifdef WIN32 Sleep( seconds*1000); #else sleep( seconds); #endif } /**************************************************************************/ /**************************************************************************/