/* * This file contains all of the OS-dependent stuff: * startup, signals, BSD sockets for tcp/ip, i/o, timing. * * The data flow for input is: * Game_loop ---> Read_from_descriptor ---> Read * Game_loop ---> Read_from_buffer * * The data flow for output is: * Game_loop ---> Process_Output ---> Write_to_descriptor -> Write * * The OS-dependent functions are Read_from_descriptor and Write_to_descriptor. * -- Furey 26 Jan 1993 */ #include <sys/types.h> #include <sys/time.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <time.h> #include <unistd.h> #include <crypt.h> #include <mysql/mysql.h> #include "include.h" #include "channel.h" /* * Malloc debugging stuff. */ #if defined(sun) #undef MALLOC_DEBUG #endif #if defined(MALLOC_DEBUG) #include <malloc.h> extern int malloc_debug args((int)); extern int malloc_verify args((void)); #endif #include <signal.h> #if defined(apollo) #undef __attribute #endif #include <fcntl.h> #include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/telnet.h> const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' }; const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' }; const char go_ahead_str[] = { IAC, GA, '\0' }; /* * Global variables. */ DESCRIPTOR_DATA *descriptor_free; /* Free list for descriptors */ DESCRIPTOR_DATA *descriptor_list; /* All open descriptors */ DESCRIPTOR_DATA *d_next; /* Next descriptor in loop */ bool merc_down; /* Shutdown */ time_t current_time; /* Time of this pulse */ ACCOUNT *account_list; ACNT_CMD *acnt_cmd_list; ACNT_CMD *acnt_cmd_last; std::list<Attendant *> attendant_list; std::list<CodeBlob *> codeList; bool iCopyover = false; //A bool to tell if the muds in copyover mode for the new process bool oCopyover = false; //A bool to tell if the muds in copyover mode for the old process bool dCopyover = false; //A bool for the copyover.done file void game_loop_unix args((int control)); int init_socket args((int port)); void new_descriptor args((int control)); bool read_from_descriptor args((DESCRIPTOR_DATA * d)); bool write_to_descriptor args((int desc, char *txt, int length)); /* * Other local functions (OS-independent). */ bool check_parse_name args((char *name)); bool check_playing args((DESCRIPTOR_DATA * d, char *name)); int main args((int argc, char **argv)); void nanny args((DESCRIPTOR_DATA * d, char *argument)); bool process_output args((DESCRIPTOR_DATA * d, bool fPrompt)); void read_from_buffer args((DESCRIPTOR_DATA * d)); void initMysql(char *name, char *database, char *passwd, char *host); MYSQL *db; /* Made globle for Copyover */ int port, control, tempcont; int main(int argc, char **argv) { struct timeval now_time; /* * Memory debugging if needed. */ #if defined(MALLOC_DEBUG) malloc_debug(2); #endif initMysql("mudcon", "user", "password", "localhost"); Channel::loadChannels(); /* * Init time. */ gettimeofday(&now_time, NULL); current_time = (time_t) now_time.tv_sec; /* * Get the port number. */ port = 1234; if (argc > 1) { if (!is_number(argv[1])) { fprintf(stderr, "Usage: %s [port #]\n", argv[0]); exit(1); } else if ((port = atoi(argv[1])) <= 1024) { fprintf(stderr, "Port number must be above 1024.\n"); exit(1); } } /* Are we recovering from a copyover? */ if (argv[2] && argv[2][0]) { iCopyover = true; /* Can't start listening on a port untill the other pid dies */ if(!dCopyover) control = atoi(argv[3]); } /* * Run the game. */ if(!iCopyover) control = init_socket(port); boot_db(); if(iCopyover) { fclose(fopen("./copyover.save", "w" ) ); tempcont = atoi(argv[3]); copyover_loop(); /* This is a loop that checks for the files written by other process to insta copyover! For the new process*/ } game_loop_unix(control); close(control); /* * That's all, folks. */ exit(0); return 0; } int init_socket(int port) { static struct sockaddr_in sa_zero; struct sockaddr_in sa; int x = 1; int fd; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Init_socket: socket"); exit(1); } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof(x)) < 0) { perror("Init_socket: SO_REUSEADDR"); close(fd); exit(1); } #if defined(SO_DONTLINGER) && !defined(SYSV) { struct linger ld; ld.l_onoff = 1; ld.l_linger = 1000; if (setsockopt(fd, SOL_SOCKET, SO_DONTLINGER, (char *) &ld, sizeof(ld)) < 0) { perror("Init_socket: SO_DONTLINGER"); close(fd); exit(1); } } #endif sa = sa_zero; sa.sin_family = AF_INET; sa.sin_port = htons(port); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("Init_socket: bind"); close(fd); exit(1); } if (listen(fd, 3) < 0) { perror("Init_socket: listen"); close(fd); exit(1); } return fd; } void copyover_loop() { while(!dCopyover) { if(file_exists("./copyover.ready" ) ) { unlink("./copyover.ready"); recover_accounts(); control = tempcont; dCopyover = true; fclose(fopen("./copyover.done", "w" ) ); } } return; } void game_loop_unix(int control) { static struct timeval null_time; struct timeval last_time; signal(SIGPIPE, SIG_IGN); gettimeofday(&last_time, NULL); current_time = (time_t) last_time.tv_sec; /* Main loop */ while (!merc_down) { fd_set in_set; fd_set out_set; fd_set exc_set; DESCRIPTOR_DATA *d; int maxdesc; #if defined(MALLOC_DEBUG) if (malloc_verify() != 1) abort(); #endif if(oCopyover) { if(file_exists("./copyover.save" ) ) { for(d=descriptor_list; d;d=d->next ) { ACCOUNT *wch; if(d->connected == CON_OOC_CHAT ) { wch = d->account; save_account(wch); } unlink("./copyover.save" ); /* Make sure it doesn't keep readin it */ } fclose(fopen("./copyover.ready", "w" )); } if(file_exists("./copyover.done" ) ) { unlink("./copyover.done" ); exit(1); } } /* * Poll all active descriptors. */ FD_ZERO(&in_set); FD_ZERO(&out_set); FD_ZERO(&exc_set); FD_SET(control, &in_set); maxdesc = control; for (d = descriptor_list; d; d = d->next) { maxdesc = UMAX(maxdesc, d->descriptor); FD_SET(d->descriptor, &in_set); FD_SET(d->descriptor, &out_set); FD_SET(d->descriptor, &exc_set); } if (select(maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) < 0) { perror("Game_loop: select: poll"); exit(1); } /* * New connection? */ if (FD_ISSET(control, &in_set)) new_descriptor(control); /* * Kick out the freaky folks. */ for (d = descriptor_list; d != NULL; d = d_next) { d_next = d->next; if (FD_ISSET(d->descriptor, &exc_set)) { FD_CLR(d->descriptor, &in_set); FD_CLR(d->descriptor, &out_set); d->outtop = 0; close_socket(d); } } /* * Process input. */ for (d = descriptor_list; d != NULL; d = d_next) { d_next = d->next; d->fcommand = false; if (FD_ISSET(d->descriptor, &in_set)) { if (!read_from_descriptor(d)) { FD_CLR(d->descriptor, &out_set); d->outtop = 0; if(d->account) { infoChan("%s has left the MUD-Con.", d->account->name); free_account(d->account); } close_socket(d); continue; } } read_from_buffer(d); if (d->incomm[0] != '\0') { d->fcommand = true; if (d->connected == CON_OOC_CHAT) { if ( d->pString ) string_add( d->account, d->incomm ); else if(d->account && d->account->buffer ) d->account->page(); else if (d->showstr_point) show_string(d, d->incomm); else interp_acnt_cmd( d->account, d->incomm ); } else account_gen(d, d->incomm); d->incomm[0] = '\0'; } } /* * Autonomous game motion. */ /* * Output. */ for (d = descriptor_list; d != NULL; d = d_next) { d_next = d->next; if ((d->fcommand || d->outtop > 0) && FD_ISSET(d->descriptor, &out_set)) { if (!process_output(d, true)) { d->outtop = 0; close_socket(d); } } } /* * Synchronize to a clock. * Sleep( last_time + 1/PULSE_PER_SECOND - now ). * Careful here of signed versus unsigned arithmetic. */ { 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; if (select(0, NULL, NULL, NULL, &stall_time) < 0) { perror("Game_loop: select: stall"); exit(1); } } } gettimeofday(&last_time, NULL); current_time = (time_t) last_time.tv_sec; } return; } void new_descriptor(int control) { static DESCRIPTOR_DATA d_zero; char buf[MAX_STRING_LENGTH]; DESCRIPTOR_DATA *dnew; struct sockaddr_in sock; struct hostent *from; int desc; int size; size = sizeof(sock); getsockname(control, (struct sockaddr *) &sock, (socklen_t *) & size); if ( (desc = accept(control, (struct sockaddr *) &sock, (socklen_t *) & size)) < 0) { perror("New_descriptor: accept"); return; } #if !defined(FNDELAY) #define FNDELAY O_NDELAY #endif if (fcntl(desc, F_SETFL, FNDELAY) == -1) { perror("New_descriptor: fcntl: FNDELAY"); return; } /* * Cons a new descriptor. */ if (descriptor_free == NULL) { dnew = (DESCRIPTOR_DATA *) alloc_perm(sizeof(*dnew)); } else { dnew = descriptor_free; descriptor_free = descriptor_free->next; } *dnew = d_zero; dnew->descriptor = desc; dnew->connected = CON_GET_ACCOUNT_NAME; dnew->showstr_head = NULL; dnew->showstr_point = NULL; dnew->outsize = 2000; dnew->outbuf = (char *) alloc_mem(dnew->outsize); dnew->pEdit = NULL; dnew->pString = NULL; size = sizeof(sock); if (getpeername(desc, (struct sockaddr *) &sock, (socklen_t *) & size) < 0) { perror("New_descriptor: getpeername"); dnew->host = str_dup("(unknown)"); } else { /* * Would be nice to use inet_ntoa here but it takes a struct arg, * which ain't very compatible between gcc and system libraries. */ int addr; addr = ntohl(sock.sin_addr.s_addr); sprintf(buf, "%d.%d.%d.%d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr) & 0xFF); from = gethostbyaddr((char *) &sock.sin_addr, sizeof(sock.sin_addr), AF_INET); dnew->host = str_dup(from ? from->h_name : buf); } /* * Init descriptor data. */ dnew->next = descriptor_list; descriptor_list = dnew; /* * Send the greeting. */ { write_to_buffer(dnew, help_greeting, 0); } return; } void close_socket(DESCRIPTOR_DATA * dclose) { if (dclose->outtop > 0) process_output(dclose, false); if (dclose->snoop_by != NULL) write_to_buffer(dclose->snoop_by, "Your victim has left the game.\n\r", 0); { DESCRIPTOR_DATA *d; for (d = descriptor_list; d != NULL; d = d->next) { if (d->snoop_by == dclose) d->snoop_by = NULL; } } if (d_next == dclose) d_next = d_next->next; if (dclose == descriptor_list) { descriptor_list = descriptor_list->next; } else { DESCRIPTOR_DATA *d; for (d = descriptor_list; d && d->next != dclose; d = d->next); if (d != NULL) d->next = dclose->next; } close(dclose->descriptor); free_string(dclose->host); dclose->next = descriptor_free; descriptor_free = dclose; return; } bool read_from_descriptor(DESCRIPTOR_DATA * d) { int iStart; /* Hold horses if pending command already. */ if (d->incomm[0] != '\0') return true; /* Check for overflow. */ iStart = strlen(d->inbuf); if (iStart >= (int) sizeof(d->inbuf) - 10) { write_to_descriptor(d->descriptor, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0); return false; } /* Snarf input. */ for (;;) { int nRead; nRead = read(d->descriptor, d->inbuf + iStart, sizeof(d->inbuf) - 10 - iStart); if (nRead > 0) { iStart += nRead; if (d->inbuf[iStart - 1] == '\n' || d->inbuf[iStart - 1] == '\r') break; } else if (nRead == 0) return false; else if (errno == EWOULDBLOCK) break; else { perror("Read_from_descriptor"); return false; } } d->inbuf[iStart] = '\0'; return true; } /* * Transfer one line from input buffer to input line. */ void read_from_buffer(DESCRIPTOR_DATA * d) { int i, j, k; /* * Hold horses if pending command already. */ if (d->incomm[0] != '\0') return; /* * Look for at least one new line. */ for (i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++) { if (d->inbuf[i] == '\0') return; } /* * Canonical input processing. */ for (i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++) { if (k >= MAX_INPUT_LENGTH - 2) { write_to_descriptor(d->descriptor, "Line too long.\n\r", 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; } if (d->inbuf[i] == '\b' && k > 0) --k; else if (isascii(d->inbuf[i]) && isprint(d->inbuf[i])) 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 >= 20) { write_to_descriptor(d->descriptor, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0); strcpy(d->incomm, "quit"); } } } /* * Do '!' substitution. */ if (d->incomm[0] == '!') strcpy(d->incomm, d->inlast); else strcpy(d->inlast, d->incomm); /* * Shift the input buffer. */ while (d->inbuf[i] == '\n' || d->inbuf[i] == '\r') i++; for (j = 0; (d->inbuf[j] = d->inbuf[i + j]) != '\0'; j++) ; return; } /* * Low level output function. */ bool process_output(DESCRIPTOR_DATA * d, bool fPrompt) { extern bool merc_down; ACCOUNT *pAcnt; /* * Bust a prompt. */ if (fPrompt && !merc_down ) { if ( ( pAcnt = d->account ) && pAcnt->buffer ) { if(!pAcnt->buffer->isBlob) ptc(pAcnt, "{r[{WPress Enter to Continue{r]---[{W%d{r/{D%d{r]{x\n\r", pAcnt->buffer->pos, pAcnt->buffer->lines.size() ); } else if( d->connected == CON_OOC_CHAT ) { if(d->pString ) { write_to_buffer(d, ">", 0 ); write_to_buffer(d, go_ahead_str, 0); } else { write_to_buffer(d, "\n\r\n\r--> ", 0); write_to_buffer(d, go_ahead_str, 0); } } } /* * Short-circuit if nothing to write. */ if (d->outtop == 0) return true; /* * Snoop-o-rama. */ if (d->snoop_by != NULL) { write_to_buffer(d->snoop_by, "% ", 2); write_to_buffer(d->snoop_by, d->outbuf, d->outtop); } /* * OS-dependent output. */ if (!write_to_descriptor(d->descriptor, d->outbuf, d->outtop)) { d->outtop = 0; return false; } else { d->outtop = 0; return true; } } /* * Append onto an output buffer. */ void write_to_buffer(DESCRIPTOR_DATA * d, const char *txt, int length) { const char *tmp; tmp = colour_string(txt); length = strlen(tmp); /* * Initial \n\r if needed. */ if (d->outtop == 0 && !d->fcommand) { d->outbuf[0] = '\n'; d->outbuf[1] = '\r'; d->outtop = 2; } /* * Expand the buffer as needed. */ while (d->outtop + length >= d->outsize) { char *outbuf; outbuf = (char *) alloc_mem(2 * d->outsize); strncpy(outbuf, d->outbuf, d->outtop); free_mem(d->outbuf, d->outsize); d->outbuf = outbuf; d->outsize *= 2; } /* * Copy. */ strcpy(d->outbuf + d->outtop, tmp); d->outtop += length; return; } /* * Lowest level output function. * Write a block of text to the file descriptor. * If this gives errors on very long blocks (like 'ofind all'), * try lowering the max block size. */ bool write_to_descriptor(int desc, char *txt, int length) { int iStart; int nWrite; int nBlock; if (length <= 0) length = strlen(txt); for (iStart = 0; iStart < length; iStart += nWrite) { nBlock = UMIN(length - iStart, 4096); if ((nWrite = write(desc, txt + iStart, nBlock)) < 0) { logfp(LOG_BUG, "Write_to_descriptor: Bad Descriptor - %s", txt); return false; } } return true; } /* * Parse a name for acceptability. */ bool check_parse_name(char *name) { /* * Length restrictions. */ if (strlen(name) < 3) return false; if (strlen(name) > 12) return false; /* * Alphanumerics only. * Lock out IllIll twits. */ { char *pc; bool fIll; fIll = true; for (pc = name; *pc != '\0'; pc++) { if (!isalpha(*pc)) return false; if (LOWER(*pc) != 'i' && LOWER(*pc) != 'l') fIll = false; } if (fIll) return false; } return true; } /* The heart of the pager. Thanks to N'Atas-Ha, ThePrincedom for porting this SillyMud code for MERC 2.0 and laying down the groundwork. Thanks to Blackstar, hopper.cs.uiowa.edu 4000 for which the improvements to the pager was modeled from. - Kahn */ void show_string(struct descriptor_data *d, char *input) { char buffer[MAX_STRING_LENGTH]; char buf[MAX_INPUT_LENGTH]; register char *scan, *chk; int lines = 0, toggle = 1; one_argument(input, buf); switch (UPPER(buf[0])) { case '\0': case 'C': /* show next page of text */ lines = 0; break; case 'R': /* refresh current page of text */ lines = -1 - (d->pagelen); break; case 'B': /* scroll back a page of text */ lines = -(2 * d->pagelen); break; case 'H': /* Show some help */ write_to_buffer(d, "C, or Return = continue, R = redraw this page,\n\r", 0); write_to_buffer(d, "B = back one page, H = this help, Q or other keys = exit.\n\r\n\r", 0); lines = -1 - (d->pagelen); break; default: /*otherwise, stop the text viewing */ if (d->showstr_head) { free_string(d->showstr_head); d->showstr_head = str_dup(""); } free_string(d->showstr_point); d->showstr_point = str_dup(""); return; } /* do any backing up necessary */ if (lines < 0) { for (scan = d->showstr_point; scan > d->showstr_head; scan--) if ((*scan == '\n') || (*scan == '\r')) { toggle = -toggle; if (toggle < 0) if (!(++lines)) break; } d->showstr_point = scan; } /* show a chunk */ lines = 0; toggle = 1; for (scan = buffer;; scan++, d->showstr_point++) if (((*scan = *d->showstr_point) == '\n' || *scan == '\r') && (toggle = -toggle) < 0) lines++; else if (!*scan || (lines >= d->pagelen)) { *scan = '\0'; write_to_buffer(d, buffer, strlen(buffer)); /* See if this is the end (or near the end) of the string */ for (chk = d->showstr_point; isspace(*chk); chk++); if (!*chk) { if (d->showstr_head) { free_string(d->showstr_head); d->showstr_head = 0; } d->showstr_point = 0; } return; } return; } /* * Returns an initial-capped string. */ char *capitalize( const char *str ) { static char strcap[MAX_STRING_LENGTH]; int i; for ( i = 0; str[i] != '\0'; i++ ) strcap[i] = LOWER(str[i]); strcap[i] = '\0'; strcap[0] = UPPER(strcap[0]); return strcap; } const struct log_type log_table[] = { { LOG_BUG, "[BUG] - ", "../log/bug.log" }, { LOG_SITE, "[SITE] - ", "../log/connect.log" }, { LOG_CMD, "[CMD] - ", "../log/command.log" }, { LOG_TYPO, "[TYPO] - ", "../log/typo.log" }, { LOG_BANS, "[BANS] - ", "../log/bans.log" }, { LOG_MAX, NULL, NULL } }; void logfp(int log_value, char *fmt, ... ) { FILE *fp; va_list args; char buf[MSL]; char string[MSL]; int log_entry = 0; bool logfpound = false; struct tm *time_str; char crs_time[MSL]; va_start(args, fmt); vsprintf(string, fmt, args); va_end(args); for( log_entry = 0; log_entry < LOG_MAX; log_entry++ ) { if( log_table[log_entry].log_value == log_value ) { logfpound = true; break; } } if( !logfpound ) { sprintf( buf, "Invalid log type - %d", log_value ); perror(buf); return; } if(!file_exists(log_table[log_value].path ) ) { if( ( fp = fopen( log_table[log_value].path, "w" ) ) == NULL ) { perror(log_table[log_value].path); return; } } else { if( ( fp = fopen( log_table[log_value].path, "a" ) ) == NULL ) { perror(log_table[log_value].path); return; } } time_str = localtime( ¤t_time ); strftime (crs_time, 256, "[%B %d %Y %l:%M %p] ", time_str); fprintf(fp, "%s%s%s\n", crs_time, log_table[log_value].string, string ); fclose(fp); return; } bool file_exists( const char *path ) { FILE *fp = NULL; if((fp = fopen(path, "r")) != NULL) { fclose(fp); return true; } return false; } /* I know! I know! A cheap hack. But eh. Bite me :P */ void acnt_act(ACCOUNT *ch, void *vo, int to_flag, char *fmt, ... ) { ACCOUNT *victim = (ACCOUNT *) vo; DESCRIPTOR_DATA *d; va_list ap; /* points to each unnamed arg in turn */ char *p, *sval; char buf[MSL], buf2[MSL], tmp[MSL]; char *string; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); tmp[0] = '\0'; string = tmp; if(!ch) { logfp(LOG_BUG, "APRINTF: NULL ch pointer passed through command" ); return; } for(p = buf; *p; p++) { sval = NULL; if(*p != '$') { *string++ = *p; continue; } switch (*++p) { case 'n': sval = buf2; sprintf(sval, "%s", ch->name ); break; case 'N': sval = buf2; if(!victim) sprintf(sval, "%c", *p); else sprintf(sval, "%s", victim->name); break; default: sval = buf2; sprintf(sval, "$%c", *p); break; } while(*sval && sval) *string++ = *sval++; } *string = '\0'; sprintf(buf, "%s", tmp); switch(to_flag ) { case TO_CHAR: if(!ch) return; write_to_buffer(ch->desc, buf, 0); break; case NOTCHAR_WORLD: for( d = descriptor_list; d; d = d->next ) { ACCOUNT *wch; if(d->connected != CON_OOC_CHAT ) continue; wch = d->account; if(wch == ch ) continue; write_to_buffer(wch->desc, buf, 0); } break; case NOTVICT_WORLD: for( d= descriptor_list;d; d = d->next ) { ACCOUNT *wch; if( d->connected != CON_OOC_CHAT ) continue; wch = d->account; if( wch == victim ) continue; write_to_buffer(wch->desc, buf, 0); } break; case NOTARGET_WORLD: for( d= descriptor_list;d; d = d->next ) { ACCOUNT *wch; if( d->connected != CON_OOC_CHAT ) continue; wch = d->account; if( wch == victim || wch == ch) continue; write_to_buffer(wch->desc, buf, 0); } break; case TO_VICT: if(!victim) return; write_to_buffer(victim->desc, buf, 0); break; case TO_WORLD: for( d= descriptor_list;d; d = d->next ) { ACCOUNT *wch; if( d->connected != CON_OOC_CHAT ) continue; wch = d->account; write_to_buffer(wch->desc, buf, 0); } break; default: break; } return; } void recover_accounts() { DESCRIPTOR_DATA *d; FILE *fp; int desc = -1; char name [MSL]; char host[MSL]; ACCOUNT *wch; if( ( fp = fopen("./copyover.acnt", "r" ) ) == NULL ) { perror("./copyover.acnt" ); fclose(fopen("./copyover.fail", "w" ) ); /* If the dat file aint there. We gotta abort! */ exit(1); } for(;;) { fscanf (fp, "%d %s %s\n", &desc, name, host); if (desc == -1) break; /* Little test to make sure that the character's there */ if (!write_to_descriptor (desc, "The Darkness shifts in the realm. It is renewed.\n\r",0)) { close (desc); /* nope */ continue; } d = copyover_desc(); d->descriptor = desc; d->host = str_dup (host); d->next = descriptor_list; descriptor_list = d; d->connected = CON_OOC_CHAT; if( ( wch = load_account(d, name ) ) == NULL ) { write_to_descriptor( desc, "Your player was not found. Sorry. Try re-creating!\n\r",0 ); close_socket(d); continue; } write_to_buffer(d, go_ahead_str, 0); } unlink("./copyover.acnt"); /* Make sure we don't read it if it crashes */ return; } void ptc (ACCOUNT *ch, char *fmt, ...) { char buf [MAX_STRING_LENGTH]; va_list args; va_start (args, fmt); vsprintf (buf, fmt, args); va_end (args); write_to_buffer(ch->desc, buf, 0); } void seperateArgs(char *string, char *arg1, char *arg2) { bool after = false; for( ; *string != '\0' ; ++string ) { if(*string == ':' && !after) { after = true; continue; } if(after) { *arg2 = *string; ++arg2; } else { *arg1 = *string; ++arg1; } } *arg1 = '\0'; *arg2 = '\0'; } void disarmString(char *string) { for( ;*string != '\0' ; ++string) { if(*string == '`' || *string == '\'') *string = '\"'; } } void addAttendant(ACCOUNT *pAcnt) { Attendant *attnd = new Attendant(); free_string(attnd->name); free_string(attnd->mud); free_string(attnd->url); attnd->name = str_dup(pAcnt->name); attnd->url = str_dup(pAcnt->url); attnd->mud = str_dup(pAcnt->mud); attendant_list.push_back(attnd); char query[MSL]; sprintf(query, "INSERT INTO `Attendants` ( `name`, `mud`, `url`, `id` ) VALUES ('%s', '%s', '%s', '');", attnd->name, attnd->mud, attnd->url ); if( mysql_real_query(db, query, strlen(query) ) ) logfp(LOG_BUG, "addAttendant: %s", mysql_error(db) ); } void initMysql(char *name, char *database, char *passwd, char *host) { db = mysql_init(db); if( !( db = mysql_real_connect(db, host, name, passwd, database, 0, NULL, 0 ) ) ) { logfp(LOG_BUG, "initMysql: %s", mysql_error(db) ); abort(); } logfp(LOG_BUG, "initMysql: Database Enabled"); return; } char *getline( char *str, char *buf ); /*Buffer Stuffer */ void Buffer::Add(char *txt, ... ) { char buf[MAX_STRING_LENGTH], *ptr; char line[MSL]; va_list args; va_start (args, txt); vsprintf (buf, txt, args); va_end (args); isBlob = false; ptr = buf; finish = 0; while(*ptr) { std::string *str; ptr = getline(ptr, line); str = new std::string(line); lines.push_back(str); } } void Buffer::AddBlob( char *blob ) { char line[MSL]; isBlob = true; while(*blob) { std::string *str; blob = getline(blob, line); str = new std::string(line); lines.push_back(str); } return; } void Buffer::Empty() { std::list<std::string *>::iterator i, last; std::string *ptr; for( i = lines.begin() ; i != lines.end() ; ) { ptr = (*i); last = i; ++i; lines.erase(last); delete ptr; } } void Buffer::Send(ACCOUNT *pAcnt) { int atATime = 20; int start = 0; int length; if(!isBlob) length = (finish ? finish : lines.size()); else length = pAcnt->bufEnd; std::list<std::string *>::iterator i; i = lines.begin(); for(start = 0; start < (isBlob ? pAcnt->bufPos : pos) ; start++, i++); for(;(isBlob ? pAcnt->bufPos : pos) < start+atATime && (isBlob ? pAcnt->bufPos : pos) < length ; (isBlob ? ++pAcnt->bufPos : ++pos), i++) if(!isBlob) ptc(pAcnt, "%s\n\r", (*(i))->c_str()); else ptc(pAcnt, "{D%-4d{r|{x %s\n\r", pAcnt->bufPos+1, (*(i))->c_str()); } CodeBlob::CodeBlob(MYSQL_ROW row) { id = atoi(row[0]); language = str_dup(row[1]); subject = str_dup(row[2]); poster = str_dup(row[3]); blob = parseHtmlString(row[4]); codeList.push_back(this); buffer = new Buffer(); buffer->AddBlob(blob); } void infoChan(char *txt, ...) { char buf[MAX_STRING_LENGTH]; va_list args; va_start (args, txt); vsprintf (buf, txt, args); va_end (args); for(ACCOUNT *pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next) if(pAcnt->desc->connected == CON_OOC_CHAT) ptc(pAcnt, "{r[{WInfo{r] {D%s\n\r", buf); } void ACCOUNT::sendChanHelp() { INIT_BUFFER(this); buffer->Add(" {r[{D==={r]{WPlease Read{r[{D==={r] \n\r"); buffer->Add("{WWell! I hope you take that little warning into consideration, and do read this! \n\r"); buffer->Add("or you'll be totally lost once you enter this MudCon and realize that the channels\n\r"); buffer->Add("don't work for you to well! Now, first off let me go off by saying welcome to \n\r"); buffer->Add("MudCon V! This is my(Davion) first time hosting one of these things, and I hope \n\r"); buffer->Add("it goes off without a hitch! Now, lets just get to the point so you can go on in\n\r"); buffer->Add("and begin chattin away! \n\r"); buffer->Add(" {r[{D==={r]{WThe Channel Code{r[{D==={r] \n\r"); buffer->Add("{WMany hours went into the creation of these channels. They are slightly more \n\r"); buffer->Add("sophisticated than your normal code. If you've talked to me, or read somethings \n\r"); buffer->Add("I've posted, you've heard me refer to them as a living forum, if you haven't, you\n\r"); buffer->Add("have now! What I mean by this is it flows like a forum. Every thing you say over\n\r"); buffer->Add("the 11 designated channels is a reply to something on a topic. Does it have to be?\n\r"); buffer->Add("Yes. Why? Because it's the only solution I could come up with! Now don't quit! \n\r"); buffer->Add("I have set things up so it's not to hard to flow into a conversation without a \n\r"); buffer->Add("bunch of annoying syntax to bend your fingers around. This is going to be a bit \n\r"); buffer->Add("long, but bare with me, it'll be easier in the end. I will show you all the little\n\r"); buffer->Add("shortcuts I've put in to help you out. These might not be so obvious and easy to\n\r"); buffer->Add("pick up as you go! So lets get started! We're going to be using the Legal channel\n\r"); buffer->Add("as the example for this. Why? Because it has the shortest name! \n\r"); buffer->Add(" \n\r"); buffer->Add("First off, lets show you how to start a new topic! It's gunna be used frequently\n\r"); buffer->Add("(I hope). First off you need a subject, and the content for the first post (forum\n\r"); buffer->Add("like!) So! I have a question on the Diku license. That will be the subject, and \n\r"); buffer->Add("the content will be my question. To do that \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal !new Diku Licence Question:Do I have to leave the credits in?! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WYou seperate the subject, and the content of your first post with a colon(:). \n\r"); buffer->Add("Once that is executed, everyone on the MUD with the legal channel turned on will\n\r"); buffer->Add("recieve a notice that says you started a new Topic on the Legal channel, state \n\r"); buffer->Add("the subject, and show the first post. It'll look something like \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{r[{WLegal{r] {WDavion has started a new Topic{r[{WT{D:{W1{r]{D:{W Diku Licence Question\n\r"); buffer->Add("{r[{WLegal{r] {r[{WT{D:{W1{r][{WP{D:{W1{r] {WDavion {Dsays{r:{W Do I have to leave the credits in?!\n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WNow with this information, you have enough to reply to this thread! The [T:1][P:1]\n\r"); buffer->Add("part tells you the ID's of the topic (the 'T') and the post (the 'P'). These are\n\r"); buffer->Add("a vital part to start talking. To reply to this thread, we're once again going to\n\r"); buffer->Add("use that ever so useful colon(:) and use it to seperate the topic ID(tid) and \n\r"); buffer->Add("the post ID(pid). So! Lets reply to this. To do this \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal 1:1 Of course you do! Read the license in full! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WAnd you'll get something along the lines of \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{r[{WLegal{r] [{WT{D:{W1{r][{WP{D:{W2{r]{W In reply to {DDavion{r[{WP{D:{W1{r]{W %s {Dsays{r:{W Of course you do! Read\n\r",name); buffer->Add("the license in full! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WOk! I know I said there'd be no cumbersum syntax to bend your fingers around, so\n\r"); buffer->Add("I bet your thinking, 'Then wtf is 1:1! Cumbersom syntax you lying..', and ya, \n\r"); buffer->Add("that'd be some syntax, and it is a half lie! I have spend a few hours going over\n\r"); buffer->Add("the idea and came up with some automated features to sort them based on your last\n\r"); buffer->Add("reply. So now! Davion has some stupid rebuddle to make you hate him, and for him\n\r"); buffer->Add("to do that, he mearly types \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal But I really don't want the credits in! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WAnd the idiots reply is auto-sorted and thrown into reply to you! \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{r[{WLegal{r] [{WT{D:{W1{r][{WP{r:{W3{r]{W In reply to {D%s{r[{WP{D:{W2{r] {WDavion {Dsays{r:{W But I really don't want\n\r", name); buffer->Add("{Wthe credits in! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WNow I don't want to go on like this and show you an entire conversation with many\n\r"); buffer->Add("threads running off this one, but that'd get boring. I'll just tell you the other\n\r"); buffer->Add("short cut I implemented, and that is, you do not have to have both a PID and a \n\r"); buffer->Add("TID. You can simply use one or the other if you want to aim your post alittle \n\r"); buffer->Add("better. But make sure to insert the colon(:) so the MUD knows which one your \n\r"); buffer->Add("providing. So for no TID but a PID, it's \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal :3 I'm out of things to say. Stfu. \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WThat will reply to the 3rd post, on the last topic you replied too. If you use \n\r"); buffer->Add("the reverse, it'll simply reply to the last post made on that topic. I hope you \n\r"); buffer->Add("don't need an example of that one, cause I aint giving one! It's getting long \n\r"); buffer->Add("enough and your probably loosing interest! But only two more things! Well, two \n\r"); buffer->Add("and a half. So lets get to them. Quite obviously if your reading this you have \n\r"); buffer->Add("just arrived to MudCon for the first time (this should only pop up on character \n\r"); buffer->Add("creation. If your not in character creation, be scared. Be very scared. Anyways,\n\r"); buffer->Add("your going to want to get caught up. There's two ways to do this. The logs are \n\r"); buffer->Add("live, meaning they are sent directly to the website, or, simply use the history \n\r"); buffer->Add("command implemented directly into the mud. It has two levels two it. One shows \n\r"); buffer->Add("the last X (default 10) topics which is \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal !history \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WAnd you'll get \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{WThese are the last 10 topics made on Legal \n\r"); buffer->Add("{r[{WLegal{r][{WT{D:{W1{r] {DDavion{r:{WDiku Licence Question \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WClearly there's only one topic, cause I'm far to lazy to flesh the thing out by \n\r"); buffer->Add("myself, but that's why your reading this, right? Anyways. The next step. You got\n\r"); buffer->Add("the topics, now lets see the posts! \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal 1 !history \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WAnd you get \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{WThese are the last 10 posts made on Legal under Diku Licence Question \n\r"); buffer->Add("{r[{WLegal{r][{WT{D:{W1{r]{r[{WP{D:{W1{r] {DDavion says{r:{W Do I have to leave the credits in?!\n\r"); buffer->Add("{r[{WLegal{r][{WT{D:{W1{r]{r[{WP{D:{W2{r] {WIn reply to {DDavion{r[{WP{D:{W1{r] {D%s says{r: {WOf course you do! Read\n\r", name); buffer->Add("the license in full! \n\r"); buffer->Add("{r[{WLegal{r][{WT{D:{W1{r][{WP{D:{W3{r] {WIn reply to {D%s{r[{WP{D:{W2{r]{D Davion says{r:{W But I really don't want\n\r", name); buffer->Add("the credits in! \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WYa, not 10. Bah! Sue me!... Wait. Please don't! Ok! That's one and a half things\n\r"); buffer->Add("down! Last thing! Now I know you don't want to be spammed with useless topics \n\r"); buffer->Add("and all that jazz, so for your sanity (and mine!) I have implemented a way to \n\r"); buffer->Add("ignore entire topics. And this is really simple! \n\r"); buffer->Add(" \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add("{dlegal !ignore 1 \n\r"); buffer->Add("{r[{D=============================================================================={r]\n\r"); buffer->Add(" \n\r"); buffer->Add("{WThen you'll get some generic message telling you which topic your ignoring. I'd \n\r"); buffer->Add("bore you with it here, but I think I've done enough of that. {DFor some at-a-glance\n\r"); buffer->Add("{Dsyntax help, you can type the channel name with no arguments{W. Anyways. This is \n\r"); buffer->Add("the end of my little channel tutorial. I hope this system makes sense to you, if\n\r"); buffer->Add("it doesn't, I have a 'chat' channel that works like basic channels! I promise! \n\r"); buffer->Add("Thank you for reading! \n\r"); buffer->Add(" {r[{D==={r]{WEnd[{D==={r{D] \n\r"); return; }