/**************************************************************************/
// 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
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;
game_loop();
// closesocket(control);
// closesocket(irc_control);
close_network();
// 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();
// 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\">`c[Hit Return to continue]`x</send>")),
0 );
}else{
write_to_buffer( c, "`#`=\xaa""`c[Hit Return to continue]`x`^\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( ch->iwizi){
strcat( buf2, FORMATF(" `#(`Ciw`c%d`&)", ch->iwizi));
}
if( ch->owizi){
strcat( buf2, FORMATF(" `#(`Cow`c%d`&)", ch->owizi));
}
if(!IS_SET(TRUE_CH(ch)->comm, COMM_WHOVIS)){
strcat( buf2, " `#`c(`yWI`c)`&");
}
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 `Rreplay`c, `Rreplayroom`c, and `Rreplaychan`c 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
}
/**************************************************************************/
/**************************************************************************/