diff -uN src/Makefile mccp-src/Makefile --- src/Makefile Mon Aug 25 12:51:09 1997 +++ mccp-src/Makefile Wed Mar 10 12:15:05 1999 @@ -2,13 +2,13 @@ PROF = -O -g NOCRYPT = C_FLAGS = -Wall $(PROF) $(NOCRYPT) -L_FLAGS = $(PROF) +L_FLAGS = $(PROF) -lz -lcrypt ROT_FILES = act_comm.o act_enter.o act_info.o act_move.o act_obj.o act_wiz.o \ alias.o ban.o clans.o comm.o const.o curse.o db.o db2.o effects.o \ fight.o finger.o flags.o forget.o handler.o healer.o interp.o note.o \ lookup.o magic.o magic2.o music.o recycle.o repent.o save.o scan.o \ - sign.o skills.o special.o tables.o update.o wizlist.o + sign.o skills.o special.o tables.o update.o wizlist.o mccp.o all:: rot diff -uN src/README.Rot14.mccp mccp-src/README.Rot14.mccp --- src/README.Rot14.mccp Wed Dec 31 18:00:00 1969 +++ mccp-src/README.Rot14.mccp Wed Mar 10 14:08:19 1999 @@ -0,0 +1,64 @@ +Mud Client Compression Protocol support for ROT 1.4 + +Oliver Jowett , 990310 +Dominic Eidson , 990310 + +This patch (against Rot 1.4) adds support for compression of output to +clients that support mccp. The two main benefits of mccp are: + +1. lower bandwidth use by the mud. +2. clients (i.e. players) get large responses (such as, for example, combat + spam) from the mud at a higher rate - the mud will "feel" faster, despite + the fact that the actual round-trip times are unchanged. + +With a suitable client, mccp is entirely transparent to the user - it +automatically enables itself when supported by both the server and client. + +For more details on the protocol, and a list of clients and servers currently +supporting it, see . If +you add mccp support to your mud or a mud client, mail me and I'll add it to +the list. + +Improvements and bugfixes are welcome - unfortunately I don't have the time +(or desire) to personally work on a large number of patches for many +different codebases. + +To use this patch, you will need a copy of zlib installed - see +. Many systems will already have +a version installed. + +Copyright/usage: see mccp.c - in essence, use as you wish, as long as +copyright notices are retained. + + +CAVEATS + +. This patch was thrown together in about an hour, and I last looked at the + Rom source code about 12 months ago. YMMV. + +. Testing has only been done over local loopback. Unforseen bugs due to timing + or network error handling etc. are possible. + + +. The simple-but-stupid approach has been taken. Much of the network I/O + code could do with a rewrite, but I've left it mostly as-is and only changed + those parts that are necessary to implement mccp. +. Telnet option negotiation and handling of IAC sequences is very naive (but, + it's completely nonexistant in the base code..) +. Telnet option negotiation is not done continuously, but only when the client + enters a complete line. + +. The write_to_descriptor prototype has changed! If you call it anywhere + other than where the standard code calls it, you will need to: + - Update the prototype to the new version (see comm.cc) if you've copied + it to other files. + - Pass the entire DESCRIPTOR_DATA * of the descriptor, instead of just the + fd - i.e. write_to_descriptor(d, txt, len) instead of + write_to_descriptor(d->descriptor, txt, len). + +. If you must write to a descriptor by fd not DESCRIPTOR_DATA *, then + write_to_descriptor_2 is available -- BUT it will not compress output + data! If you call this function on a connection that has compression + enabled, compression will FAIL and the client will disconnect! diff -uN src/comm.c mccp-src/comm.c --- src/comm.c Mon Aug 25 12:51:09 1997 +++ mccp-src/comm.c Wed Mar 10 12:35:21 1999 @@ -62,7 +62,7 @@ #include #include #include - +#include #include "merc.h" #include "recycle.h" #include "tables.h" @@ -116,6 +116,10 @@ const char echo_off_str [] = { '\0' }; const char echo_on_str [] = { '\0' }; const char go_ahead_str [] = { '\0' }; +const char compress_will [] = { '\0' }; +const char compress_do [] = { '\0' }; +const char compress_dont [] = { '\0' }; +const char compress_start [] = { '\0' }; #endif #if defined(unix) @@ -124,9 +128,15 @@ #include #include #include "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' }; + +/* mccp: compression negotiation strings */ +const char compress_will [] = { IAC, WILL, TELOPT_COMPRESS, '\0' }; +const char compress_do [] = { IAC, DO, TELOPT_COMPRESS, '\0' }; +const char compress_dont [] = { IAC, DONT, TELOPT_COMPRESS, '\0' }; #endif @@ -171,21 +181,6 @@ #include #endif -#if defined(linux) -int accept args( ( int s, struct sockaddr *addr, int *addrlen ) ); -int bind args( ( int s, struct sockaddr *name, int namelen ) ); -int close args( ( int fd ) ); -int getpeername args( ( int s, struct sockaddr *name, int *namelen ) ); -int getsockname args( ( int s, struct sockaddr *name, int *namelen ) ); -int gettimeofday args( ( struct timeval *tp, struct timezone *tzp ) ); -int listen args( ( int s, int backlog ) ); -int read args( ( int fd, char *buf, int nbyte ) ); -int select args( ( int width, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout ) ); -int socket args( ( int domain, int type, int protocol ) ); -int write args( ( int fd, char *buf, int nbyte ) ); -#endif - #if defined(macintosh) #include #include @@ -326,7 +321,8 @@ void init_descriptor args( ( int control ) ); void init_descriptor_www args( ( int wwwcontrol ) ); bool read_from_descriptor args( ( DESCRIPTOR_DATA *d ) ); -bool write_to_descriptor args( ( int desc, char *txt, int length ) ); +bool write_to_descriptor args( ( DESCRIPTOR_DATA *d, char *txt, int length ) ); +bool write_to_descriptor_2 args( ( int desc, char *txt, int length ) ); #endif @@ -882,10 +878,18 @@ { d_next = d->next; - if ( ( d->fcommand || d->outtop > 0 ) + if ( ( d->fcommand || d->outtop > 0 || d->out_compress ) && FD_ISSET(d->descriptor, &out_set) ) - { - if ( !process_output( d, TRUE ) ) + { + bool ok = TRUE; + + if ( d->fcommand || d->outtop > 0 ) + ok = process_output( d, TRUE ); + + if (ok && d->out_compress) + ok = processCompressed(d); + + if (!ok) { if ( d->character != NULL && d->character->level > 1) save_char_obj( d->character ); @@ -1023,7 +1027,7 @@ */ if ( check_ban(dnew->host,BAN_ALL)) { - write_to_descriptor( desc, + write_to_descriptor_2( desc, "Your site has been banned from this mud.\n\r", 0 ); close( desc ); free_descriptor(dnew); @@ -1038,6 +1042,10 @@ /* * Send the greeting. */ + + /* mccp: tell the client we support compression */ + write_to_buffer( dnew, compress_will, 0 ); + { extern char * help_greetinga; extern char * help_greetingb; @@ -1345,6 +1353,12 @@ bug( "Close_socket: dclose not found.", 0 ); } + if (dclose->out_compress) { + deflateEnd(dclose->out_compress); + free_mem(dclose->out_compress_buf, COMPRESS_BUF_SIZE); + free_mem(dclose->out_compress, sizeof(z_stream)); + } + close( dclose->descriptor ); free_descriptor(dclose); #if defined(MSDOS) || defined(macintosh) @@ -1369,7 +1383,7 @@ { sprintf( log_buf, "%s input overflow!", d->host ); log_string( log_buf ); - write_to_descriptor( d->descriptor, + write_to_descriptor( d, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0 ); return FALSE; } @@ -1454,7 +1468,7 @@ { if ( k >= MAX_INPUT_LENGTH - 2 ) { - write_to_descriptor( d->descriptor, "Line too long.\n\r", 0 ); + write_to_descriptor( d, "Line too long.\n\r", 0 ); /* skip the rest of the line */ for ( ; d->inbuf[i] != '\0'; i++ ) @@ -1471,6 +1485,16 @@ --k; else if ( isascii(d->inbuf[i]) && isprint(d->inbuf[i]) ) d->incomm[k++] = d->inbuf[i]; + else if (d->inbuf[i] == (signed char)IAC) { + if (!memcmp(&d->inbuf[i], compress_do, strlen(compress_do))) { + i += strlen(compress_do) - 1; + compressStart(d); + } + else if (!memcmp(&d->inbuf[i], compress_dont, strlen(compress_dont))) { + i += strlen(compress_dont) - 1; + compressEnd(d); + } + } } /* @@ -1645,7 +1669,7 @@ /* * OS-dependent output. */ - if ( !write_to_descriptor( d->descriptor, d->outbuf, d->outtop ) ) + if ( !write_to_descriptor( d, d->outbuf, d->outtop ) ) { d->outtop = 0; return FALSE; @@ -1847,7 +1871,7 @@ * 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 ) +bool write_to_descriptor_2( int desc, char *txt, int length ) { int iStart; int nWrite; @@ -1871,8 +1895,15 @@ return TRUE; } - - +/* mccp: write_to_descriptor wrapper */ +bool write_to_descriptor(DESCRIPTOR_DATA *d, char *txt, int length) +{ + if (d->out_compress) + return writeCompressed(d, txt, length); + else + return write_to_descriptor_2(d->descriptor, txt, length); +} + /* * Deal with sockets that haven't logged in yet. */ diff -uN src/db.c mccp-src/db.c --- src/db.c Mon Aug 25 12:51:09 1997 +++ mccp-src/db.c Wed Mar 10 12:35:40 1999 @@ -56,14 +56,6 @@ extern int _filbuf args( (FILE *) ); #endif -#if !defined(OLD_RAND) -long random(); -void srandom(unsigned int); -int getpid(); -time_t time(time_t *tloc); -#endif - - /* externals for counting purposes */ extern OBJ_DATA *obj_free; extern CHAR_DATA *char_free; diff -uN src/interp.c mccp-src/interp.c --- src/interp.c Mon Aug 25 12:51:09 1997 +++ mccp-src/interp.c Wed Mar 10 12:13:27 1999 @@ -174,6 +174,7 @@ { "color", do_colour, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, { "combine", do_combine, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, { "compact", do_compact, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, + { "compress", do_compress, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, { "description", do_description, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, { "long", do_long, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, { "nofollow", do_nofollow, POS_DEAD, 0, 1, LOG_NORMAL, 1 }, diff -uN src/interp.h mccp-src/interp.h --- src/interp.h Mon Aug 25 12:51:09 1997 +++ mccp-src/interp.h Wed Mar 10 12:10:27 1999 @@ -117,6 +117,7 @@ DECLARE_DO_FUN( do_combine ); DECLARE_DO_FUN( do_compact ); DECLARE_DO_FUN( do_compare ); +DECLARE_DO_FUN( do_compress ); DECLARE_DO_FUN( do_consider ); DECLARE_DO_FUN( do_corner ); DECLARE_DO_FUN( do_count ); diff -uN src/mccp.c mccp-src/mccp.c --- src/mccp.c Wed Dec 31 18:00:00 1969 +++ mccp-src/mccp.c Wed Mar 10 12:10:27 1999 @@ -0,0 +1,206 @@ +/* + * mccp.c - support functions for mccp (the Mud Client Compression Protocol) + * + * see http://homepages.ihug.co.nz/~icecube/compress/ and README.Rom24-mccp + * + * Copyright (c) 1999, Oliver Jowett . + * + * This code may be freely distributed and used if this copyright notice is + * retained intact. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "merc.h" +#include "telnet.h" + +char compress_start [] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' }; + +bool processCompressed(DESCRIPTOR_DATA *desc); +bool write_to_descriptor args( ( DESCRIPTOR_DATA *d, char *txt, int length ) ); + +/* + * Memory management - zlib uses these hooks to allocate and free memory + * it needs + */ + +void *zlib_alloc(void *opaque, unsigned int items, unsigned int size) +{ + return calloc(items, size); +} + +void zlib_free(void *opaque, void *address) +{ + free(address); +} + +/* + * Begin compressing data on `desc' + */ +bool compressStart(DESCRIPTOR_DATA *desc) +{ + z_stream *s; + + if (desc->out_compress) /* already compressing */ + return TRUE; + + /* allocate and init stream, buffer */ + s = (z_stream *)alloc_mem(sizeof(*s)); + desc->out_compress_buf = (unsigned char *)alloc_mem(COMPRESS_BUF_SIZE); + + s->next_in = NULL; + s->avail_in = 0; + + s->next_out = desc->out_compress_buf; + s->avail_out = COMPRESS_BUF_SIZE; + + s->zalloc = zlib_alloc; + s->zfree = zlib_free; + s->opaque = NULL; + + if (deflateInit(s, 9) != Z_OK) { + /* problems with zlib, try to clean up */ + free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE); + free_mem(s, sizeof(z_stream)); + return FALSE; + } + + write_to_descriptor(desc, compress_start, strlen(compress_start)); + + /* now we're compressing */ + desc->out_compress = s; + return TRUE; +} + +/* Cleanly shut down compression on `desc' */ +bool compressEnd(DESCRIPTOR_DATA *desc) +{ + unsigned char dummy[1]; + + if (!desc->out_compress) + return TRUE; + + desc->out_compress->avail_in = 0; + desc->out_compress->next_in = dummy; + + /* No terminating signature is needed - receiver will get Z_STREAM_END */ + + if (deflate(desc->out_compress, Z_FINISH) != Z_STREAM_END) + return FALSE; + + if (!processCompressed(desc)) /* try to send any residual data */ + return FALSE; + + deflateEnd(desc->out_compress); + free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE); + free_mem(desc->out_compress, sizeof(z_stream)); + desc->out_compress = NULL; + desc->out_compress_buf = NULL; + + return TRUE; +} + +/* Try to send any pending compressed-but-not-sent data in `desc' */ +bool processCompressed(DESCRIPTOR_DATA *desc) +{ + int iStart, nBlock, nWrite, len; + + if (!desc->out_compress) + return TRUE; + + /* Try to write out some data.. */ + len = desc->out_compress->next_out - desc->out_compress_buf; + if (len > 0) { + /* we have some data to write */ + + for (iStart = 0; iStart < len; iStart += nWrite) + { + nBlock = UMIN (len - iStart, 4096); + if ((nWrite = write (desc->descriptor, desc->out_compress_buf + iStart, nBlock)) < 0) + { + if (errno == EAGAIN || + errno == ENOSR) + break; + + return FALSE; /* write error */ + } + + if (nWrite <= 0) + break; + } + + if (iStart) { + /* We wrote "iStart" bytes */ + if (iStart < len) + memmove(desc->out_compress_buf, desc->out_compress_buf+iStart, len - iStart); + + desc->out_compress->next_out = desc->out_compress_buf + len - iStart; + } + } + + return TRUE; +} + +/* write_to_descriptor, the compressed case */ +bool writeCompressed(DESCRIPTOR_DATA *desc, char *txt, int length) +{ + z_stream *s = desc->out_compress; + + s->next_in = (unsigned char *)txt; + s->avail_in = length; + + while (s->avail_in) { + s->avail_out = COMPRESS_BUF_SIZE - (s->next_out - desc->out_compress_buf); + + if (s->avail_out) { + int status = deflate(s, Z_SYNC_FLUSH); + + if (status != Z_OK) { + /* Boom */ + return FALSE; + } + } + + /* Try to write out some data.. */ + if (!processCompressed(desc)) + return FALSE; + + /* loop */ + } + + /* Done. */ + return TRUE; +} + +/* User-level compression toggle */ +void do_compress( CHAR_DATA *ch, char *argument ) +{ + if (!ch->desc) { + send_to_char("What descriptor?!\n", ch); + return; + } + + if (!ch->desc->out_compress) { + if (!compressStart(ch->desc)) { + send_to_char("Failed.\n", ch); + return; + } + + send_to_char("Ok, compression enabled.\n", ch); + } else { + if (!compressEnd(ch->desc)) { + send_to_char("Failed.\n", ch); + return; + } + + send_to_char("Ok, compression disabled.\n", ch); + } +} diff -uN src/merc.h mccp-src/merc.h --- src/merc.h Mon Aug 25 12:51:09 1997 +++ mccp-src/merc.h Wed Mar 10 12:33:48 1999 @@ -52,6 +52,10 @@ int unlink(); int system(); +/* mccp: support bits */ +#include +#define TELOPT_COMPRESS 85 +#define COMPRESS_BUF_SIZE 16384 /* @@ -356,6 +360,8 @@ int outtop; char * showstr_head; char * showstr_point; + z_stream * out_compress; /* mccp: support data */ + unsigned char * out_compress_buf; }; @@ -2286,8 +2292,6 @@ char * crypt args( ( const char *key, const char *salt ) ); #endif - - /* * The crypt(3) function is not available on some operating systems. * In particular, the U.S. Government prohibits its export from the @@ -2643,6 +2647,13 @@ void gain_exp args( ( CHAR_DATA *ch, int gain ) ); void gain_condition args( ( CHAR_DATA *ch, int iCond, int value ) ); void update_handler args( ( void ) ); + +/* mccp.c */ +bool compressStart(DESCRIPTOR_DATA *desc); +bool compressEnd(DESCRIPTOR_DATA *desc); +bool processCompressed(DESCRIPTOR_DATA *desc); +bool writeCompressed(DESCRIPTOR_DATA *desc, char *txt, int length); + /* wizlist.c */ void update_wizlist args( ( CHAR_DATA *ch, int level ) );