diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 4eb084d48b..c50f7d8ed3 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,13 @@ -February 21, 2009 (Changes by Graf Zahl) +February 21, 2009 +- Added selective compression of network packets. Interestingly, most packets + don't actually compress all that well, even the ones that aren't too short + to possibly compress. (Maybe make the whole thing one long, never-ending + zlib data stream with Z_SYNC_FLUSH used to chunk things at packet + boundaries? That would probably help the compression ratio, but it would + require changing the way the netcode determines sequence, which would be + a much more invasive change.) + +February 21, 2009 (Changes by Graf Zahl) - Fixed: Heretic's fullscreen HUD crashed when the player had armor without a valid icon. - Fixed: The StrifePlayer was missing a RunHealth setting. diff --git a/src/d_net.cpp b/src/d_net.cpp index 3400e45da1..ce16d48d2a 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -67,35 +67,6 @@ EXTERN_CVAR (Int, autosavecount) //#define SIMULATEERRORS (RAND_MAX/3) #define SIMULATEERRORS 0 -#define NCMD_EXIT 0x80 -#define NCMD_RETRANSMIT 0x40 -#define NCMD_SETUP 0x20 -#define NCMD_MULTI 0x10 // multiple players in this packet -#define NCMD_QUITTERS 0x08 // one or more players just quit (packet server only) - -#define NCMD_XTICS 0x03 // packet contains >2 tics -#define NCMD_2TICS 0x02 // packet contains 2 tics -#define NCMD_1TICS 0x01 // packet contains 1 tic -#define NCMD_0TICS 0x00 // packet contains 0 tics - -// [RH] -// New generic packet structure: -// -// Header: -// One byte with above flags. -// One byte with starttic -// One byte with master's maketic (master -> slave only!) -// If NCMD_RETRANSMIT set, one byte with retransmitfrom -// If NCMD_XTICS set, one byte with number of tics (minus 3, so theoretically up to 258 tics in one packet) -// If NCMD_QUITTERS, one byte with number of players followed by one byte with each player's consolenum -// If NCMD_MULTI, one byte with number of players followed by one byte with each player's consolenum -// - The first player's consolenum is not included in this list, because it always matches the sender -// -// For each tic: -// Two bytes with consistancy check, followed by tic data -// -// Setup packets are different, and are described just before D_ArbitrateNetStart(). - extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos extern char savedescription[SAVESTRINGSIZE]; extern FString savegamefile; diff --git a/src/d_net.h b/src/d_net.h index 43be497102..3ad0bddede 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -48,7 +48,7 @@ // Probably not enough. #define MAX_MSGLEN (BACKUPTICS*10) #else -#define MAX_MSGLEN 1400 +#define MAX_MSGLEN 14000 #endif #define CMD_SEND 1 @@ -144,4 +144,34 @@ extern int nettics[MAXNETNODES]; extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; extern int ticdup; +// [RH] +// New generic packet structure: +// +// Header: +// One byte with following flags. +// One byte with starttic +// One byte with master's maketic (master -> slave only!) +// If NCMD_RETRANSMIT set, one byte with retransmitfrom +// If NCMD_XTICS set, one byte with number of tics (minus 3, so theoretically up to 258 tics in one packet) +// If NCMD_QUITTERS, one byte with number of players followed by one byte with each player's consolenum +// If NCMD_MULTI, one byte with number of players followed by one byte with each player's consolenum +// - The first player's consolenum is not included in this list, because it always matches the sender +// +// For each tic: +// Two bytes with consistancy check, followed by tic data +// +// Setup packets are different, and are described just before D_ArbitrateNetStart(). + +#define NCMD_EXIT 0x80 +#define NCMD_RETRANSMIT 0x40 +#define NCMD_SETUP 0x20 +#define NCMD_MULTI 0x10 // multiple players in this packet +#define NCMD_QUITTERS 0x08 // one or more players just quit (packet server only) +#define NCMD_COMPRESSED 0x04 // remainder of packet is compressed + +#define NCMD_XTICS 0x03 // packet contains >2 tics +#define NCMD_2TICS 0x02 // packet contains 2 tics +#define NCMD_1TICS 0x01 // packet contains 1 tic +#define NCMD_0TICS 0x00 // packet contains 0 tics + #endif diff --git a/src/i_net.cpp b/src/i_net.cpp index caa3a61fb8..7df97a76e3 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -64,7 +64,9 @@ #include "i_net.h" - +// As per http://support.microsoft.com/kb/q192599/ the standard +// size for network buffers is 8k. +#define TRANSMIT_SIZE 8000 /* [Petteri] Get more portable: */ #ifndef __WIN32__ @@ -133,6 +135,8 @@ struct PreGamePacket } machines[MAXNETNODES]; }; +BYTE TransmitBuffer[TRANSMIT_SIZE]; + // // UDPsocket // @@ -191,11 +195,47 @@ void PacketSend (void) { int c; - //printf ("sending %i\n",gametic); - c = sendto (mysocket , (const char*)doomcom.data, doomcom.datalength - ,0,(sockaddr *)&sendaddress[doomcom.remotenode] - ,sizeof(sendaddress[doomcom.remotenode])); + // FIXME: Catch this before we've overflown the buffer. With long chat + // text and lots of backup tics, it could conceivably happen. (Though + // apparently it hasn't yet, which is good.) + if (doomcom.datalength > MAX_MSGLEN) + { + I_FatalError("Netbuffer overflow!"); + } + uLong size = TRANSMIT_SIZE - 1; + if (doomcom.datalength >= 10) + { + assert(!(doomcom.data[0] & NCMD_COMPRESSED)); + TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED; + c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9); + size += 1; + } + else + { + c = -1; // Just some random error code to avoid sending the compressed buffer. + } + if (c == Z_OK && size < (uLong)doomcom.datalength) + { +// Printf("send %lu/%d\n", size, doomcom.datalength); + c = sendto(mysocket, (char *)TransmitBuffer, size, + 0, (sockaddr *)&sendaddress[doomcom.remotenode], + sizeof(sendaddress[doomcom.remotenode])); + } + else + { + if (doomcom.datalength > TRANSMIT_SIZE) + { + I_Error("Net compression failed (zlib error %d)", c); + } + else + { +// Printf("send %d\n", doomcom.datalength); + c = sendto(mysocket, (char *)doomcom.data, doomcom.datalength, + 0, (sockaddr *)&sendaddress[doomcom.remotenode], + sizeof(sendaddress[doomcom.remotenode])); + } + } // if (c == -1) // I_Error ("SendPacket error: %s",strerror(errno)); } @@ -212,8 +252,8 @@ void PacketGet (void) int node; fromlen = sizeof(fromaddress); - c = recvfrom (mysocket, (char*)doomcom.data, MAX_MSGLEN, 0 - , (sockaddr *)&fromaddress, &fromlen); + c = recvfrom (mysocket, (char*)TransmitBuffer, TRANSMIT_SIZE, 0, + (sockaddr *)&fromaddress, &fromlen); node = FindNode (&fromaddress); if (node >= 0 && c == SOCKET_ERROR) @@ -239,6 +279,26 @@ void PacketGet (void) return; } } + else if (c > 0) + { + doomcom.data[0] = TransmitBuffer[0] & ~NCMD_COMPRESSED; + if (TransmitBuffer[0] & NCMD_COMPRESSED) + { + uLongf msgsize = MAX_MSGLEN - 1; + int err = uncompress(doomcom.data + 1, &msgsize, TransmitBuffer + 1, c - 1); +// Printf("recv %d/%lu\n", c, msgsize + 1); + if (err != Z_OK) + { + I_Error("Net decompression failed (zlib error %d)\n", err); + } + c = msgsize + 1; + } + else + { +// Printf("recv %d\n", c); + memcpy(doomcom.data + 1, TransmitBuffer + 1, c - 1); + } + } doomcom.remotenode = node; doomcom.datalength = (short)c;