/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA clicomm.c The server side file for RoAClient<-> RoAServer communications. Text messages/files/binary messages/ files (sound, graphics, etc) is all prepared and sent from this file. ******** 100% Completely Original Code ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** 100% Completely Original Code ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** V1 - original design... V2 - improved design, prevents single client from locking up main server at times, but too much overhead. Also forced recursive memory management between processes which dmalloc seemed to have a difficult time with. Basically fork()'d for every send but still blocked on read from client. V3 - Spawn a new process on bootup called cli_router to monitor the inet client port. Create a unique unix socket local to this machine (AF_UNIX) to allow this new router to communicate with the main mud server. Router is a mini server that waits for client connection attempts. When it receives one, it'll fork off another process to handle that individual client connection. This way, client lag only blocks each indiviual client as the main mud's unix socket never hears from the router until a full data block has been read from a client. cli_route.c is the entire routing process and is much more appropriate to fork off rather than forking off the entire mud for every write to a client. *************************************************************************/ #include "sysdep.h" #include "conf.h" #include <sys/socket.h> #include <sys/wait.h> #include <sys/resource.h> #include <netinet/in.h> #include <arpa/telnet.h> #include <arpa/inet.h> #include <netdb.h> #include <signal.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/un.h> #include <unistd.h> #include "structures.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #include "acmd.h" #include "handler.h" #include "db.h" #include "screen.h" #include "clicomm.h" #include "global.h" #include "objsave.h" #include "darkenelf.h" /* external functions */ extern void nonblock(int s); extern void block(int s); extern void who_to_buf(char *whobuf); extern char *winkillr(char *str); /* external vars */ extern dsdata *descriptor_list; extern dsdata *cdesc_list; extern cldesc *prelim_list; extern char *wv_bits[]; extern struct str_app_type str_app[]; // protos void update_client_who(void); void update_client_eq(dsdata *d); void update_client_inv(dsdata *d); void update_allclient_chan(void); void update_client_chan(dsdata *d); void update_client_stats(BOOL combat); void send_client_help_file(dsdata *d, char *name); void send_client_help_index(dsdata *d); void send_client_hitmanamove(dsdata *d); // internal to this file static int localport; // set up some client options... ACMD(do_client_options) { if (IS_NPC(ch)) return; one_argument(argument, arg); if (!*arg) { send_to_char("Usage: clientopt < sound | who | eq | inv | chan | stats >.\n\r",ch); return; } if (is_abbrev(arg, "stats")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTSTAT); if (PLR2_FLAGGED(ch, PLR2_CLIENTSTAT)) send_to_char("Client will be sent character stats.\n\r",ch); else send_to_char("Client will not be sent character stats.\n\r",ch); } else if (is_abbrev(arg, "sound")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTMUTE); if (PLR2_FLAGGED(ch, PLR2_CLIENTMUTE)) send_to_char("Client will not be sent sound data.\n\r",ch); else send_to_char("Client will be sent sound data.\n\r",ch); } else if (is_abbrev(arg, "who")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTWHO); if (PLR2_FLAGGED(ch, PLR2_CLIENTWHO)) send_to_char("Client will be sent who data.\n\r",ch); else send_to_char("Client will not be sent who data.\n\r",ch); } else if (is_abbrev(arg, "eq")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTEQ); if (PLR2_FLAGGED(ch, PLR2_CLIENTEQ)) send_to_char("Client will be sent eq data.\n\r",ch); else send_to_char("Client will not be sent eq data.\n\r",ch); } else if (is_abbrev(arg, "inv")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTINV); if (PLR2_FLAGGED(ch, PLR2_CLIENTINV)) send_to_char("Client will be sent inv data.\n\r",ch); else send_to_char("Client will not be sent inv data.\n\r",ch); } else if (is_abbrev(arg, "chan")) { TOGGLE_BIT(PLR2_FLAGS(ch), PLR2_CLIENTCHAN); if (PLR2_FLAGGED(ch, PLR2_CLIENTCHAN)) send_to_char("Client will be sent channel data.\n\r",ch); else send_to_char("Client will not be sent channel data.\n\r",ch); } else { send_to_char("Usage: clientopt < sound | who | eq | inv | chan | stats >.\n\r",ch); return; } } // for global client PID list, add and remove 12/5/97 -jtrhone void insert_clpid_entry(int pid, int bitvector, dsdata *d, char *stream) { clpid_info *cl; CREATE(cl, clpid_info, 1); cl->pid = pid; cl->bitvector = bitvector; cl->d = d; cl->ch = d->character; cl->stream = stream; cl->next = cl_pids; cl_pids = cl; } // when it's done downloading, reaper will call this to remove it // yank entry from list, free memory void delete_clpid_entry(clpid_info *cl) { clpid_info *temp = NULL; #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: Child client PID %d removed and freed.", cl->pid); mudlog(buf, BUG, LEV_IMM, TRUE); #endif REMOVE_FROM_LIST(cl, cl_pids, next); FREENULL(cl->stream); FREENULL(cl); } // when a descriptor gets extracted, wax all clpids associated with it // 3/1/98 -jtrhone void wax_descriptor_clpids(dsdata *d) { clpid_info *cl, *clnext; for (cl=cl_pids; cl; cl=clnext) { clnext = cl->next; if (cl->d == d) kill(SIGTERM, cl->pid); // the reaper will handle removing it from the list... } } // when a char gets extracted, null out all clpids associated with him/her // 3/1/98 -jtrhone void null_char_clpids(chdata *ch) { clpid_info *cl; for (cl=cl_pids; cl; cl=cl->next) if (cl->ch == ch) cl->ch = NULL; } int myread(int sok, char *trans, int length) { int cnt, sofar; sofar = 0; while (sofar < length) { cnt = read(sok, trans + sofar, length - sofar); if (cnt < 0) { if (errno == EAGAIN) { #ifdef DEBUG_MAX sprintf(buf, "%d out of %d bytes read...", sofar, length); log(buf); #endif continue; } else return cnt; } else if (!cnt) return cnt; sofar += cnt; #ifdef DEBUG_MAX sprintf(buf, "%d out of %d bytes read...", sofar, length); log(buf); #endif } return sofar; } int mywrite(int sok, char *trans, int length) { int cnt, sofar; sofar = 0; while (sofar < length) { cnt = write(sok, trans + sofar, length - sofar); if (cnt < 0) { if (errno == EAGAIN) { #ifdef DEBUG_MAX sprintf(buf, "%d out of %d bytes wrote...", sofar, length); log(buf); #endif continue; } else return cnt; } else if (!cnt) return cnt; sofar += cnt; #ifdef DEBUG_MAX sprintf(buf, "%d out of %d bytes wrote...", sofar, length); log(buf); #endif } return sofar; } // have to deep free this one void wax_dblock(dblock *blk) { if (blk) { FREENULL(blk->stream); FREENULL(blk); } } // after V3 update, no longer forked here 5/21/98 -jtrhone int send_dblock(dblock *blk, dsdata *d) { int size, bufsize, retval = -1, nbytes = 0; char *sndbuf; void (*func)(); func = signal(SIGPIPE, SIG_IGN); size = sizeof(dblock) + blk->streamlen; bufsize = size + sizeof(int); CREATE(sndbuf, char, bufsize); if (!sndbuf) { return -1; } // assemble the three pieces... header, block, stream memcpy(sndbuf, (void *) &size, sizeof(int)); memcpy(&(sndbuf[sizeof(int)]), blk, sizeof(dblock)); memcpy(&(sndbuf[sizeof(int)+sizeof(dblock)]), blk->stream, blk->streamlen); nbytes = mywrite(d->cdesc, sndbuf, bufsize); if (nbytes != bufsize) { FREENULL(sndbuf); sprintf(buf, "SYSERR: %d bytes out of %d wrote on d->cdesc: %d\n", nbytes, bufsize, d->cdesc); log(buf); perror("send_dblock:"); return -1; } else retval = size; FREENULL(sndbuf); signal(SIGPIPE, func); return retval; } // send a TEXT_MESG to client... V2 2/13/98 -jtrhone int send_msg_to_client(char *msg, dsdata *d) { dblock blk; // assemble the block... blk.type = TEXT_MESG; blk.version = 2; strcpy(blk.str1, msg); strcpy(blk.str2, ""); blk.streamlen = 0; blk.stream = NULL; return send_dblock(&blk, d); } // fork off a client router... 8/4/98 -jtrhone void start_cli_router(void) { char sport[64]; dsdata *d; extern int MASTER_DESCRIPTOR; extern int CLIENT_DESCRIPTOR; // flush everything before fork fflush(NULL); // before we do anything, fork off a client router... switch ((cli_router_pid = fork())) { case -1: log("Error forking off cli_router..."); exit(-1); case 0: // child close(MASTER_DESCRIPTOR); close(CLIENT_DESCRIPTOR); // close every descriptor in this child.. Descriptors(d) close(d->descriptor); sprintf(sport, "%d", localport); execl("../bin/cli_router", "cli_router", sport, NULL); log("Error in execl() for cli_router!"); exit(-1); default: sprintf(buf, "SYSUPD: cli_router %d started.", cli_router_pid); mudlog(buf, BRF, LEV_IMM, TRUE); break; } } // sets up the client descriptor - creates the socket, binds it, and listens. int init_client_socket(int port) { int s, opt; struct sockaddr_un sa; // remember this port this runtime... localport = port; // fork off a router first... start_cli_router(); // create local socket name based on pid... sprintf(local_sockname, "/tmp/rcli_sock_%d", getpid()); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("Create client socket"); exit(1); } #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.sun_family = AF_UNIX; strcpy(sa.sun_path, local_sockname); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("client bind"); close(s); exit(1); } nonblock(s); listen(s, 5); return s; } // read in a data block from this descriptor // this allocs memory for the data block, BTW int read_dblock(void **blk_ptr, int desc) { int size; int nbytes; char *msg; void (*func)(); func = signal(SIGPIPE, SIG_IGN); errno = 0; // error check here... nbytes = myread(desc, (char *)&size, sizeof(int)); if (nbytes != sizeof(int)) { #ifdef DEBUG_MAX printf("ERROR: %d bytes out of %d read on desc: %d\n", nbytes, sizeof(int), desc); perror("hmm:"); #endif return -1; } #ifdef DEBUG_MAX sprintf(buf, "Allocating for %d size...", size); log(buf); #endif if (!size) { log("SYSERR: read_dblock, 0 bytes read."); return -1; } msg = (char *)malloc(size); if (!msg) { log("SYSERR: Read error in read_dblock...out of memory."); return -1; } nbytes = myread(desc, msg, size); if (nbytes != size) { #ifdef DEBUG_MAX printf("ERROR: %d bytes out of %d read on desc: %d\n", nbytes, size, desc); perror("hmm:"); #endif return -1; } // else... we read the right size... *blk_ptr = msg; signal(SIGPIPE, func); return nbytes; } // given a pword and name, locate descriptor in game... // if cant find one... send message, then return NULL... dsdata *get_char_desc(char *name, char *pword, char *res) { chdata *ch; BOOL check_password(dsdata *d, char *arg, BOOL main_server); // first get the character with that name... if (!(ch = get_char(name))) { sprintf(res, "Unable to locate %s on main server...", name); return NULL; } if (!ch->desc) { sprintf(res, "%s is linkless on main server...", name); return NULL; } if (!check_password(ch->desc, pword, FALSE)) { sprintf(res, "Incorrect password for %s...", name); return NULL; } return ch->desc; } // set up client descriptor // V2 changes - 2/12/98 -jtrhone // - now, expect login block immediately... validate name and pword... // - if ok, send a text message saying welcome // no longer request read of login_request immediately, now just insert this // descriptor into our prelim client list to select on it // when select on this desc gets triggered, we'll read login 5/20/98 -jtrhone int new_client_descriptor(dsdata *newd, int desc) { if (newd->cdesc > 0) close_client_socket(newd); newd->cdesc = desc; /* prepend to our list of client descriptors */ newd->next_client = cdesc_list; cdesc_list = newd; if (newd->character) { sprintf(buf, "SYSUPD: %s established RoAClient connection.", GET_NAME(newd->character)); mudlog(buf, BUG, MAX(LEV_IMM, GET_INVIS_LEV(newd->character)), TRUE); } send_welcome_to_client(newd); send_policies_to_client(newd); send_soundfile_to_client(newd, "hero.mid"); return 0; } // just insert this descriptor into our list of prelim clients then move on // 5/20/98 -jtrhone int new_prelim_socket(int s) { int desc,i; struct sockaddr_un peer; cldesc *c; /* accept the new connection */ i = sizeof(peer); if ((desc = accept(s, (struct sockaddr *) &peer, &i)) < 0) { perror("prelim client accept"); return -1; } nonblock(desc); // now, allocate a prelim struct and insert it CREATE(c, cldesc, 1); c->desc = desc; /* prepend to our list of client descriptors */ c->next = prelim_list; prelim_list = c; #ifdef DEBUG mudlog("SYSUPD: Preliminary client connection accepted.", BUG, LEV_IMM, TRUE); #endif return 0; } /* something happened, close the client descriptor and yank it */ void close_client_socket(dsdata *d) { dsdata *temp; close(d->cdesc); d->cdesc = -1; // if they have pending clpids, kill em all... 3/1/98 -jtrhone wax_descriptor_clpids(d); if (d->character) { sprintf(buf, "SYSUPD: %s lost client connection.",GET_NAME(d->character)); mudlog(buf, BUG, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } else mudlog("Losing client connection without char.", CMP, LEV_IMM, TRUE); REMOVE_FROM_LIST(d, cdesc_list, next_client); } /* something happened, close the prelim client descriptor and yank it */ void close_prelim_socket(cldesc *c) { cldesc *temp; close(c->desc); c->desc = -1; REMOVE_FROM_LIST(c, prelim_list, next); FREENULL(c); } // sit and wait for a socket to become available from another process // this uses a file for a locking mechanism... 12/3/97 -jtrhone void request_socket_lock(int sok) { FILE *fp; struct stat s; char fname[MAX_INPUT_LENGTH]; sprintf(fname, "misc/%d.soklock", sok); // keep trying until its not there while (!stat(fname, &s)) { #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: Server %d waiting for socket unlock: %d.",getpid(), sok); log(buf); #endif sleep(1); } // OK, make our own lock... if (!(fp = fopen(fname, "w"))) log("REQUEST SOCKET LOCK FAILED TO WRITE FILE."); else fclose(fp); #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: Server %d locked socket: %d.",getpid(), sok); log(buf); #endif } // simply removes the file representing the socket lock void unlock_socket(int sok) { char fname[MAX_INPUT_LENGTH]; sprintf(fname, "misc/%d.soklock", sok); unlink(fname); #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: Server %d unlocked socket: %d.",getpid(), sok); log(buf); #endif } // nice little send package... 2/12/97 -jtrhone int send_message(dblock *h, dsdata *d, BOOL block) { int retval = 1; int sok = d->cdesc; // sleep until we can write to it... if (block) request_socket_lock(sok); // ok... our turn retval = send_dblock(h, d); // let em know it's free... if (block) unlock_socket(sok); return retval; } // a preliminary descriptor has tripped select and caused us to // attempt a read, only those client which havent logged in yet // will be allowed here, so we expect a LOGIN_REQUEST // 5/20/98 -jtrhone int process_prelim_client(cldesc *c) { cldesc *temp; dsdata *newd, tmpd; char res[MAX_INPUT_LENGTH]; dblock *blk; // ok, lets read and validate first... if (read_dblock((void*)&blk, c->desc) <= 0) { close_prelim_socket(c); return -1; } // if we have a stream, offset the pointer correctly... if (blk->streamlen > 0) blk->stream = (void *) &blk[1]; // ok, we have allocated and read a data block... now figure what to do if (blk->type != LOGIN_REQUEST) { sprintf(buf, "SYSERR: Unknown rclient connection request, disconnecting."); mudlog(buf, BRF, LEV_IMM, TRUE); close_prelim_socket(c); wax_dblock(blk); return -1; } // must validate username and password... player MUST be online on the main server if (!(newd = get_char_desc(blk->str1, blk->str2, res))) { sprintf(buf, "SYSUPD: Failed rclient login attempt (%s).", blk->str1); mudlog(buf, BUG, LEV_IMM, TRUE); wax_dblock(blk); // notify client that login failed... tmpd.cdesc = c->desc; send_msg_to_client(res, &tmpd); close_prelim_socket(c); return -1; } // free it baba... wax_dblock(blk); // now, append it to list of client connections new_client_descriptor(newd, c->desc); // now, since it's in the client list, remove it from prelim list and free // we dont wanna close_prelim_socket cause we don't want the desc closed REMOVE_FROM_LIST(c, prelim_list, next); FREENULL(c); // update everybody's who list 6/15/98 -jtrhone update_client_who(); if (D_CHECK(newd) && HAS_CLIENT(newd) && PLR2_FLAGGED(newd->character, PLR2_CLIENTEQ)) update_client_eq(newd); if (D_CHECK(newd) && HAS_CLIENT(newd) && PLR2_FLAGGED(newd->character, PLR2_CLIENTINV)) update_client_inv(newd); if (D_CHECK(newd) && HAS_CLIENT(newd) && PLR2_FLAGGED(newd->character, PLR2_CLIENTCHAN)) update_client_chan(newd); return 0; } // for all messages passing from clients to server... relatively short // for now... mainly soung gos/nogos 2/20/98 -jtrhone int process_client_input(dsdata *d) { dblock *rblk = NULL; static char txt[85000]; extern void string_add(dsdata *d, char *str, BOOL term); // lock it until we finish reading...we should get immediate lock request_socket_lock(d->cdesc); // await the response... if (read_dblock((void*)&rblk, d->cdesc) < 0) { unlock_socket(d->cdesc); close_client_socket(d); wax_dblock(rblk); if (d->rerouted) { d->rerouted = FALSE; strcpy(txt, "SYSERR: Linkloss forced premature exit."); string_add(d, txt, TRUE); } return FALSE; } // we got what we want... unlock it unlock_socket(d->cdesc); // if we have a stream, offset the pointer correctly... if (rblk->streamlen > 0) rblk->stream = (void *) &rblk[1]; switch (rblk->type) { case SOUND_GO: #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: GO acknowledged. Sending %s.",rblk->str1); log(buf); #endif send_sound_data(d, rblk->str1); break; case SOUND_NOGO: #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: NOGO acknowledged. Not sending %s.",rblk->str1); log(buf); #endif break; case EDIT_REPLY: // 2/25/98 -jtrhone can send us back stuff now #ifdef DEBUG_MAX log("SYSUPD: EDIT_RESPONSE acknowledged."); #endif // 3/1/98 -jtrhone, if we get a reply and we dont think they are out // ignore the reply if (!d->rerouted) { log("SYSUPD: Non rerouted EDIT_RESPONSE ignored..."); break; } else // guess what... they back 2/26/98 -jtrhone d->rerouted = FALSE; if (rblk->stream) strcpy(txt, rblk->stream); else strcpy(txt, ""); string_add(d, txt, TRUE); break; case COMMAND: // 2/27/98 -jtrhone can send us commands #ifdef DEBUG_MAX log("SYSUPD: COMMAND acknowledged."); #endif // if everything jives, execute the command... if (*rblk->str1 && d->character) { strcpy(txt, rblk->str1); command_interpreter(d->character, txt); if (VT100(d->character)) updatescr(d->character); } break; case HELPINDEX_REQUEST: #ifdef DEBUG_MAX log("SYSUPD: HELPINDEX_REQ acknowledged. Sending help index."); #endif send_client_help_index(d); break; case HELPFILE_REQUEST: #ifdef DEBUG_MAX sprintf(buf, "SYSUPD: HELPFILE_REQ acknowledged. Sending %s.",rblk->str1); log(buf); #endif send_client_help_file(d, rblk->str1); break; default: mudlog("SYSERR: Unknown client request, ignoring.", BRF, LEV_IMM, TRUE); break; } wax_dblock(rblk); return TRUE; } // doesn't do much yet, client output isn't buffered in a coded queue ... int process_client_output(dsdata *d) { return 1; } // send latest stats to client (hit mana move)... void send_client_hitmanamove(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; chdata *ch = d->character; blk.type = HITMANAMOVE; blk.version = 2; // this string will have helpfilename length X max # of files sprintf(blk.str1, "%d/%d %d/%d %d/%d", GET_HIT(ch), GET_MAX_HIT(ch), GET_MANA(ch), GET_MAX_MANA(ch), GET_MOVE(ch), GET_MAX_MOVE(ch)); strcpy(blk.str2, ""); blk.stream = NULL; blk.streamlen = 0; if (D_CHECK(d) && HAS_CLIENT(d)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client helpfilesend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } } // send hitmanamove to any clients who are fighting and want it 8/3/98 -jtrhone // and every tick regardless 8/4/98 -jtrhone void update_client_stats(BOOL combat) { dsdata *d; Descriptors(d) if (D_CHECK(d) && HAS_CLIENT(d) && (!combat || FIGHTING(d->character)) && PLR2_FLAGGED(d->character, PLR2_CLIENTSTAT)) send_client_hitmanamove(d); } // send help index to client... void send_client_help_index(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; blk.type = HELPINDEX_REPLY; blk.version = 2; // this string will have helpfilename length X max # of files sprintf(blk.str1, "%d %d", TITLE_LENGTH, MAX_HELPFILES); strcpy(blk.str2, ""); blk.streamlen = sizeof(struct roa_help_data) * MAX_HELPFILES; CREATE(blk.stream, char, blk.streamlen); memcpy(blk.stream, roa_help, blk.streamlen); if (D_CHECK(d) && HAS_CLIENT(d)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client helpfilesend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } // send a particular help file to client... void send_client_help_file(dsdata *d, char *name) { extern void add_underscores(char *str); #ifdef CLIENT_FORK int pid, bitv = 0; #endif char fname[512]; dblock blk; add_underscores(name); sprintf(fname, "help/%s", name); if (file_to_string(fname, buf2) < 0) { mudlog("SYSERR: Client helpfilesend file_to_string() error.", BRF, LEV_IMM, TRUE); return; } killp(buf2); blk.type = HELPFILE_REPLY; blk.version = 2; strcpy(blk.str1, name); strcpy(blk.str2, ""); blk.stream = (void *)winkillr(buf2); blk.streamlen = strlen((char *)blk.stream)+1; if (D_CHECK(d) && HAS_CLIENT(d)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client helpfilesend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } // send a welcome text stream to client upon connection void send_welcome_to_client(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; // must FLUSH EVERYTHING!! fflush(NULL); switch ((pid = fork())) { case -1: mudlog("SYSERR: Client welcome fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif sprintf(buf, "Welcome, %s. RoAServer (%s) acknowledges your connection.", d->character ? GET_NAME(d->character) : "<Unknown>", RoA_version); send_msg_to_client(buf, d); #ifdef CLIENT_FORK exit(1); default: // parent, just to keep track of ALL forks... record this as well SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, NULL); return ; } #endif } /* give client local copy of latest policies */ void send_policies_to_client(dsdata *d) { extern char *policies; #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; strcpy(buf, policies); killr(buf); // memory must be allocated by parent 12/5/97 -jtrhone blk.type = TEXT_FILE; blk.version = 2; strcpy(blk.str1, "policies"); strcpy(blk.str2, ""); blk.streamlen = strlen(buf) + 1; blk.stream = (char *)STR_DUP(buf); // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client policies fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... 12/5/97 -jtrhone SET_BIT(bitv, CL_POLICIES); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #else FREENULL(blk.stream); #endif } void lgoss_to_buf(char *buf) { int i; strcpy(buf, "Latest channel activity:\n\r"); for (i=GOSSIP_PAGE_LENGTH-1; i >= 0; i--) if (*latest_gossip[i].name) sprintf(buf+strlen(buf), "%s %s\n\r", latest_gossip[i].name, latest_gossip[i].said); } // send channel update to one client... void update_client_chan(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; lgoss_to_buf(buf2); killp(buf2); blk.type = CHANNEL_TEXT; blk.version = 2; strcpy(blk.str1, ""); strcpy(blk.str2, ""); blk.stream = (void *)winkillr(buf2); blk.streamlen = strlen((char *)blk.stream)+1; if (D_CHECK(d) && HAS_CLIENT(d) && PLR2_FLAGGED(d->character, PLR2_CLIENTCHAN)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client eqsend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } // send channel update to all clients... void update_allclient_chan(void) { dsdata *d = descriptor_list; for ( ; d; d=d->next) if (D_CHECK(d) && HAS_CLIENT(d) && PLR2_FLAGGED(d->character, PLR2_CLIENTCHAN)) update_client_chan(d); } // send latest who info to clients 6/15/98 -jtrhone void update_client_who(void) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; dsdata *d; who_to_buf(buf2); killp(buf2); blk.type = WHO_LIST; blk.version = 2; strcpy(blk.str1, ""); strcpy(blk.str2, ""); blk.stream = (void *)winkillr(buf2); blk.streamlen = strlen((char *)blk.stream)+1; for (d = descriptor_list; d; d=d->next) if (D_CHECK(d) && HAS_CLIENT(d) && PLR2_FLAGGED(d->character, PLR2_CLIENTWHO)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client whosend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } void inv_to_buf(chdata *ch, char *buf) { obdata *o; BOOL found = FALSE; strcpy(buf, "You are carrying:\n\r"); for (o=ch->carrying; o; o=o->next_content) if (CAN_SEE_OBJ(ch, o)) { sprintf(buf+strlen(buf), " %s\n\r", o->shdesc); found = TRUE; } if (!found) strcat(buf, " Nothing.\n\r"); else { int ic = check_num(ch); sprintf(buf+strlen(buf),"Total of %d items (weight: %d, max: %d).\n\r", ic, IS_CARRYING_W(ch), CAN_CARRY_W(ch)); if (ic > MAX_RENT_S) sprintf(buf+strlen(buf),"WARNING! You have exceeded the MAX rent limit by %d items.\n\r", (ic - MAX_RENT_S)); } } // stick chars eq list into a buffer void eq_to_buf(chdata *ch, char *buf) { int j; BOOL found; char tbuf[MAX_INPUT_LENGTH]; char cstr[MAX_INPUT_LENGTH]; strcpy(buf, "You are using:\n\r"); for (found = j = 0; j < MAX_WEAR; j++) { if (EQ(ch, j)) { if (WV_FLAGS(EQ(ch, j))) sprintbit(WV_FLAGS(EQ(ch,j)), wv_bits, tbuf); else strcpy(tbuf, ""); sprintf(cstr, "%s %s%s%s", wv_bits[j], *tbuf ? "(" : "" , tbuf, *tbuf ? ")" : "" ); if (CAN_SEE_OBJ(ch, EQ(ch, j))) sprintf(buf+strlen(buf), "%%B%-35s%%0: %s\n\r", cstr, EQ(ch, j)->shdesc); else sprintf(buf+strlen(buf), "%%B%-35s%%0: %%4Something...%%0\n\r", cstr); found = TRUE; } } if (!found) strcat(buf, " Nothing.\n\r"); } // send updated eq listing to client void update_client_eq(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; eq_to_buf(d->character, buf2); killp(buf2); blk.type = EQ_LIST; blk.version = 2; strcpy(blk.str1, ""); strcpy(blk.str2, ""); blk.stream = (void *)winkillr(buf2); blk.streamlen = strlen((char *)blk.stream)+1; if (D_CHECK(d) && HAS_CLIENT(d) && PLR2_FLAGGED(d->character, PLR2_CLIENTEQ)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client eqsend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } void update_client_inv(dsdata *d) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; inv_to_buf(d->character, buf2); killp(buf2); blk.type = INV_LIST; blk.version = 2; strcpy(blk.str1, ""); strcpy(blk.str2, ""); blk.stream = (void *)winkillr(buf2); blk.streamlen = strlen((char *)blk.stream)+1; if (D_CHECK(d) && HAS_CLIENT(d) && PLR2_FLAGGED(d->character, PLR2_CLIENTEQ)) { // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client eqsend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #endif } // free that which we winkillrdmallocedthingie FREENULL(blk.stream); } // send date and time over to client (every tick) void send_time_to_client(dsdata *d, char *str1, char *str2) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; blk.type = MUDTERM_INFO_MSG; blk.version = 2; strcpy(blk.str1, str1); strcpy(blk.str2, str2); blk.streamlen = 0; blk.stream = NULL; // must FLUSH EVERYTHING!! fflush(NULL); #ifdef CLIENT_FORK switch ((pid = fork())) { case -1: mudlog("SYSERR: Client timesend fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... SET_BIT(bitv, CL_TEXTMSG); insert_clpid_entry(pid, bitv, d, blk.stream); return; } #else FREENULL(blk.stream); #endif } // go thru descriptors and update the times of clients... void update_client_times(void) { extern void fill_time_buf(char *txt1, char *txt2); dsdata *d; char str1[BLOCKSTRLENGTH]; char str2[BLOCKSTRLENGTH]; fill_time_buf(str1, str2); killp(str1); killp(str2); for (d = descriptor_list; d; d=d->next) if (D_CHECK(d) && HAS_CLIENT(d)) send_time_to_client(d, str1, str2); } // no longer await the response here... // client will send us a response, we receive in process_client_input() void client_sound_query(dsdata *d, char *sname) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; blk.version = 2; blk.type = SOUND_QUERY; strcpy(blk.str1, sname); strcpy(blk.str2, ""); blk.streamlen = 0; blk.stream = NULL; #ifdef CLIENT_FORK // must FLUSH EVERYTHING!! fflush(NULL); switch ((pid = fork())) { case -1: mudlog("SYSERR: Client sound query fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif #ifdef DEBUG_MAX log("Sending soundquery..."); #endif // lock it, send query, unlock... send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent SET_BIT(bitv, CL_SOUNDQUERY); insert_clpid_entry(pid, bitv, d, blk.stream); return ; } #endif } /* given a sound filename, send it after reading it */ void send_soundfile_to_client(dsdata *d, char *sname) { char fname[MAX_INPUT_LENGTH]; struct stat stats; // add check for mute flag on char 6/15/98 -jtrhone // add check for ppl in OLC (cause cliedit blocks on client side) 6/30/98 -jtrhone if (!D_CHECK(d) || PLR2_FLAGGED(d->character, PLR2_CLIENTMUTE) || PLR_FLAGGED(d->character, PLR_BUILDING)) return; /* first check if it's there... */ sprintf(fname, "sound/%s", sname); if (stat(fname, &stats) < 0) { sprintf(buf, "SYSERR: %s not found for client send.",fname); mudlog(buf, BRF, LEV_IMM, TRUE); return; } // now... just send the query to client... client_sound_query(d, sname); } // this ACTUALLY sends the large sound file...checking again if it's there... void send_sound_data(dsdata *d, char *sname) { dblock blk; int length; char fname[MAX_INPUT_LENGTH]; struct stat stats; FILE *fp; #ifdef CLIENT_FORK int pid, bitv = 0; #endif /* first check if it's there...again */ sprintf(fname, "sound/%s", sname); if (stat(fname, &stats) < 0) { sprintf(buf, "SYSERR: %s not found for client send.",fname); mudlog(buf, BRF, LEV_IMM, TRUE); return; } else length = stats.st_size; // setup structure and allocate memory in parent first 12/5/97 -jtrhone CREATE(blk.stream, char, length); /* read blk.stream */ if (!(fp = fopen(fname, "rb"))) { sprintf(buf, "SYSERR: Unable to open %s for read.",fname); mudlog(buf, BRF, LEV_IMM, TRUE); return; } fread(blk.stream, 1, length, fp); fclose(fp); blk.type = SOUND_FILE; blk.version = 2; strcpy(blk.str1, sname); strcpy(blk.str2, ""); blk.streamlen = length; #ifdef CLIENT_FORK // must FLUSH EVERYTHING!! fflush(NULL); switch ((pid = fork())) { case -1: mudlog("SYSERR: Client soundfile fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif #ifdef DEBUG_MAX log("Sending soundfile..."); #endif send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent inserts pid structure into list... 12/5/97 -jtrhone SET_BIT(bitv, CL_SOUNDFILE); insert_clpid_entry(pid, bitv, d, blk.stream); break; } #else FREENULL(blk.stream); #endif } // send an edit request block out to client... void client_edit(dsdata *d, char *prompt, char *txt) { #ifdef CLIENT_FORK int pid, bitv = 0; #endif dblock blk; static char bigbuf[MAX_STRING_LENGTH*2]; blk.version = 2; blk.type = EDIT_REQUEST; strcpy(blk.str1, prompt); strcpy(blk.str2, ""); if (txt && *txt) { // first, convert it... strcpy(bigbuf, txt); blk.stream = (void *)winkillr(bigbuf); blk.streamlen = strlen((char *)blk.stream)+1; } else { blk.streamlen = 0; blk.stream = NULL; } #ifdef CLIENT_FORK // must FLUSH EVERYTHING!! fflush(NULL); switch ((pid = fork())) { case -1: mudlog("SYSERR: Client edit request fork() error.", BRF, LEV_IMM, TRUE); return; case 0: // CHILD!!! default_sigs(); #endif #ifdef DEBUG_MAX log("Sending edit request..."); #endif // lock it, send query, unlock... send_message(&blk, d, TRUE); #ifdef CLIENT_FORK exit(1); default: // parent if (blk.stream) { SET_BIT(bitv, CL_EDITREQUEST); insert_clpid_entry(pid, bitv, d, blk.stream); } #endif // so we don't take input... d->rerouted = TRUE; return; #ifdef CLIENT_FORK } #endif }