diff -uN Rom24/src/Makefile Rom24-mccp/src/Makefile --- Rom24/src/Makefile Thu Aug 24 07:52:39 1995 +++ Rom24-mccp/src/Makefile Wed Mar 10 12:35:46 1999 @@ -2,13 +2,13 @@ PROF = -O -g NOCRYPT = C_FLAGS = -Wall $(PROF) $(NOCRYPT) -L_FLAGS = $(PROF) +L_FLAGS = $(PROF) -lz O_FILES = act_comm.o act_enter.o act_info.o act_move.o act_obj.o act_wiz.o \ alias.o ban.o comm.o const.o db.o db2.o effects.o fight.o flags.o \ handler.o healer.o interp.o note.o lookup.o magic.o magic2.o \ music.o recycle.o save.o scan.o skills.o special.o tables.o \ - update.o + update.o mccp.o rom: $(O_FILES) rm -f rom diff -uN Rom24/src/README.Rom24-mccp Rom24-mccp/src/README.Rom24-mccp --- Rom24/src/README.Rom24-mccp Thu Jan 1 12:00:00 1970 +++ Rom24-mccp/src/README.Rom24-mccp Wed Mar 10 12:37:33 1999 @@ -0,0 +1,60 @@ +Mud Client Compression Protocol support for Rom 2.4b4 + +Oliver Jowett , 990310 + + +This patch (against Rom 2.4b4) 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 Rom24/src/comm.c Rom24-mccp/src/comm.c --- Rom24/src/comm.c Sat Jul 20 11:57:51 1996 +++ Rom24-mccp/src/comm.c Wed Mar 10 12:35:46 1999 @@ -109,6 +109,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) @@ -117,9 +121,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 @@ -322,7 +332,8 @@ int init_socket args( ( int port ) ); void init_descriptor args( ( int control ) ); 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 @@ -789,10 +800,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->connected == CON_PLAYING) save_char_obj( d->character ); @@ -930,7 +949,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); @@ -945,6 +964,10 @@ /* * Send the greeting. */ + + /* mccp: tell the client we support compression */ + write_to_buffer( dnew, compress_will, 0 ); + { extern char * help_greeting; if ( help_greeting[0] == '.' ) @@ -1019,6 +1042,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) @@ -1043,7 +1072,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; } @@ -1128,7 +1157,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++ ) @@ -1145,6 +1174,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); + } + } } /* @@ -1298,7 +1337,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; @@ -1511,7 +1550,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; @@ -1535,8 +1574,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 Rom24/src/interp.c Rom24-mccp/src/interp.c --- Rom24/src/interp.c Thu Jul 18 23:44:10 1996 +++ Rom24-mccp/src/interp.c Wed Mar 10 12:35:46 1999 @@ -159,6 +159,7 @@ /* { "channels", do_channels, POS_DEAD, 0, LOG_NORMAL, 1 }, */ { "combine", do_combine, POS_DEAD, 0, LOG_NORMAL, 1 }, { "compact", do_compact, POS_DEAD, 0, LOG_NORMAL, 1 }, + { "compress", do_compress, POS_DEAD, 0, LOG_NORMAL, 1 }, { "description", do_description, POS_DEAD, 0, LOG_NORMAL, 1 }, { "delet", do_delet, POS_DEAD, 0, LOG_ALWAYS, 0 }, { "delete", do_delete, POS_STANDING, 0, LOG_ALWAYS, 1 }, diff -uN Rom24/src/interp.h Rom24-mccp/src/interp.h --- Rom24/src/interp.h Thu Jul 18 23:44:49 1996 +++ Rom24-mccp/src/interp.h Wed Mar 10 12:35:46 1999 @@ -99,6 +99,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_count ); DECLARE_DO_FUN( do_credits ); diff -uN Rom24/src/mccp.c Rom24-mccp/src/mccp.c --- Rom24/src/mccp.c Thu Jan 1 12:00:00 1970 +++ Rom24-mccp/src/mccp.c Wed Mar 10 12:40:12 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 Rom24/src/merc.h Rom24-mccp/src/merc.h --- Rom24/src/merc.h Thu Jul 18 23:44:58 1996 +++ Rom24-mccp/src/merc.h Wed Mar 10 12:35:46 1999 @@ -72,7 +72,13 @@ typedef unsigned char bool; #endif +/* mccp: support bits */ +#include + +#define TELOPT_COMPRESS 85 + +#define COMPRESS_BUF_SIZE 16384 /* * Structure types. @@ -267,6 +273,10 @@ int outtop; char * showstr_head; char * showstr_point; + + /* mccp: support data */ + z_stream * out_compress; + unsigned char * out_compress_buf; }; @@ -2316,6 +2326,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); + #undef CD #undef MID