/* ************************************************************************ * File: comm.c Part of CircleMUD * * Usage: Communication, socket handling, main(), central game loop * * * * All rights reserved. See license.doc for complete information. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #define __COMM_C__ #include "conf.h" #include "sysdep.h" #ifdef CIRCLE_WINDOWS /* Includes for Win32 */ #include #include #else /* Includes for UNIX */ #include #include #include #include #include #endif #include "structs.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #include "handler.h" #include "db.h" #include "house.h" #include "clan.h" #include "screen.h" /*#include "color.c"*/ #include "quest.h" #include "world.h" #include "arena.h" #define TIME_STAMP_ON YES /* for the chronically afk, taerin */ #ifdef TIME_STAMP_ON void display_time_stamp(void); #endif #ifdef HAVE_ARPA_TELNET_H #include #else #include "telnet.h" #endif #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif /*#define YES 1 #define NO 0*/ /* externs */ extern int restrict; extern int mini_mud; extern int no_rent_check; extern FILE *player_fl; extern int DFLT_PORT; extern char *DFLT_DIR; extern int MAX_PLAYERS; extern int MAX_DESCRIPTORS_AVAILABLE; extern int sevent[5]; extern int in_arena; extern char *ANSI; extern struct room_data *world; /* In db.c */ extern int top_of_world; /* In db.c */ extern struct time_info_data time_info; /* In db.c */ extern char help[]; extern char *han_weekdays[]; /* local globals */ struct descriptor_data *descriptor_list = NULL; /* master desc list */ struct txt_block *bufpool = 0; /* pool of large output buffers */ int buf_largecount = 0; /* # of large buffers which exist */ int buf_overflows = 0; /* # of overflows of output */ int buf_switches = 0; /* # of switches from small to large buf */ int circle_shutdown = 0; /* clean shutdown */ int circle_reboot = 0; /* reboot the game after a shutdown */ int no_specials = 0; /* Suppress ass. of special routines */ int max_players = 0; /* max descriptors available */ int tics = 0; /* for extern checkpointing */ int scheck = 0; /* for syntax checking mode */ bool MOBTrigger = TRUE; /* For MOBProg */ extern int nameserver_is_slow; /* see config.c */ /*extern int auto_save; see config.c */ /*extern int autosave_time; see config.c */ struct timeval null_time; /* zero-valued time structure */ static bool fCopyOver; /* Are we booting in copyover mode? */ int mother_desc; /* Now a global */ int port; int level_exp(int class, int level); /* functions in this file */ int get_from_q(struct txt_q *queue, char *dest, int *aliased); void room_healing(void); void beware_lightning(void); void init_game(int port); void signal_setup(void); void game_loop(int mother_desc); void auction_update(void); int init_socket(int port); int new_descriptor(int s); int get_max_players(void); int process_output(struct descriptor_data *t); int process_input(struct descriptor_data *t); void close_socket(struct descriptor_data *d); struct timeval timediff(struct timeval a, struct timeval b); struct timeval timeadd(struct timeval a, struct timeval b); void flush_queues(struct descriptor_data *d); void nonblock(socket_t s); int perform_subst(struct descriptor_data *t, char *orig, char *subst); int perform_alias(struct descriptor_data *d, char *orig); void record_usage(void); void make_prompt(struct descriptor_data *point); void check_idle_passwords(void); void heartbeat(int pulse); void tick_grenade(void); void init_descriptor (struct descriptor_data *newd, int desc); void death_count_write(void); extern int top_of_mobt; extern struct char_data *mob_proto; /* extern fcnts */ void boot_db(void); void boot_world(void); void zone_update(void); void affect_update(void); /* In spells.c */ void point_update(void); /* In limits.c */ void mobile_activity(void); void room_activity(void); void string_add(struct descriptor_data *d, char *str); void perform_violence(void); void show_string(struct descriptor_data *d, char *input); int isbanned(char *hostname); void weather_and_time(int mode); extern void mprog_act_trigger(char *buf, struct char_data *mob, struct char_data *ch, struct obj_data *obj, void *vo); extern void proc_color(char *inbuf, int color); void pmail_check(void); void start_arena(); extern char *exp_graph(double nextlevelexp, double levelexp, double exp, int mode); extern struct title_type titles[1][LVL_BULSA + 1]; /* ********************************************************************* * main game loop and related stuff * ********************************************************************* */ /* Windows doesn't have gettimeofday, so we'll simulate it. */ #ifdef CIRCLE_WINDOWS void gettimeofday(struct timeval *t, struct timezone *dummy) { DWORD millisec = GetTickCount(); t->tv_sec = (int) (millisec / 1000); t->tv_usec = (millisec % 1000) * 1000; } #endif ACMD(do_save); int main(int argc, char **argv) { /* int port;*/ char buf[512]; int pos = 1; char *dir; port = DFLT_PORT; dir = DFLT_DIR; while ((pos < argc) && (*(argv[pos]) == '-')) { switch (*(argv[pos] + 1)) { case 'C': fCopyOver = TRUE; mother_desc = atoi(argv[pos]+2); break; case 'd': if (*(argv[pos] + 2)) dir = argv[pos] + 2; else if (++pos < argc) dir = argv[pos]; else { log("Directory arg expected after option -d."); exit(1); } break; case 'm': mini_mud = 1; no_rent_check = 1; log("Running in minimized mode & with no rent check."); break; case 'c': scheck = 1; log("Syntax check mode enabled."); break; case 'q': no_rent_check = 1; log("Quick boot mode -- rent check supressed."); break; case 'r': restrict = 1; log("Restricting game -- no new players allowed."); break; case 's': no_specials = 1; log("Suppressing assignment of special routines."); break; default: sprintf(buf, "SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1)); log(buf); break; } pos++; } if (pos < argc) { if (!isnhdigit(*argv[pos])) { fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n", argv[0]); exit(1); } else if ((port = atoi(argv[pos])) <= 1024) { fprintf(stderr, "Illegal port number.\n"); exit(1); } } #ifdef CIRCLE_WINDOWS if (_chdir(dir) < 0) { #else if (chdir(dir) < 0) { #endif perror("Fatal error changing to data directory"); exit(1); } sprintf(buf, "Using %s as data directory.", dir); log(buf); if (scheck) { boot_world(); log("Done."); exit(0); } else { sprintf(buf, "Running game on port %d.", port); log(buf); init_game(port); } return 0; } int enter_player_game(struct descriptor_data *d); /* Reload players after a copyover */ void copyover_recover() { struct descriptor_data *d; FILE *fp; char host[1024]; struct char_file_u tmp_store; int desc, player_i; bool fOld; char name[MAX_INPUT_LENGTH]; log ("Copyover recovery initiated"); fp = fopen (COPYOVER_FILE, "r"); /* there are some descriptors open which will hang forever then ? */ if (!fp) { perror ("copyover_recover:fopen"); log ("Copyover file not found. Exitting.\n\r"); exit (1); } /* In case something crashes - doesn't prevent reading */ unlink (COPYOVER_FILE); for (;;) { fOld = TRUE; fscanf (fp, "%d %s %s\n", &desc, name, host); if (desc == -1) break; /* Write something, and check if it goes error-free */ if (write_to_descriptor (desc, "\n\r»õ·Î¿î ¼³Á¤ Àû¿ëÁß...\n\r") < 0) { close (desc); /* nope */ continue; } /* create a new descriptor */ CREATE (d, struct descriptor_data, 1); memset ((char *) d, 0, sizeof (struct descriptor_data)); init_descriptor (d,desc); /* set up various stuff */ strcpy(d->host, host); d->next = descriptor_list; descriptor_list = d; d->connected = CON_CLOSE; /* Now, find the pfile */ CREATE(d->character, struct char_data, 1); clear_char(d->character); CREATE(d->character->player_specials, struct player_special_data, 1); d->character->desc = d; if ((player_i = load_char(name, &tmp_store)) >= 0) { store_to_char(&tmp_store, d->character); GET_PFILEPOS(d->character) = player_i; if (!PLR_FLAGGED(d->character, PLR_DELETED)) REMOVE_BIT(PLR_FLAGS(d->character),PLR_WRITING | PLR_MAILING | PLR_CRYO); else fOld = FALSE; } else fOld = FALSE; /* Player file not found?! */ if (!fOld) { write_to_descriptor (desc, "\n\r¹Ì¾ÈÇÕ´Ï´Ù. ´ç½ÅÀÇ Ä³·¢ÅÍ´Â »õ·Î¿î ¼³Á¤À» À§ÇØ Á¾·áÇÕ´Ï´Ù.\n\r"); close_socket (d); } else { /* ok! */ write_to_descriptor (desc, "\n\rÀç¼³Á¤ Àû¿ëÀÌ ¿Ï·á µÇ¾ú½À´Ï´Ù.\n\r"); enter_player_game(d); /* d->character->next = character_list; character_list = d->character;*/ d->connected = CON_PLAYING; look_at_room(d->character, 0); } } fclose (fp); } /* Init sockets, run game, and cleanup sockets */ void init_game(int port) { srandom(time(0)); log("Finding player limit."); max_players = get_max_players(); /* If copyover mother_desc is already set up */ if (!fCopyOver) { log("Opening mother connection."); mother_desc = init_socket(port); } boot_db(); #ifndef CIRCLE_WINDOWS log("Signal trapping."); signal_setup(); #endif if (fCopyOver) /* reload players */ copyover_recover(); log("Entering game loop."); game_loop(mother_desc); log("Saving mob death counts."); death_count_write(); log("Closing all sockets."); while (descriptor_list) close_socket(descriptor_list); CLOSE_SOCKET(mother_desc); fclose(player_fl); if (circle_reboot) { log("Rebooting."); exit(52); /* what's so great about HHGTTG, anyhow? */ } log("Normal termination of game."); } /* * init_socket sets up the mother descriptor - creates the socket, sets * its options up, binds it, and listens. */ int init_socket(int port) { int s, opt; struct sockaddr_in sa; /* * Should the first argument to socket() be AF_INET or PF_INET? I don't * know, take your pick. PF_INET seems to be more widely adopted, and * Comer (_Internetworking with TCP/IP_) even makes a point to say that * people erroneously use AF_INET with socket() when they should be using * PF_INET. However, the man pages of some systems indicate that AF_INET * is correct; some such as ConvexOS even say that you can use either one. * All implementations I've seen define AF_INET and PF_INET to be the same * number anyway, so ths point is (hopefully) moot. */ #ifdef CIRCLE_WINDOWS { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); if (WSAStartup(wVersionRequested, &wsaData) != 0) { log("WinSock not available!\n"); exit(1); } if ((wsaData.iMaxSockets - 4) < max_players) { max_players = wsaData.iMaxSockets - 4; } sprintf(buf, "Max players set to %d", max_players); log(buf); if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { fprintf(stderr, "Error opening network connection: Winsock err #%d\n", WSAGetLastError()); exit(1); } } #else if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("Error creating socket"); exit(1); } #endif /* CIRCLE_WINDOWS */ #if defined(SO_SNDBUF) opt = LARGE_BUFSIZE + GARBAGE_SPACE; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) { perror("setsockopt SNDBUF"); exit(1); } #endif #if defined(SO_REUSEADDR) opt = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { perror("setsockopt REUSEADDR"); exit(1); } #endif #if defined(SO_LINGER) { struct linger ld; ld.l_onoff = 0; ld.l_linger = 0; if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) { perror("setsockopt LINGER"); exit(1); } } #endif sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("bind"); CLOSE_SOCKET(s); exit(1); } nonblock(s); listen(s, 5); return s; } int get_max_players(void) { #if defined(CIRCLE_OS2) || defined(CIRCLE_WINDOWS) return MAX_PLAYERS; #else int max_descs = 0; char *method; /* * First, we'll try using getrlimit/setrlimit. This will probably work * on most systems. */ #if defined (RLIMIT_NOFILE) || defined (RLIMIT_OFILE) #if !defined(RLIMIT_NOFILE) #define RLIMIT_NOFILE RLIMIT_OFILE #endif { struct rlimit limit; /* find the limit of file descs */ method = "rlimit"; if (getrlimit(RLIMIT_NOFILE, &limit) < 0) { perror("calling getrlimit"); exit(1); } /* set the current to the maximum */ limit.rlim_cur = limit.rlim_max; if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { perror("calling setrlimit"); exit(1); } #ifdef RLIM_INFINITY if (limit.rlim_max == RLIM_INFINITY) max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; else max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max); #else max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max); #endif } #elif defined (OPEN_MAX) || defined(FOPEN_MAX) #if !defined(OPEN_MAX) #define OPEN_MAX FOPEN_MAX #endif method = "OPEN_MAX"; max_descs = OPEN_MAX; /* Uh oh.. rlimit didn't work, but we have * OPEN_MAX */ #elif defined (POSIX) /* * Okay, you don't have getrlimit() and you don't have OPEN_MAX. Time to * use the POSIX sysconf() function. (See Stevens' _Advanced Programming * in the UNIX Environment_). */ method = "POSIX sysconf"; errno = 0; if ((max_descs = sysconf(_SC_OPEN_MAX)) < 0) { if (errno == 0) max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; else { perror("Error calling sysconf"); exit(1); } } #else /* if everything has failed, we'll just take a guess */ max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; #endif /* now calculate max _players_ based on max descs */ max_descs = MIN(MAX_PLAYERS, max_descs - NUM_RESERVED_DESCS); if (max_descs <= 0) { sprintf(buf, "Non-positive max player limit! (Set at %d using %s).", max_descs, method); log(buf); exit(1); } sprintf(buf, "Setting player limit to %d using %s.", max_descs, method); log(buf); return max_descs; #endif /* WINDOWS or OS2 */ } /* * game_loop contains the main loop which drives the entire MUD. It * cycles once every 0.10 seconds and is responsible for accepting new * new connections, polling existing connections for input, dequeueing * output and sending it out to players, and calling "heartbeat" functions * such as mobile_activity(). */ void cls_spaces(char string[]) { char *p; for( p = string; isspace(*p); p++ ); strcpy(string, p); } void write_mud_date_to_file(void) { FILE *f; struct time_write date; f = fopen("etc/date_record", "w"); date.year = time_info.year; date.month = time_info.month; date.day = time_info.day; fwrite(&date,sizeof(struct time_write),1,f); fclose(f); } void game_loop(int mother_desc) { fd_set input_set, output_set, exc_set, null_set; struct timeval last_time, before_sleep, opt_time, process_time, now, timeout; char comm[MAX_INPUT_LENGTH]; /* char DELAY[MAX_INPUT_LENGTH]; */ struct descriptor_data *d, *next_d; int pulse = 0, missed_pulses, maxdesc, aliased; /* char result[MAX_INPUT_LENGTH];*/ /* initialize various time values */ null_time.tv_sec = 0; null_time.tv_usec = 0; opt_time.tv_usec = OPT_USEC; opt_time.tv_sec = 0; FD_ZERO(&null_set); gettimeofday(&last_time, (struct timezone *) 0); /* if (fCopyOver) copyover_recover();*/ /* The Main Loop. The Big Cheese. The Top Dog. The Head Honcho. The.. */ while (!circle_shutdown) { /* Sleep if we don't have any connections */ if (descriptor_list == NULL) { log("No connections. Going to sleep."); FD_ZERO(&input_set); FD_SET(mother_desc, &input_set); if (select(mother_desc + 1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0) { if (errno == EINTR) log("Waking up to process signal."); else perror("Select coma"); } else log("New connection. Waking up."); gettimeofday(&last_time, (struct timezone *) 0); } /* Set up the input, output, and exception sets for select(). */ FD_ZERO(&input_set); FD_ZERO(&output_set); FD_ZERO(&exc_set); FD_SET(mother_desc, &input_set); maxdesc = mother_desc; for (d = descriptor_list; d; d = d->next) { #ifndef CIRCLE_WINDOWS if (d->descriptor > maxdesc) maxdesc = d->descriptor; #endif FD_SET(d->descriptor, &input_set); FD_SET(d->descriptor, &output_set); FD_SET(d->descriptor, &exc_set); } /* * At this point, we have completed all input, output and heartbeat * activity from the previous iteration, so we have to put ourselves * to sleep until the next 0.1 second tick. The first step is to * calculate how long we took processing the previous iteration. */ gettimeofday(&before_sleep, (struct timezone *) 0); /* current time */ process_time = timediff(before_sleep, last_time); /* * If we were asleep for more than one pass, count missed pulses and sleep * until we're resynchronized with the next upcoming pulse. */ if (process_time.tv_sec == 0 && process_time.tv_usec < OPT_USEC) { missed_pulses = 0; } else { missed_pulses = process_time.tv_sec * PASSES_PER_SEC; missed_pulses += process_time.tv_usec / OPT_USEC; process_time.tv_sec = 0; process_time.tv_usec = process_time.tv_usec % OPT_USEC; } /* Calculate the time we should wake up */ last_time = timeadd(before_sleep, timediff(opt_time, process_time)); /* Now keep sleeping until that time has come */ gettimeofday(&now, (struct timezone *) 0); timeout = timediff(last_time, now); /* go to sleep */ do { #ifdef CIRCLE_WINDOWS Sleep(timeout.tv_sec * 1000 + timeout.tv_usec / 1000); #else if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) { if (errno != EINTR) { perror("Select sleep"); exit(1); } } #endif /* CIRCLE_WINDOWS */ gettimeofday(&now, (struct timezone *) 0); timeout = timediff(last_time, now); } while (timeout.tv_usec || timeout.tv_sec); /* poll (without blocking) for new input, output, and exceptions */ if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) { perror("Select poll"); return; } /* If there are new connections waiting, accept them. */ if (FD_ISSET(mother_desc, &input_set)) new_descriptor(mother_desc); /* kick out the freaky folks in the exception set */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &exc_set)) { FD_CLR(d->descriptor, &input_set); FD_CLR(d->descriptor, &output_set); close_socket(d); } } /* process descriptors with input pending */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &input_set)) if (process_input(d) < 0) close_socket(d); } /* process commands we just read from process_input */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; /* if((d->wait > 2) && !d->wait_pulse && d->input.head) { sprintf(DELAY, "&G%dÃÊ ±â´Ù¸®¼¼¿ä&w\r\n", (d->wait / PULSE_VIOLENCE) + 1); SEND_TO_Q(DELAY, d); d->wait_pulse = 1; } */ if ((--(d->wait) <= 0) && get_from_q(&d->input, comm, &aliased)) { if (d->character) { /* reset the idle timer & pull char back from void if necessary */ d->character->char_specials.timer = 0; if (!d->connected && GET_WAS_IN(d->character) != NOWHERE) { if (d->character->in_room != NOWHERE) char_from_room(d->character); char_to_room(d->character, GET_WAS_IN(d->character)); GET_WAS_IN(d->character) = NOWHERE; act("$n´ÔÀÌ µ¹¾Æ ¿Ô½À´Ï´Ù.", TRUE, d->character, 0, 0, TO_ROOM); } } d->wait = 1; d->prompt_mode = 1; d->wait_pulse = 0; if (strchr(comm, '~') != NULL) d->wait = 1; if (d->str) /* writing boards, mail, etc. */ string_add(d, comm); else if (d->showstr_count) /* reading something w/ pager */ show_string(d, comm); else if (d->connected != CON_PLAYING) /* in menus, etc. */ nanny(d, comm); else { /* else: we're playing normally */ if (aliased) /* to prevent recursive aliases */ d->prompt_mode = 0; /* else { cls_spaces(comm); if (strstr(comm, " ") == NULL) { if (perform_alias(d, comm)) get_from_q(&d->input, comm, &aliased); } else { sprintf(result, "%s", strrchr(comm, ' ')); comm[strlen(comm) - strlen(result)] = '\0'; if (perform_alias(d, result)) get_from_q(&d->input, result, &aliased); strcat(comm, result); } }*/ else { /* else: we're playing normally */ if (aliased) /* to prevent recursive aliases */ d->prompt_mode = 0; else { if (perform_alias(d, comm)) get_from_q(&d->input, comm, &aliased); } } command_interpreter(d->character, comm); } } } /* send it to interpreter */ /* send queued output out to the operating system (ultimately to user) */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &output_set) && *(d->output)) { if (process_output(d) < 0) close_socket(d); else d->prompt_mode = 1; } } /* kick out folks in the CON_CLOSE state */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (STATE(d) == CON_CLOSE) close_socket(d); } /* give each descriptor an appropriate prompt */ for (d = descriptor_list; d; d = d->next) { if (d->prompt_mode) { make_prompt(d); d->prompt_mode = 0; } } /* * Now, we execute as many pulses as necessary--just one if we haven't * missed any pulses, or make up for lost time if we missed a few * pulses by sleeping for too long. */ missed_pulses++; if (missed_pulses <= 0) { log("SYSERR: MISSED_PULSES IS NONPOSITIVE!!!"); missed_pulses = 1; } /* If we missed more than 30 seconds worth of pulses, forget it */ if (missed_pulses > (30 * PASSES_PER_SEC)) { log("Warning: Missed more than 30 seconds worth of pulses"); missed_pulses = 30 * PASSES_PER_SEC; } /* Now execute the heartbeat functions */ while (missed_pulses--) heartbeat(++pulse); /* Roll pulse over after 10 hours */ if (pulse >= (600 * 60 * PASSES_PER_SEC)) pulse = 0; /* Update tics for deadlock protection (UNIX only) */ tics++; write_mud_date_to_file(); } } void update_blood(void) { int i; extern int top_of_world; for (i = 0; i < top_of_world; i++) if (RM_BLOOD(i) > 0) RM_BLOOD(i) -= 1; } void heartbeat(int pulse) { static int mins_since_crashsave = 0; struct descriptor_data *pt; pt = descriptor_list; if (!(pulse % PULSE_ZONE)) { zone_update(); update_blood(); } if (!(pulse % (15 * PASSES_PER_SEC))) { /* 15 seconds */ check_idle_passwords(); if(sevent[0] && (sevent[1] > 20)) { sprintf(buf, "+=--=--=--=--=--=--=--=--=--=--=--=--==-==-==-==-==-==-==-==-==-==-==-==-==+\r\n| [&B¾È³»&w] %-3d - %-3d ·¹º§À» À§ÇÑ ¼­¹ÙÀ̹úÀÌ ÁغñÁß ÀÔ´Ï´Ù. Âü¿©¸í·É:<ÃâÀü> |\r\n+=--=--=--=--=--=--=--=--=--=--=--=--==-==-==-==-==-==-==-==-==-==-==-==-==+\r\n", sevent[2], sevent[3]); new_send_to_all(buf); } } if (!(pulse % PULSE_MOBILE)) mobile_activity(); if (!(pulse % PULSE_ROOM)) room_activity(); if (!(pulse % PULSE_VIOLENCE)) { tick_grenade(); perform_violence(); } if (in_arena == ARENA_START) if(!(pulse % PULSE_ARENA)) start_arena(); if (in_arena == ARENA_RUNNING) if(!(pulse % PULSE_ARENA)) do_game(); if ((pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC)) == ((SECS_PER_MUD_HOUR - 20) * PASSES_PER_SEC)) { strcpy(buf, "&y /~~~--.__,-~~~--._.-~~~-._.--._\r\n / &Wƽ 20ÃÊÀüÀÔ´Ï´Ù.&y __--~~-_\r\n /--~~--.___,-~~-.___.-~~~--'~&w\r\n\r\n"); for (pt = descriptor_list; pt; pt = pt->next) if(!pt->connected && pt->character && PRF_FLAGGED(pt->character, PRF_AUTOSTAT) && !PLR_FLAGGED(pt->character, PLR_WRITING | PLR_MAILING)) send_to_char(buf, pt->character); } if ((pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC)) == ((SECS_PER_MUD_HOUR - 10) * PASSES_PER_SEC)) { strcpy(buf, "&y /~~~--.__,-~~~--._.-~~~-._.--._\r\n / &Wƽ 10ÃÊÀüÀÔ´Ï´Ù.&y __--~~-_\r\n /--~~--.___,-~~-.___.-~~~--'~&w\r\n\r\n"); for (pt = descriptor_list; pt; pt = pt->next) if(!pt->connected && pt->character && PRF_FLAGGED(pt->character, PRF_AUTOSTAT) && !PLR_FLAGGED(pt->character, PLR_WRITING | PLR_MAILING)) send_to_char(buf, pt->character); } if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) { strcpy(buf, "&y /~~~--.__,-~~~--._.-~~~-._.--._\r\n / &Wƽƽƽ.. ƽÀÔ´Ï´Ù.&y __--~~-_\r\n /--~~--.___,-~~-.___.-~~~--'~&w\r\n\r\n"); for (pt = descriptor_list; pt; pt = pt->next) if(!pt->connected && pt->character && PRF_FLAGGED(pt->character, PRF_AUTOSTAT) && !PLR_FLAGGED(pt->character, PLR_WRITING | PLR_MAILING)) send_to_char(buf, pt->character); } if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) { weather_and_time(1); affect_update(); point_update(); fflush(player_fl); } if (!(pulse % (60 * PASSES_PER_SEC))){ beware_lightning(); } if (auto_save && !(pulse % (60 * PASSES_PER_SEC))) { /* 1 minute */ if (++mins_since_crashsave >= autosave_time) { send_to_all(" &b*¾È³»* desperado world¿¡¼­´Â ¸ÖƼ Ç÷¹ÀÌ¿Í ·Îº¿ Ç÷¹À̸¦ Çã¿ëÇÕ´Ï´Ù.\(¹«¹ýÀÚÀ̴ϱî\)\n *¹æ¼Û* desperado world 2.0 ÀÛ¾÷ÀÌ ÁøÇà Áß ÀÔ´Ï´Ù.&w\r\n"); mins_since_crashsave = 0; Crash_save_all(); House_save_all(); } } if (!(pulse % (5 * 60 * PASSES_PER_SEC))) /* 5 minutes */ record_usage(); if (!(pulse % (15 * PASSES_PER_SEC))) /* 15 ÃÊ ¸¶´Ù °æ¸Å ¹æ¼Û */ auction_update(); if (!(pulse % (15 * PASSES_PER_SEC))) /* 15 seconds */ pmail_check(); if (!(pulse % (5 * PASSES_PER_SEC))) room_healing(); if (!(pulse % (5 * PASSES_PER_SEC))) tick_grenade(); #ifdef TIME_STAMP_ON /* mudlog displays time every 5 minutes */ if (!(pulse % (5 * 60 * PASSES_PER_SEC))) display_time_stamp(); } #endif /* ****************************************************************** * general utility stuff (for local use) * ****************************************************************** */ /* * new code to calculate time differences, which works on systems * for which tv_usec is unsigned (and thus comparisons for something * being < 0 fail). Based on code submitted by ss@sirocco.cup.hp.com. */ /* * code to return the time difference between a and b (a-b). * always returns a nonnegative value (floors at 0). */ struct timeval timediff(struct timeval a, struct timeval b) { struct timeval rslt; if (a.tv_sec < b.tv_sec) return null_time; else if (a.tv_sec == b.tv_sec) { if (a.tv_usec < b.tv_usec) return null_time; else { rslt.tv_sec = 0; rslt.tv_usec = a.tv_usec - b.tv_usec; return rslt; } } else { /* a->tv_sec > b->tv_sec */ rslt.tv_sec = a.tv_sec - b.tv_sec; if (a.tv_usec < b.tv_usec) { rslt.tv_usec = a.tv_usec + 1000000 - b.tv_usec; rslt.tv_sec--; } else rslt.tv_usec = a.tv_usec - b.tv_usec; return rslt; } } /* add 2 timevals */ struct timeval timeadd(struct timeval a, struct timeval b) { struct timeval rslt; rslt.tv_sec = a.tv_sec + b.tv_sec; rslt.tv_usec = a.tv_usec + b.tv_usec; while (rslt.tv_usec >= 1000000) { rslt.tv_usec -= 1000000; rslt.tv_sec++; } return rslt; } void record_usage(void) { int sockets_connected = 0, sockets_playing = 0; struct descriptor_data *d; char buf[256]; for (d = descriptor_list; d; d = d->next) { sockets_connected++; if (!d->connected) sockets_playing++; } sprintf(buf, "nusage: %-3d sockets connected, %-3d sockets playing", sockets_connected, sockets_playing); log(buf); #ifdef RUSAGE { struct rusage ru; getrusage(0, &ru); sprintf(buf, "rusage: user time: %ld sec, system time: %ld sec, max res size: %ld", ru.ru_utime.tv_sec, ru.ru_stime.tv_sec, ru.ru_maxrss); log(buf); } #endif } /* * Turn off echoing (specific to telnet client) */ void echo_off(struct descriptor_data *d) { char off_string[] = { (char) IAC, (char) WILL, (char) TELOPT_ECHO, (char) 0, }; SEND_TO_Q(off_string, d); } /* * Turn on echoing (specific to telnet client) */ void echo_on(struct descriptor_data *d) { char on_string[] = { (char) IAC, (char) WONT, (char) TELOPT_ECHO, (char) TELOPT_NAOFFD, (char) TELOPT_NAOCRD, (char) 0, }; SEND_TO_Q(on_string, d); } char *hit_graph(double maxhit, double hit) { double tp; int i; char *re = NULL; tp = (hit / maxhit) * 20; i = tp; if(i < 0) i = 0; switch(i) { case 0: re = "[ ]"; break; case 1: re = "[= ]"; break; case 2: re = "[== ]"; break; case 3: re = "[=== ]"; break; case 4: re = "[==== ]"; break; case 5: re = "[===== ]"; break; case 6: re = "[====== ]"; break; case 7: re = "[======= ]"; break; case 8: re = "[======== ]"; break; case 9: re = "[========= ]"; break; case 10: re = "[==========]"; break; default: re = "[==========]"; } return(re); } char *hit_gra(struct char_data *ch) { float fullpes, pes1, pes2, pes; *buf = '\0'; fullpes=100; pes1=GET_MAX_HIT(ch); pes2=pes1/fullpes; pes=GET_HIT(ch)/pes2; if(GET_MAX_HIT(ch) < GET_HIT(ch)) pes=100; sprintf(buf,"%s",buf); if (pes <=5) sprintf(buf,"%s[ ]",buf); else if (pes >=91) sprintf(buf,"%s[[=12F======[=14F======[=10F======[=8F======]",buf); else if (pes >=81) sprintf(buf,"%s[[=12F======[=14F======[=10F======[=8F=== ]",buf); else if (pes >=71) sprintf(buf,"%s[[=12F======[=14F======[=10F======[=8F ]",buf); else if (pes > 61) sprintf(buf,"%s[[=12F======[=14F======[=10F=== ]",buf); else if (pes > 51) sprintf(buf,"%s[[=12F======[=14F====== ]",buf); else if (pes > 41) sprintf(buf,"%s[[=12F======[=14F=== ]",buf); else if (pes > 31) sprintf(buf,"%s[[=12F====== ]",buf); else if (pes > 21) sprintf(buf,"%s[[=12F=== ]",buf); else if (pes > 11) sprintf(buf,"%s[[=12F== ]",buf); else if (pes > 5) sprintf(buf,"%s[[=12F= ]",buf); /*for (i=1;i <= pes;i+=4) { sprintf(buf,"%s=",buf); } for (i=1; i<= 100-pes;i+=4) sprintf(buf,"%s [=15F",buf);*/ sprintf(buf,"%s[%3.0f%%",buf, pes); return(buf); } void make_prompt(struct descriptor_data *d) { char prompt[MAX_INPUT_LENGTH]; if (d->str) write_to_descriptor(d->descriptor, "] "); else if (d->showstr_count) { sprintf(prompt, /* "\r[ ¿£ÅÍ °è¼Ó, ( . )³¡, ( ] )´Ù½Ãº¸±â, ( [ )µÚ, ÆäÀÌÁö ¹øÈ£ (%d/%d) ]",*/ "\r[¿£ÅÍ(°è¼Ó),³¡(.),´Ù½Ãº¸±â(]),µÚ([)___________________________ÆäÀÌÁö¹øÈ£ (%d/%d)]", d->showstr_page, d->showstr_count); write_to_descriptor(d->descriptor, prompt); } else if (!d->connected) { char prompt[MAX_INPUT_LENGTH]; *prompt = '\0'; if(GET_BCOLOR(d->character)) sprintf(prompt, "[[=0G"); else sprintf(prompt, "[", GET_PKKILLS(d->character), GET_PKDEATH(d->character)); /* exp_graph(titles[0][GET_LEVEL(d->character) + 1].exp, titles[0][GET_LEVEL(d->character)].exp, GET_EXP(d->character), 0)); */ if (GET_INVIS_LEV(d->character)) sprintf(prompt, "%si%d ", prompt, GET_INVIS_LEV(d->character)); if (PRF_FLAGGED(d->character, PRF_AFK)) sprintf(prompt, "%s(AFK)", prompt); /* if (PRF_FLAGGED(d->character, PRF_DISPHP)) sprintf(prompt, "%s%d&w ", prompt, GET_HIT(d->character));*/ if(PRF_FLAGGED(d->character,PRF_DISPHP)) { if((GET_MAX_HIT(d->character) >> 1) <= GET_HIT(d->character)) sprintf(prompt,"%s%d\x1B[1;35m ",prompt,GET_HIT(d->character)); else if((GET_MAX_HIT(d->character) >> 1) > GET_HIT(d->character) && (GET_MAX_HIT(d->character) >> 2) <= GET_HIT(d->character)) sprintf(prompt,"%s\x1B[1;35m%d\x1B[1;35m ",prompt,GET_HIT(d->character)); else sprintf(prompt,"%s\x1B[1;35m%d\x1B[1;35m ",prompt,GET_HIT(d->character)); } if (PRF_FLAGGED(d->character, PRF_DISPMANA)) sprintf(prompt, "%s%d ", prompt, GET_MANA(d->character)); if (PRF_FLAGGED(d->character, PRF_DISPMOVE)) sprintf(prompt, "%s%d", prompt, GET_MOVE(d->character)); if(FIGHTING(d->character)) sprintf(prompt, "[=15F[=10F[%d/%d] [=12FVS %s %s", GET_MAX_HIT(d->character), GET_HIT(d->character), GET_NAME(FIGHTING(d->character)), hit_gra(FIGHTING(d->character))); strcat(prompt, "]\r\n\r\n"); write_to_descriptor(d->descriptor, prompt); } } void write_to_q(char *txt, struct txt_q *queue, int aliased) { struct txt_block *new; CREATE(new, struct txt_block, 1); CREATE(new->text, char, strlen(txt) + 1); strcpy(new->text, txt); new->aliased = aliased; /* queue empty? */ if (!queue->head) { new->next = NULL; queue->head = queue->tail = new; } else { queue->tail->next = new; queue->tail = new; new->next = NULL; } } int get_from_q(struct txt_q *queue, char *dest, int *aliased) { struct txt_block *tmp; /* queue empty? */ if (!queue->head) return 0; tmp = queue->head; strcpy(dest, queue->head->text); *aliased = queue->head->aliased; queue->head = queue->head->next; free(tmp->text); free(tmp); return 1; } /* Empty the queues before closing connection */ void flush_queues(struct descriptor_data *d) { int dummy; if (d->large_outbuf) { d->large_outbuf->next = bufpool; bufpool = d->large_outbuf; } while (get_from_q(&d->input, buf2, &dummy)); } /* Add a new string to a player's output queue */ void write_to_output(const char *txt, struct descriptor_data *t) { int size; size = strlen(txt); /* if we're in the overflow state already, ignore this new output */ if (t->bufptr < 0) return; /* if we have enough space, just write to buffer and that's it! */ if (t->bufspace >= size) { strcpy(t->output + t->bufptr, txt); t->bufspace -= size; t->bufptr += size; return; } /* * If we're already using the large buffer, or if even the large buffer * is too small to handle this new text, chuck the text and switch to the * overflow state. */ /* if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE)) {*/ if(size + t->bufptr > LARGE_BUFSIZE -1) { t->bufptr = -1; buf_overflows++; return; } buf_switches++; /* if the pool has a buffer in it, grab it */ if (bufpool != NULL) { t->large_outbuf = bufpool; bufpool = bufpool->next; } else { /* else create a new one */ CREATE(t->large_outbuf, struct txt_block, 1); CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE); buf_largecount++; } strcpy(t->large_outbuf->text, t->output); /* copy to big buffer */ t->output = t->large_outbuf->text; /* make big buffer primary */ strcat(t->output, txt); /* now add new text */ /* set the pointer for the next write */ t->bufptr = strlen(t->output); /* calculate how much space is left in the buffer */ t->bufspace = LARGE_BUFSIZE - 1 - strlen(t->output); } /* ****************************************************************** * socket handling * ****************************************************************** */ /* Initialize a descriptor */ void init_descriptor (struct descriptor_data *newd, int desc) { static int last_desc = 0; /* last descriptor number */ newd->descriptor = desc; /*newd->connected = CON_GET_NAME;*/ newd->connected = CON_QANSI; newd->idle_tics = 0; newd->wait = 1; newd->output = newd->small_outbuf; newd->bufspace = SMALL_BUFSIZE - 1; newd->next = descriptor_list; newd->login_time = time (0); if (++last_desc == 1000) last_desc = 1; newd->desc_num = last_desc; } int new_descriptor(int s) { socket_t desc; int sockets_connected = 0; unsigned long addr; int i; /* static int last_desc = 0;*/ /* last descriptor number */ struct descriptor_data *newd; struct sockaddr_in peer; struct hostent *from; /* accept the new connection */ i = sizeof(peer); if ((desc = accept(s, (struct sockaddr *) &peer, &i)) == INVALID_SOCKET) { perror("accept"); return -1; } /* keep it from blocking */ nonblock(desc); /* make sure we have room for it */ for (newd = descriptor_list; newd; newd = newd->next) sockets_connected++; if (sockets_connected >= max_players) { write_to_descriptor(desc, "Sorry, CircleMUD is full right now... please try again later!\r\n"); CLOSE_SOCKET(desc); return 0; } /* create a new descriptor */ CREATE(newd, struct descriptor_data, 1); memset((char *) newd, 0, sizeof(struct descriptor_data)); /* find the sitename */ if (nameserver_is_slow || !(from = gethostbyaddr((char *) &peer.sin_addr, sizeof(peer.sin_addr), AF_INET))) { /* resolution failed */ if (!nameserver_is_slow) perror("gethostbyaddr"); /* find the numeric site address */ addr = ntohl(peer.sin_addr.s_addr); sprintf(newd->host, "%03u.%03u.%03u.%03u", (int) ((addr & 0xFF000000) >> 24), (int) ((addr & 0x00FF0000) >> 16), (int) ((addr & 0x0000FF00) >> 8), (int) ((addr & 0x000000FF))); } else { strncpy(newd->host, from->h_name, HOST_LENGTH); *(newd->host + HOST_LENGTH) = '\0'; } /* determine if the site is banned */ if (isbanned(newd->host) == BAN_ALL) { CLOSE_SOCKET(desc); sprintf(buf2, "[%s]¿¡¼­ÀÇ Á¢¼ÓÀÌ °ÅºÎ»óÅÂÀÔ´Ï´Ù.", newd->host); mudlog(buf2, CMP, LVL_GOD, TRUE); free(newd); return 0; } #if 0 /* Log new connections - probably unnecessary, but you may want it */ sprintf(buf2, "[%s] »õ·Î¿î Á¢¼Ó", newd->host); mudlog(buf2, CMP, LVL_GOD, FALSE); #endif /* initialize descriptor data */ newd->descriptor = desc; /* newd->connected = CON_GET_NAME; newd->connected = CON_QANSI; newd->idle_tics = 0; newd->wait = 1; newd->output = newd->small_outbuf; newd->bufspace = SMALL_BUFSIZE - 1; newd->next = descriptor_list; newd->login_time = time(0); if (++last_desc == 1000) last_desc = 1; newd->desc_num = last_desc;*/ init_descriptor(newd, desc); /* prepend to list */ descriptor_list = newd; SEND_TO_Q(ANSI, newd); return 0; } int process_output(struct descriptor_data *t) { static char i[LARGE_BUFSIZE + GARBAGE_SPACE]; static int result; /* we may need this \r\n for later -- see below */ strcpy(i, "\r\n"); /* now, append the 'real' output */ strcpy(i + 2, t->output); /* if we're in the overflow state, notify the user */ if (t->bufptr < 0) strcat(i, "**OVERFLOW**"); /* add the extra CRLF if the person isn't in compact mode */ if (!t->connected && t->character && !PRF_FLAGGED(t->character, PRF_COMPACT)) strcat(i + 2, "\r\n"); if(t->character) proc_color(i, (clr(t->character, C_NRM))); /* * now, send the output. If this is an 'interruption', use the prepended * CRLF, otherwise send the straight output sans CRLF. */ if (!t->prompt_mode) /* && !t->connected) */ result = write_to_descriptor(t->descriptor, i); else result = write_to_descriptor(t->descriptor, i + 2); /* handle snooping: prepend "% " and send to snooper */ if (t->snoop_by) { SEND_TO_Q("% ", t->snoop_by); SEND_TO_Q(t->output, t->snoop_by); SEND_TO_Q("%%", t->snoop_by); } /* * if we were using a large buffer, put the large buffer on the buffer pool * and switch back to the small one */ if (t->large_outbuf) { t->large_outbuf->next = bufpool; bufpool = t->large_outbuf; t->large_outbuf = NULL; t->output = t->small_outbuf; } /* reset total bufspace back to that of a small buffer */ t->bufspace = SMALL_BUFSIZE - 1; t->bufptr = 0; *(t->output) = '\0'; return result; } int write_to_descriptor(socket_t desc, char *txt) { int total, bytes_written; total = strlen(txt); do { #ifdef CIRCLE_WINDOWS if ((bytes_written = send(desc, txt, total, 0)) < 0) { if (WSAGetLastError() == WSAEWOULDBLOCK) #else if ((bytes_written = write(desc, txt, total)) < 0) { #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) errno = EAGAIN; #endif /* EWOULDBLOCK */ if (errno == EAGAIN) #endif /* CIRCLE_WINDOWS */ log("process_output: socket write would block, about to close"); else perror("Write to socket"); return -1; } else { txt += bytes_written; total -= bytes_written; } } while (total > 0); return 0; } /* * ASSUMPTION: There will be no newlines in the raw input buffer when this * function is called. We must maintain that before returning. */ int process_input(struct descriptor_data *t) { int buf_length, bytes_read, space_left, failed_subst; char *ptr, *read_point, *write_point, *nl_pos = NULL; char tmp[MAX_INPUT_LENGTH]; /* first, find the point where we left off reading data */ buf_length = strlen(t->inbuf); read_point = t->inbuf + buf_length; space_left = MAX_RAW_INPUT_LENGTH - buf_length - 1; do { if (space_left <= 0) { log("process_input: about to close connection: input overflow"); return -1; } #ifdef CIRCLE_WINDOWS if ((bytes_read = recv(t->descriptor, read_point, space_left, 0)) < 0) { if (WSAGetLastError() != WSAEWOULDBLOCK) { #else if ((bytes_read = read(t->descriptor, read_point, space_left)) < 0) { #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) errno = EAGAIN; #endif /* EWOULDBLOCK */ if (errno != EAGAIN) { #endif /* CIRCLE_WINDOWS */ perror("process_input: about to lose connection"); return -1; /* some error condition was encountered on * read */ } else return 0; /* the read would have blocked: just means no * data there but everything's okay */ } else if (bytes_read == 0) { log("EOF on socket read (connection broken by peer)"); return -1; } /* at this point, we know we got some data from the read */ *(read_point + bytes_read) = '\0'; /* terminate the string */ /* search for a newline in the data we just read */ for (ptr = read_point; *ptr && !nl_pos; ptr++) if (ISNEWL(*ptr)) nl_pos = ptr; read_point += bytes_read; space_left -= bytes_read; /* * on some systems such as AIX, POSIX-standard nonblocking I/O is broken, * causing the MUD to hang when it encounters input not terminated by a * newline. This was causing hangs at the Password: prompt, for example. * I attempt to compensate by always returning after the _first_ read, instead * of looping forever until a read returns -1. This simulates non-blocking * I/O because the result is we never call read unless we know from select() * that data is ready (process_input is only called if select indicates that * this descriptor is in the read set). JE 2/23/95. */ #if !defined(POSIX_NONBLOCK_BROKEN) } while (nl_pos == NULL); #else } while (0); if (nl_pos == NULL) return 0; #endif /* POSIX_NONBLOCK_BROKEN */ /* * okay, at this point we have at least one newline in the string; now we * can copy the formatted data to a new array for further processing. */ read_point = t->inbuf; while (nl_pos != NULL) { write_point = tmp; space_left = MAX_INPUT_LENGTH - 1; for (ptr = read_point; (space_left > 1) && (ptr < nl_pos); ptr++) { if (*ptr == '\b') { /* handle backspacing */ if (write_point > tmp) { if (*(--write_point) == '$') { write_point--; space_left += 2; } else space_left++; } } else if ((isascii(*ptr) && isprint(*ptr)) || ishan(*ptr)) { if ((*(write_point++) = *ptr) == '$') { /* copy one character */ *(write_point++) = '$'; /* if it's a $, double it */ space_left -= 2; } else if ((unsigned char) *ptr == 0xff) { ptr++; write_point--; } else space_left--; } } *write_point = '\0'; if ((space_left <= 0) && (ptr < nl_pos)) { char buffer[MAX_INPUT_LENGTH + 64]; sprintf(buffer, "Line too long. Truncated to:\r\n%s\r\n", tmp); if (write_to_descriptor(t->descriptor, buffer) < 0) return -1; } if (t->snoop_by) { SEND_TO_Q("% ", t->snoop_by); SEND_TO_Q(tmp, t->snoop_by); SEND_TO_Q("\r\n", t->snoop_by); } failed_subst = 0; if (*tmp == '!') strcpy(tmp, t->last_input); /* else if (*tmp == '^') { if (!(failed_subst = perform_subst(t, t->last_input, tmp))) strcpy(t->last_input, tmp); }*/ else strcpy(t->last_input, tmp); if (!failed_subst) write_to_q(tmp, &t->input, 0); /* find the end of this line */ while (ISNEWL(*nl_pos)) nl_pos++; /* see if there's another newline in the input buffer */ read_point = ptr = nl_pos; for (nl_pos = NULL; *ptr && !nl_pos; ptr++) if (ISNEWL(*ptr)) nl_pos = ptr; } /* now move the rest of the buffer up to the beginning for the next pass */ write_point = t->inbuf; while (*read_point) *(write_point++) = *(read_point++); *write_point = '\0'; return 1; } /* * perform substitution for the '^..^' csh-esque syntax * orig is the orig string (i.e. the one being modified. * subst contains the substition string, i.e. "^telm^tell" */ int perform_subst(struct descriptor_data *t, char *orig, char *subst) { char new[MAX_INPUT_LENGTH + 5]; char *first, *second, *strpos; /* * first is the position of the beginning of the first string (the one * to be replaced */ first = subst + 1; /* now find the second '^' */ if (!(second = strchr(first, '^'))) { SEND_TO_Q("Invalid substitution.\r\n", t); return 1; } /* terminate "first" at the position of the '^' and make 'second' point * to the beginning of the second string */ *(second++) = '\0'; /* now, see if the contents of the first string appear in the original */ if (!(strpos = strstr(orig, first))) { SEND_TO_Q("Invalid substitution.\r\n", t); return 1; } /* now, we construct the new string for output. */ /* first, everything in the original, up to the string to be replaced */ strncpy(new, orig, (strpos - orig)); new[(strpos - orig)] = '\0'; /* now, the replacement string */ strncat(new, second, (MAX_INPUT_LENGTH - strlen(new) - 1)); /* now, if there's anything left in the original after the string to * replaced, copy that too. */ if (((strpos - orig) + strlen(first)) < strlen(orig)) strncat(new, strpos + strlen(first), (MAX_INPUT_LENGTH - strlen(new) - 1)); /* terminate the string in case of an overflow from strncat */ new[MAX_INPUT_LENGTH - 1] = '\0'; strcpy(subst, new); return 0; } void close_socket(struct descriptor_data *d) { char buf[128]; struct descriptor_data *temp; long target_idnum = -1; ACMD(do_goto); CLOSE_SOCKET(d->descriptor); flush_queues(d); /* Forget snooping */ if (d->snooping) d->snooping->snoop_by = NULL; if (d->snoop_by) { SEND_TO_Q("´ç½ÅÀÇ °¨½Ã ´ë»óÀÌ Á¢¼ÓÁ¾·á Çϼ̽À´Ï´Ù.\r\n", d->snoop_by); d->snoop_by->snooping = NULL; } if (d->character) { target_idnum = GET_IDNUM(d->character); if (d->connected == CON_PLAYING) { do_save(d->character,(char *)0,208,0); /*save_char(d->character, NOWHERE);*/ act("$n´ÔÀÇ À°½Å¿¡¼­ ¿µÈ¥ÀÌ ºüÁ®³ª°©´Ï´Ù.", TRUE, d->character, 0, 0, TO_ROOM); if(GET_LEVEL(d->character)<304){ sprintf(buf,"[&C¾Ë¸²&w] [&Y%s&w]´ÔÀÌ °ÔÀÓÀ» ¶°³µ½À´Ï´Ù\r\n",GET_NAME(d->character)); other_to_all(buf,d->character); } sprintf(buf, "°­Àç Á¢¼Ó ²÷±è: %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE); if(!d->SAVEHMV[3] && (d->character->in_room != NOWHERE) && IS_SET(ROOM_FLAGS(d->character->in_room), ROOM_PAROOM)) { GET_HIT(d->character) = d->SAVEHMV[0]; GET_MANA(d->character) = d->SAVEHMV[1]; GET_MOVE(d->character) = d->SAVEHMV[2]; do_goto(d->character, "3001", 0, 0); } if(d->SAVEHMV[3]) check_sevent(d->character, NULL, 1); STATE(d) = CON_CLOSE; d->character->desc = NULL; } else { sprintf(buf, "»ç¿ëÀÚ Á¢¼ÓÁ¾·á: %s.", GET_NAME(d->character) ? GET_NAME(d->character) : ""); mudlog(buf, CMP, LVL_IMMORT, TRUE); free_char(d->character); } } else mudlog("Losing descriptor without char.", CMP, LVL_IMMORT, TRUE); /* JE 2/22/95 -- part of my unending quest to make switch stable */ if (d->original && d->original->desc) d->original->desc = NULL; REMOVE_FROM_LIST(d, descriptor_list, next); if (d->showstr_head) free(d->showstr_head); if (d->showstr_count) free(d->showstr_vector); if (d->storage) free(d->storage); free(d); } void check_idle_passwords(void) { struct descriptor_data *d, *next_d; for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (STATE(d) != CON_PASSWORD && STATE(d) != CON_GET_NAME && STATE(d) != CON_QANSI) continue; if (!d->idle_tics) { d->idle_tics++; continue; } else { echo_on(d); SEND_TO_Q("\r\n½Ã°£Ãʰú.\r\n", d); STATE(d) = CON_CLOSE; } } } /* * I tried to universally convert Circle over to POSIX compliance, but * alas, some systems are still straggling behind and don't have all the * appropriate defines. In particular, NeXT 2.x defines O_NDELAY but not * O_NONBLOCK. Krusty old NeXT machines! (Thanks to Michael Jones for * this and various other NeXT fixes.) */ #ifdef CIRCLE_WINDOWS void nonblock(socket_t s) { long val; val = 1; ioctlsocket(s, FIONBIO, &val); } #else #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif void nonblock(socket_t s) { int flags; flags = fcntl(s, F_GETFL, 0); flags |= O_NONBLOCK; if (fcntl(s, F_SETFL, flags) < 0) { perror("Fatal error executing nonblock (comm.c)"); exit(1); } } /* ****************************************************************** * signal-handling functions (formerly signals.c) * ****************************************************************** */ RETSIGTYPE checkpointing() { if (!tics) { log("SYSERR: CHECKPOINT shutdown: tics not updated"); abort(); } else tics = 0; } RETSIGTYPE reread_wizlists() { void reboot_wizlists(void); mudlog("Signal received - rereading wizlists.", CMP, LVL_IMMORT, TRUE); reboot_wizlists(); } RETSIGTYPE unrestrict_game() { extern struct ban_list_element *ban_list; extern int num_invalid; mudlog("Received SIGUSR2 - completely unrestricting game (emergent)", BRF, LVL_IMMORT, TRUE); ban_list = NULL; restrict = 0; num_invalid = 0; } RETSIGTYPE hupsig() { log("Received SIGHUP, SIGINT, or SIGTERM. Shutting down..."); exit(0); /* perhaps something more elegant should * substituted */ } /* * This is an implementation of signal() using sigaction() for portability. * (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced * Programming in the UNIX Environment_. We are specifying that all system * calls _not_ be automatically restarted for uniformity, because BSD systems * do not restart select(), even if SA_RESTART is used. * * Note that NeXT 2.x is not POSIX and does not have sigaction; therefore, * I just define it to be the old signal. If your system doesn't have * sigaction either, you can use the same fix. * * SunOS Release 4.0.2 (sun386) needs this too, according to Tim Aldric. */ #ifndef POSIX #define my_signal(signo, func) signal(signo, func) #else sigfunc *my_signal(int signo, sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS */ #endif if (sigaction(signo, &act, &oact) < 0) return SIG_ERR; return oact.sa_handler; } #endif /* NeXT */ void signal_setup(void) { #ifndef CIRCLE_OS2 struct itimerval itime; #endif struct timeval interval; /* user signal 1: reread wizlists. Used by autowiz system. */ my_signal(SIGUSR1, reread_wizlists); /* * user signal 2: unrestrict game. Used for emergencies if you lock * yourself out of the MUD somehow. (Duh...) */ my_signal(SIGUSR2, unrestrict_game); /* * set up the deadlock-protection so that the MUD aborts itself if it gets * caught in an infinite loop for more than 3 minutes. Doesn't work with * OS/2. */ #ifndef CIRCLE_OS2 interval.tv_sec = 180; interval.tv_usec = 0; itime.it_interval = interval; itime.it_value = interval; setitimer(ITIMER_VIRTUAL, &itime, NULL); my_signal(SIGVTALRM, checkpointing); #endif /* just to be on the safe side: */ my_signal(SIGHUP, hupsig); my_signal(SIGINT, hupsig); my_signal(SIGTERM, hupsig); my_signal(SIGPIPE, SIG_IGN); my_signal(SIGALRM, SIG_IGN); #ifdef CIRCLE_OS2 #if defined(SIGABRT) my_signal(SIGABRT, hupsig); #endif #if defined(SIGFPE) my_signal(SIGFPE, hupsig); #endif #if defined(SIGILL) my_signal(SIGILL, hupsig); #endif #if defined(SIGSEGV) my_signal(SIGSEGV, hupsig); #endif #endif /* CIRCLE_OS2 */ } #endif /* CIRCLE_WINDOWS */ /* **************************************************************** * Public routines for system-to-player-communication * **************************************************************** */ void send_skip_message(const char *txt, struct descriptor_data *t) { char short_msg[MAX_STRING_LENGTH]; int size = 0, leng = 0; if(!t->bufptr) { sprintf(short_msg, "\r\n %s", txt); txt = short_msg; } if (t->bufptr < 0) return; size = strlen(txt); leng = t->lastskipleng; if((t->bufptr >= size) && (t->bufspace >= size) && (strlen(txt) > 2) && !strcmp(t->output + (t->bufptr - size), txt)) { t->skipnum += 1; t->bufspace += strlen(txt); t->bufptr -= strlen(txt); if(t->skipnum != 0) { t->bufspace += leng; t->bufptr -= leng; t->lastskipleng = 0; } memcpy(t->output, t->output, t->bufptr); sprintf(short_msg, "[x%d] ", t->skipnum + 1); t->lastskipleng = strlen(short_msg); sprintf(short_msg, "[x%d] %s", t->skipnum + 1, txt); txt = short_msg; } else { t->skipnum = 0; t->lastskipleng = 0; t->lastskipsize = 0; } write_to_output(txt, t); } void send_to_char(char *messg, struct char_data *ch) { if (ch->desc && messg) SEND_TO_Q(messg, ch->desc); } void other_to_all(char *messg,struct char_data *ch) { struct descriptor_data *i; if (!messg || !*messg) return; if (messg) for (i = descriptor_list; i; i = i->next) if (!i->connected&&i!=ch->desc) if(!PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING)) if(!PRF_FLAGGED(i->character,PRF_INFORM)) SEND_TO_Q(messg, i); } void new_send_to_all(char *messg) { struct descriptor_data *i; if(!messg || !*messg) return; for(i = descriptor_list; i; i = i->next) if(!i->connected && !PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING)) if(!PRF_FLAGGED(i->character,PRF_GUIDE)) send_to_char(messg, i->character); } void room_send_all(char *messg,int room) { struct descriptor_data *i; if(!messg || !*messg) return; for(i = descriptor_list; i; i = i->next) if(!i->connected && !PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING)) if(i->character->in_room==room) send_to_char(messg, i->character); } void fight_send_to_all(char *messg) { struct descriptor_data *i; if(!messg || !*messg) return; for(i = descriptor_list; i; i = i->next) if(!i->connected && !PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING)) if(!PRF_FLAGGED(i->character,(1<<27))) /*ÀüÀïÁ¤º¸ ¼³Á¤½Ã¿¡¸¸ */ send_to_char(messg, i->character); } void fight_info_send(char *msg){ char fightbuf[300]; if(!*msg) return; sprintf(fightbuf,"[&GÀüÀïÁ¤º¸&w] &w%s",msg); fight_send_to_all(fightbuf); } void company_send_to_all(char *messg,int cnum) { struct descriptor_data *i; if(!messg || !*messg) return; for(i = descriptor_list; i; i = i->next) if(!i->connected && !PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING)) if(i->character->player.cnum==cnum&&i->character->player.cpos>0) send_to_char(messg, i->character); } void send_to_arena(char *messg) { struct descriptor_data *d; for (d = descriptor_list; d; d = d->next) { if (d->descriptor && messg && PRF_FLAGGED(d->character, PRF_ARENA)) SEND_TO_Q(messg, d); } } void send_to_all(char *messg) { struct descriptor_data *i; if (!messg || !*messg) return; if (messg) for (i = descriptor_list; i; i = i->next) if (!i->connected) SEND_TO_Q(messg, i); } void send_to_except(char *messg, struct char_data *ch) { struct descriptor_data *i; if (messg && ch->desc) for (i = descriptor_list; i; i = i->next) if (!i->connected) if (i != ch->desc) SEND_TO_Q(messg, i); } void send_to_outdoor(char *messg) { struct descriptor_data *i; if (!messg || !*messg) return; for (i = descriptor_list; i; i = i->next) if (!i->connected && i->character && AWAKE(i->character) && OUTSIDE(i->character)) SEND_TO_Q(messg, i); } void send_to_room(char *messg, int room) { struct char_data *i; if (!messg || !*messg) return; if (messg) for (i = world[room].people; i; i = i->next_in_room) if (i->desc) SEND_TO_Q(messg, i->desc); } char *ACTNULL = ""; #define CHECK_NULL(pointer, expression) \ if ((pointer) == NULL) i = ACTNULL; else i = (expression); /* higher-level communication: the act() function */ void perform_act(char *orig, struct char_data *ch, struct obj_data *obj, void *vict_obj, struct char_data *to) { register char *i = NULL, *buf = NULL; static char lbuf[MAX_STRING_LENGTH]; buf = lbuf; for (;;) { if (*orig == '$') { switch (*(++orig)) { case 'n': CHECK_NULL(ch, PERS(ch, to)); break; case 'I': CHECK_NULL(obj, check_josa(obj->short_description, 0)); break; case 'l': CHECK_NULL(obj, check_josa(obj->short_description, 0)); break; case 'L': CHECK_NULL(obj, check_josa(obj->short_description, 1)); break; case 'k': CHECK_NULL(obj, check_josa(obj->short_description, 4)); break; case 'j': if(!ch) i = " "; else if(IS_NPC(ch)) i = check_josa(GET_NAME(ch), 0); else i = "´ÔÀÌ"; break; case 'J': if(!ch) i = " "; else if(IS_NPC(ch)) i = check_josa(GET_NAME(ch), 1); else i = "´ÔÀ»"; break; case 'v': if(!ch) i = " "; else if(IS_NPC(ch)) i = check_josa(GET_NAME(ch), 2); else i = "´Ô°ú"; break; case 'V': if(!ch) i = " "; else if(IS_NPC(ch)) i = check_josa(GET_NAME(ch), 4); else i = "´ÔÀº"; break; case 'd': if(!ch) i = " "; else if(IS_NPC(ch)) i = "ÀÇ"; else i = "´ÔÀÇ"; break; case 'g': if(!vict_obj) i = " "; else if(IS_NPC((struct char_data *) vict_obj)) i = check_josa(GET_NAME((struct char_data *) vict_obj), 0); else i = "´ÔÀÌ"; break; case 'G': if(!vict_obj) i = " "; else if(IS_NPC((struct char_data *) vict_obj)) i = check_josa(GET_NAME((struct char_data *) vict_obj), 1); else i = "´ÔÀ»"; break; case 'c': if(!vict_obj) i = " "; else if(IS_NPC((struct char_data *) vict_obj)) i = check_josa(GET_NAME((struct char_data *) vict_obj), 2); else i = "´Ô°ú"; break; case 'C': if(!vict_obj) i = " "; else if(IS_NPC((struct char_data *) vict_obj)) i = check_josa(GET_NAME((struct char_data *) vict_obj), 4); else i = "´ÔÀº"; break; case 'D': if(!vict_obj) i = " "; else if(IS_NPC((struct char_data *) vict_obj)) i = "ÀÇ"; else i = "´ÔÀÇ"; break; case 'N': CHECK_NULL(vict_obj, PERS((struct char_data *) vict_obj, to)); break; case 'm': i = HMHR(ch); break; case 'M': CHECK_NULL(vict_obj, HMHR((struct char_data *) vict_obj)); break; case 's': i = HSHR(ch); break; case 'S': CHECK_NULL(vict_obj, HSHR((struct char_data *) vict_obj)); break; case 'e': i = HSSH(ch); break; case 'E': CHECK_NULL(vict_obj, HSSH((struct char_data *) vict_obj)); break; case 'o': CHECK_NULL(obj, OBJN(obj, to)); break; case 'O': CHECK_NULL(vict_obj, OBJN((struct obj_data *) vict_obj, to)); break; case 'p': CHECK_NULL(obj, OBJS(obj, to)); break; case 'P': CHECK_NULL(vict_obj, OBJS((struct obj_data *) vict_obj, to)); break; case 'a': CHECK_NULL(obj, SANA(obj)); break; case 'A': CHECK_NULL(vict_obj, SANA((struct obj_data *) vict_obj)); break; case 'T': CHECK_NULL(vict_obj, (char *) vict_obj); break; case 'F': CHECK_NULL(vict_obj, fname((char *) vict_obj)); break; case '$': i = "$"; break; default: log("SYSERR: Illegal $-code to act():"); strcpy(buf1, "SYSERR: "); strcat(buf1, orig); log(buf1); break; } while ((*buf = *(i++))) buf++; orig++; } else if (!(*(buf++) = *(orig++))) break; } *(--buf) = '\r'; *(++buf) = '\n'; *(++buf) = '\0'; if (to->desc) SEND_TO_Q(CAP(lbuf), to->desc); if (MOBTrigger) mprog_act_trigger(lbuf, to, ch, obj, vict_obj); } #define SENDOK(ch) ((ch)->desc && (AWAKE(ch) || sleep) && \ !PLR_FLAGGED((ch), PLR_WRITING)) void act(char *str, int hide_invisible, struct char_data *ch, struct obj_data *obj, void *vict_obj, int type) { struct char_data *to; static int sleep; if (!str || !*str) { MOBTrigger = TRUE; return; } /* * Warning: the following TO_SLEEP code is a hack. * * I wanted to be able to tell act to deliver a message regardless of sleep * without adding an additional argument. TO_SLEEP is 128 (a single bit * high up). It's ONLY legal to combine TO_SLEEP with one other TO_x * command. It's not legal to combine TO_x's with each other otherwise. */ /* check if TO_SLEEP is there, and remove it if it is. */ if ((sleep = (type & TO_SLEEP))) type &= ~TO_SLEEP; if(!ch && (type != TO_VICT)) return; if(!ch && !vict_obj && !obj) return; if (type == TO_CHAR) { if (ch && SENDOK(ch)) perform_act(str, ch, obj, vict_obj, ch); MOBTrigger = TRUE; return; } if (type == TO_VICT) { if(!vict_obj) return; if ((to = (struct char_data *) vict_obj) && SENDOK(to)) perform_act(str, ch, obj, vict_obj, to); MOBTrigger = TRUE; return; } /* ASSUMPTION: at this point we know type must be TO_NOTVICT or TO_ROOM */ if (ch && ch->in_room != NOWHERE) to = world[ch->in_room].people; else if (obj && obj->in_room != NOWHERE) to = world[obj->in_room].people; else { log("SYSERR: no valid target to act()!"); return; } for (; to; to = to->next_in_room) if (SENDOK(to) && !(hide_invisible && ch && !CAN_SEE(to, ch)) && (to != ch) && (type == TO_ROOM || (to != vict_obj))) perform_act(str, ch, obj, vict_obj, to); MOBTrigger = TRUE; } void death_count_write(void) { FILE *fl; int nr; fl = fopen("world/mob/ripcount", "w"); for (nr = 0;nr <= top_of_mobt;nr++) fprintf(fl, "%d %ld %ld\n", nr, mob_proto[nr].points.deaths, mob_proto[nr].points.kills); fclose(fl); } void display_time_stamp() { struct tm *t; /* char *time_str, time_buf[64];*/ time_t right_now; right_now = time(0); t = localtime(&right_now); sprintf(buf, "½Ç½Ã°£ üũ: %d³â %d¿ù %dÀÏ (%s¿äÀÏ) %d½Ã %dºÐ %dÃÊ", t->tm_year+1900, t->tm_mon+1, t->tm_mday, han_weekdays[t->tm_wday], t->tm_hour, t->tm_min, t->tm_sec); mudlog(buf, NRM, LVL_IMMORT, FALSE); } void brag(struct char_data *ch, struct char_data *vict) { /* Npc taunts slayed player characters. Text goes out through gossip channel. Muerte - Telnet://betterbox.net:4000 */ struct descriptor_data *i; char brag[256]={" "}; switch (number(0, 11)) { case 0: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '±×·¸°Ô ¾àÇØ¼­¾ß... Âì!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 1: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '¾ß ¶Ç ´ýºô°Å³É! ǪÇÏÇÏ!!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 2: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) ǪÇÏÇÏ! ±× ½Ç·ÂÀ¸·Î ³¯ Á×ÀÌ·Á±¸?'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w",GET_NAME(vict)); break; case 3: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '¾ö¸¶ Á¥À̳ª ´õ¸Ô°í ¿Í!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 4: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) 'ÀÔ¿øÇÏ°í ½Í¾î ¹ÌÄ¡°ÚÁö! ǪÇÏÇÏ!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w",GET_NAME(vict)); break; case 5: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '¹Ùº¸°°Àº Áþ Á» ÇÏÁö¸¶!'¶ó°í $n´ÔÀ» ŸÀ̸¨´Ï´Ù.&w", GET_NAME(vict)); break; case 6: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) 'Çѹø¸¸ ´õ ½Î¿òÀ» °É¾îºÁ¶ó'¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 7: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '³Êµµ ÀÌÁ¦ ´ÙµÇ¾ú±º'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 8: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '³Í Ãʺ¸Á¸¿¡¼­³ª ³î¾Æ!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w",GET_NAME(vict)); break; case 9: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '½Ãü µ¹·ÁÁÙ±î ¸»±î^^'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w'", GET_NAME(vict)); break; case 10: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '¾ß ´Ï ½Ãü ã¾Æ°¡!'¶ó¸ç $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; case 11: sprintf(brag, "\x1B[0;0m\x1B[33m%s°¡(ÀÌ) '±îºÒÁö¸¶!'¶ó°í $n´ÔÀ» ºñ¿ô½À´Ï´Ù.&w", GET_NAME(vict)); break; } for (i = descriptor_list; i; i = i->next) if (!i->connected && i != ch->desc && i->character && !PRF_FLAGGED(i->character, PRF_NOGOSS) && !PLR_FLAGGED(i->character, PLR_WRITING) && !ROOM_FLAGGED(i->character->in_room, ROOM_SOUNDPROOF)) { if (COLOR_LEV(i->character) >= C_NRM) send_to_char(CCRED(i->character, C_NRM), i->character); act(brag, FALSE, ch, 0, i->character, TO_VICT | TO_SLEEP); if (COLOR_LEV(i->character) >= C_NRM) send_to_char(CCNRM(i->character, C_NRM), i->character); } } void sort_desc_in_list(struct descriptor_data *d) { struct descriptor_data *temp, *prev; //* If this is the only desc return if ((descriptor_list->character == d->character) && !d->next) return; //* remove the desc from list REMOVE_FROM_LIST(d, descriptor_list, next); //* find the correct spot fo the desc for (temp = descriptor_list,prev=NULL; temp;) { if (temp->character && (GET_LEVEL(temp->character) < GET_LEVEL(d->character))) { //* We've found the spot...put in here d->next = temp; if (!prev) descriptor_list = d; else prev->next = d; break; } else { //* If this is the end, stick desc here. if ((temp->character != d->character) &&!temp->next) { d->next = NULL; temp->next = d; break; } //* Step to next in list prev = temp; temp = temp->next; } } }