From 2bf2118d79994cfd805c3ffb5b421c6a65658b84 Mon Sep 17 00:00:00 2001 From: Thilo Schulz Date: Wed, 13 Jul 2011 17:11:30 +0000 Subject: - Improve snapshot rate and data rate control - Make server send packet fragments and queued packets when server is idle - Voip protocol detection is tied to com_protocol making past-end-of-message reading unncessary - Use Hunk_AllocateTempMemory() for buffering VOIP packets and fix buffering scheme that ryan hates so much - Disable packet scrambling for new protocol as it is useless now - Get rid of the old packet scrambling functions predating latest point release - Use Hunk_AllocateTempMemory() for netchan packet queue to fix memory leak when client gets disconnected with packets in the queue - Use Hunk_AllocateTempMemory() for download blocks to fix memory leak when client gets disconnected with download blocks in the queue - Fix SV_RateMsec to account for udp/udp6 packet lengths --- src/client/cl_input.c | 54 ++++++++++---- src/client/cl_main.c | 6 +- src/client/cl_net_chan.c | 32 +++++++-- src/client/cl_parse.c | 22 +----- src/client/cl_scrn.c | 2 +- src/client/client.h | 3 +- src/qcommon/common.c | 21 +++++- src/qcommon/net_chan.c | 96 +++---------------------- src/qcommon/qcommon.h | 12 ++-- src/server/server.h | 10 ++- src/server/sv_client.c | 183 +++++++++++++++++++++++++++-------------------- src/server/sv_init.c | 2 +- src/server/sv_main.c | 49 ++++++++++++- src/server/sv_net_chan.c | 103 +++++++++++++++++--------- src/server/sv_snapshot.c | 126 +++++++++----------------------- 15 files changed, 372 insertions(+), 349 deletions(-) (limited to 'src') diff --git a/src/client/cl_input.c b/src/client/cl_input.c index f9ec9d04..736378ba 100644 --- a/src/client/cl_input.c +++ b/src/client/cl_input.c @@ -801,8 +801,46 @@ void CL_WritePacket( void ) { #ifdef USE_VOIP if (clc.voipOutgoingDataSize > 0) { // only send if data. - MSG_WriteByte (&buf, clc_EOF); // placate legacy servers. - MSG_WriteByte (&buf, clc_extension); + // Move cl_voipSendTarget from a string to the bitmasks if needed. + if (cl_voipSendTarget->modified) { + char buffer[32]; + const char *target = cl_voipSendTarget->string; + + if (Q_stricmp(target, "attacker") == 0) { + int player = VM_Call( cgvm, CG_LAST_ATTACKER ); + Com_sprintf(buffer, sizeof (buffer), "%d", player); + target = buffer; + } else if (Q_stricmp(target, "crosshair") == 0) { + int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); + Com_sprintf(buffer, sizeof (buffer), "%d", player); + target = buffer; + } + + if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) { + const int all = 0x7FFFFFFF; + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all; + } else if (Q_stricmp(target, "none") == 0) { + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; + } else { + const char *ptr = target; + clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; + do { + if ((*ptr == ',') || (*ptr == '\0')) { + const int val = atoi(target); + target = ptr + 1; + if ((val >= 0) && (val < 31)) { + clc.voipTarget1 |= (1 << (val-0)); + } else if ((val >= 31) && (val < 62)) { + clc.voipTarget2 |= (1 << (val-31)); + } else if ((val >= 62) && (val < 93)) { + clc.voipTarget3 |= (1 << (val-62)); + } + } + } while (*(ptr++)); + } + cl_voipSendTarget->modified = qfalse; + } + MSG_WriteByte (&buf, clc_voip); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); @@ -824,8 +862,6 @@ void CL_WritePacket( void ) { MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); MSG_Bitstream (&fakemsg); MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); - MSG_WriteByte (&fakemsg, svc_EOF); - MSG_WriteByte (&fakemsg, svc_extension); MSG_WriteByte (&fakemsg, svc_voip); MSG_WriteShort (&fakemsg, clc.clientNum); MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration); @@ -889,16 +925,6 @@ void CL_WritePacket( void ) { } CL_Netchan_Transmit (&clc.netchan, &buf); - - // clients never really should have messages large enough - // to fragment, but in case they do, fire them all off - // at once - // TTimo: this causes a packet burst, which is bad karma for winsock - // added a WARNING message, we'll see if there are legit situations where this happens - while ( clc.netchan.unsentFragments ) { - Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" ); - CL_Netchan_TransmitNextFragment( &clc.netchan ); - } } /* diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 621d3cd6..1f95b7e0 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -289,7 +289,7 @@ void CL_Voip_f( void ) reason = "Not connected to a server"; else if (!clc.speexInitialized) reason = "Speex not initialized"; - else if (!cl_connectedToVoipServer) + else if (!clc.voipEnabled) reason = "Server doesn't support VoIP"; if (reason != NULL) { @@ -383,7 +383,7 @@ void CL_CaptureVoip(void) qboolean dontCapture = qfalse; if (clc.state != CA_ACTIVE) dontCapture = qtrue; // not connected to a server. - else if (!cl_connectedToVoipServer) + else if (!clc.voipEnabled) dontCapture = qtrue; // server doesn't support VoIP. else if (clc.demoplaying) dontCapture = qtrue; // playing back a demo. @@ -1429,7 +1429,7 @@ void CL_Disconnect( qboolean showMainMenu ) { #ifdef USE_VOIP // not connected to voip server anymore. - cl_connectedToVoipServer = qfalse; + clc.voipEnabled = qfalse; #endif // Stop recording any video diff --git a/src/client/cl_net_chan.c b/src/client/cl_net_chan.c index fbfc4dc9..0d057d1d 100644 --- a/src/client/cl_net_chan.c +++ b/src/client/cl_net_chan.c @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/qcommon.h" #include "client.h" +#ifdef LEGACY_PROTOCOL /* ============== CL_Netchan_Encode @@ -126,14 +127,22 @@ static void CL_Netchan_Decode( msg_t *msg ) { *(msg->data + i) = *(msg->data + i) ^ key; } } +#endif /* ================= CL_Netchan_TransmitNextFragment ================= */ -void CL_Netchan_TransmitNextFragment( netchan_t *chan ) { - Netchan_TransmitNextFragment( chan ); +qboolean CL_Netchan_TransmitNextFragment(netchan_t *chan) +{ + if(chan->unsentFragments) + { + Netchan_TransmitNextFragment(chan); + return qtrue; + } + + return qfalse; } /* @@ -144,8 +153,18 @@ CL_Netchan_Transmit void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); - CL_Netchan_Encode( msg ); - Netchan_Transmit( chan, msg->cursize, msg->data ); +#ifdef LEGACY_PROTOCOL + if(chan->compat) + CL_Netchan_Encode(msg); +#endif + + Netchan_Transmit(chan, msg->cursize, msg->data); + + // Transmit all fragments without delay + while(CL_Netchan_TransmitNextFragment(chan)) + { + Com_DPrintf("WARNING: #462 unsent fragments (not supposed to happen!)\n"); + } } /* @@ -160,7 +179,10 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { if (!ret) return qfalse; - CL_Netchan_Decode( msg ); +#ifdef LEGACY_PROTOCOL + if(chan->compat) + CL_Netchan_Decode(msg); +#endif return qtrue; } diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index 1b0e2802..147c3156 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -35,7 +35,6 @@ char *svc_strings[256] = { "svc_download", "svc_snapshot", "svc_EOF", - "svc_extension", "svc_voip", }; @@ -331,10 +330,6 @@ void CL_ParseSnapshot( msg_t *msg ) { int cl_connectedToPureServer; int cl_connectedToCheatServer; -#ifdef USE_VOIP -int cl_connectedToVoipServer; -#endif - /* ================== CL_SystemInfoChanged @@ -364,10 +359,8 @@ void CL_SystemInfoChanged( void ) { } #ifdef USE_VOIP - // in the future, (val) will be a protocol version string, so only - // accept explicitly 1, not generally non-zero. s = Info_ValueForKey( systemInfo, "sv_voip" ); - cl_connectedToVoipServer = (atoi( s ) == 1); + clc.voipEnabled = atoi(s); #endif s = Info_ValueForKey( systemInfo, "sv_cheats" ); @@ -862,19 +855,6 @@ void CL_ParseServerMessage( msg_t *msg ) { cmd = MSG_ReadByte( msg ); - // See if this is an extension command after the EOF, which means we - // got data that a legacy client should ignore. - if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) { - SHOWNET( msg, "EXTENSION" ); - MSG_ReadByte( msg ); // throw the svc_extension byte away. - cmd = MSG_ReadByte( msg ); // something legacy clients can't do! - // sometimes you get a svc_extension at end of stream...dangling - // bits in the huffman decoder giving a bogus value? - if (cmd == -1) { - cmd = svc_EOF; - } - } - if (cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c index c616640a..afafea25 100644 --- a/src/client/cl_scrn.c +++ b/src/client/cl_scrn.c @@ -343,7 +343,7 @@ void SCR_DrawVoipMeter( void ) { return; // not recording at the moment. else if (clc.state != CA_ACTIVE) return; // not connected to a server. - else if (!cl_connectedToVoipServer) + else if (!clc.voipEnabled) return; // server doesn't support VoIP. else if (clc.demoplaying) return; // playing back a demo. diff --git a/src/client/client.h b/src/client/client.h index 3e243df2..3db4e27f 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -237,6 +237,7 @@ typedef struct { unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations #ifdef USE_VOIP + qboolean voipEnabled; qboolean speexInitialized; int speexFrameSize; int speexSampleRate; @@ -521,7 +522,6 @@ extern int cl_connectedToPureServer; extern int cl_connectedToCheatServer; #ifdef USE_VOIP -extern int cl_connectedToVoipServer; void CL_Voip_f( void ); #endif @@ -627,7 +627,6 @@ void LAN_SaveServersToCache( void ); // cl_net_chan.c // void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data ); -void CL_Netchan_TransmitNextFragment( netchan_t *chan ); qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ); // diff --git a/src/qcommon/common.c b/src/qcommon/common.c index da0e8967..8732eab6 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -2948,7 +2948,11 @@ void Com_Frame( void ) { { if(com_sv_running->integer) { - // Send out download messages now that we're idle + // Send out fragmented packets now that we're idle + delayT = SV_SendQueuedMessages(); + if(delayT >= 0 && delayT < timeVal) + timeVal = delayT; + if(sv_dlRate->integer) { // Rate limiting. This is very imprecise for high @@ -2982,21 +2986,32 @@ void Com_Frame( void ) { // all of the bandwidth. This will result in an // effective maximum rate of 1MB/s per user, but the // low download window size limits this anyways. - timeVal = 2; + if(timeVal > 2) + timeVal = 2; + dlNextRound = dlStart + deltaT + 1; } else { dlNextRound = dlStart + delayT; - timeVal = delayT - deltaT; + delayT -= deltaT; + + if(delayT < timeVal) + timeVal = delayT; } } } } else + { SV_SendDownloadMessages(); + timeVal = 1; + } } + if(timeVal == 0) + timeVal = 1; + if(com_busyWait->integer) NET_Sleep(0); else diff --git a/src/qcommon/net_chan.c b/src/qcommon/net_chan.c index d9c6760b..8f2d8de7 100644 --- a/src/qcommon/net_chan.c +++ b/src/qcommon/net_chan.c @@ -96,92 +96,6 @@ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int chan->challenge = challenge; } -// TTimo: unused, commenting out to make gcc happy -#if 0 -/* -============== -Netchan_ScramblePacket - -A probably futile attempt to make proxy hacking somewhat -more difficult. -============== -*/ -#define SCRAMBLE_START 6 -static void Netchan_ScramblePacket( msg_t *buf ) { - unsigned seed; - int i, j, c, mask, temp; - int seq[MAX_PACKETLEN]; - - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); - c = buf->cursize; - if ( c <= SCRAMBLE_START ) { - return; - } - if ( c > MAX_PACKETLEN ) { - Com_Error( ERR_DROP, "MAX_PACKETLEN" ); - } - - // generate a sequence of "random" numbers - for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); - seq[i] = seed; - } - - // transpose each character - for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { - } - mask >>= 1; - for (i = SCRAMBLE_START ; i < c ; i++) { - j = SCRAMBLE_START + ( seq[i] & mask ); - temp = buf->data[j]; - buf->data[j] = buf->data[i]; - buf->data[i] = temp; - } - - // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } -} - -static void Netchan_UnScramblePacket( msg_t *buf ) { - unsigned seed; - int i, j, c, mask, temp; - int seq[MAX_PACKETLEN]; - - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); - c = buf->cursize; - if ( c <= SCRAMBLE_START ) { - return; - } - if ( c > MAX_PACKETLEN ) { - Com_Error( ERR_DROP, "MAX_PACKETLEN" ); - } - - // generate a sequence of "random" numbers - for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); - seq[i] = seed; - } - - // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } - - // transpose each character in reverse order - for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { - } - mask >>= 1; - for (i = c-1 ; i >= SCRAMBLE_START ; i--) { - j = SCRAMBLE_START + ( seq[i] & mask ); - temp = buf->data[j]; - buf->data[j] = buf->data[i]; - buf->data[i] = temp; - } -} -#endif - /* ================= Netchan_TransmitNextFragment @@ -219,7 +133,11 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) { MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength ); // send the datagram - NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); + NET_SendPacket(chan->sock, send.cursize, send.data, chan->remoteAddress); + + // Store send time and size of this packet for rate control + chan->lastSentTime = Sys_Milliseconds(); + chan->lastSentSize = send.cursize; if ( showpackets->integer ) { Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n" @@ -289,6 +207,10 @@ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) { // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); + // Store send time and size of this packet for rate control + chan->lastSentTime = Sys_Milliseconds(); + chan->lastSentSize = send.cursize; + if ( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n" , netsrcString[ chan->sock ] diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index 28834168..49501d52 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -225,6 +225,8 @@ typedef struct { byte unsentBuffer[MAX_MSGLEN]; int challenge; + int lastSentTime; + int lastSentSize; } netchan_t; void Netchan_Init( int qport ); @@ -277,9 +279,7 @@ enum svc_ops_e { svc_snapshot, svc_EOF, - // svc_extension follows a svc_EOF, followed by another svc_* ... - // this keeps legacy clients compatible. - svc_extension, +// new commands, supported only by ioquake3 protocol but not legacy svc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; @@ -295,9 +295,7 @@ enum clc_ops_e { clc_clientCommand, // [string] message clc_EOF, - // clc_extension follows a clc_EOF, followed by another clc_* ... - // this keeps legacy servers compatible. - clc_extension, +// new commands, supported only by ioquake3 protocol but not legacy clc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; @@ -1013,7 +1011,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg ); int SV_FrameMsec(void); qboolean SV_GameCommand( void ); int SV_SendDownloadMessages(void); - +int SV_SendQueuedMessages(void); // // UI interface diff --git a/src/server/server.h b/src/server/server.h index 1434a87f..7887a2c3 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -35,6 +35,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_ENT_CLUSTERS 16 #ifdef USE_VOIP +#define VOIP_QUEUE_LENGTH 64 + typedef struct voipServerPacket_s { int generation; @@ -170,7 +172,7 @@ typedef struct client_s { int nextReliableTime; // svs.time when another reliable command will be allowed int lastPacketTime; // svs.time when packet was last received int lastConnectTime; // svs.time when connection started - int nextSnapshotTime; // send another snapshot when svs.time >= nextSnapshotTime + int lastSnapshotTime; // svs.time of last sent snapshot qboolean rateDelayed; // true if nextSnapshotTime was set based on rate instead of snapshotMsec int timeoutCount; // must timeout a few frames in a row so debugging doesn't break clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here @@ -191,8 +193,9 @@ typedef struct client_s { qboolean hasVoip; qboolean muteAllVoip; qboolean ignoreVoipFromClient[MAX_CLIENTS]; - voipServerPacket_t voipPacket[64]; // !!! FIXME: WAY too much memory! + voipServerPacket_t *voipPacket[VOIP_QUEUE_LENGTH]; int queuedVoipPackets; + int queuedVoipIndex; #endif int oldServerTime; @@ -297,6 +300,7 @@ void SV_RemoveOperatorCommands (void); void SV_MasterShutdown (void); void SV_MasterGameStat( const char *data ); +int SV_RateMsec(client_t *client); @@ -425,6 +429,6 @@ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, con // sv_net_chan.c // void SV_Netchan_Transmit( client_t *client, msg_t *msg); -void SV_Netchan_TransmitNextFragment( client_t *client ); +int SV_Netchan_TransmitNextFragment(client_t *client); qboolean SV_Netchan_Process( client_t *client, msg_t *msg ); diff --git a/src/server/sv_client.c b/src/server/sv_client.c index 99dca704..b05aba56 100644 --- a/src/server/sv_client.c +++ b/src/server/sv_client.c @@ -348,7 +348,7 @@ gotnewcl: Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name ); newcl->state = CS_CONNECTED; - newcl->nextSnapshotTime = svs.time; + newcl->lastSnapshotTime = 0; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; @@ -535,7 +535,7 @@ void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) { client->gentity = ent; client->deltaMessage = -1; - client->nextSnapshotTime = svs.time; // generate a snapshot immediately + client->lastSnapshotTime = 0; // generate a snapshot immediately if(cmd) memcpy(&client->lastUsercmd, cmd, sizeof(client->lastUsercmd)); @@ -574,7 +574,7 @@ static void SV_CloseDownload( client_t *cl ) { // Free the temporary buffer space for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) { if (cl->downloadBlocks[i]) { - Z_Free( cl->downloadBlocks[i] ); + Hunk_FreeTempMemory(cl->downloadBlocks[i]); cl->downloadBlocks[i] = NULL; } } @@ -766,7 +766,7 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg) curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW); if (!cl->downloadBlocks[curindex]) - cl->downloadBlocks[curindex] = Z_Malloc( MAX_DOWNLOAD_BLKSIZE ); + cl->downloadBlocks[curindex] = Hunk_AllocateTempMemory(MAX_DOWNLOAD_BLKSIZE); cl->downloadBlockSize[curindex] = FS_Read( cl->downloadBlocks[curindex], MAX_DOWNLOAD_BLKSIZE, cl->download ); @@ -833,11 +833,42 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg) return 1; } +/* +================== +SV_SendQueuedMessages + +Send one round of fragments, or queued messages to all clients that have data pending. +Return the shortest time interval for sending next packet to client +================== +*/ + +int SV_SendQueuedMessages(void) +{ + int i, retval = -1, nextFragT; + client_t *cl; + + for(i=0; i < sv_maxclients->integer; i++) + { + cl = &svs.clients[i]; + + if(cl->state) + { + nextFragT = SV_Netchan_TransmitNextFragment(cl); + + if(nextFragT >= 0 && (retval == -1 || retval > nextFragT)) + retval = nextFragT; + } + } + + return retval; +} + + /* ================== SV_SendDownloadMessages -Send download messages to all clients +Send one round of download messages to all clients ================== */ @@ -848,25 +879,22 @@ int SV_SendDownloadMessages(void) msg_t msg; byte msgBuffer[MAX_MSGLEN]; - for(i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) + for(i=0; i < sv_maxclients->integer; i++) { + cl = &svs.clients[i]; + if(cl->state && *cl->downloadName) { - if(cl->netchan.unsentFragments) - SV_Netchan_TransmitNextFragment(cl); - else - { - MSG_Init(&msg, msgBuffer, sizeof(msgBuffer)); - MSG_WriteLong(&msg, cl->lastClientCommand); + MSG_Init(&msg, msgBuffer, sizeof(msgBuffer)); + MSG_WriteLong(&msg, cl->lastClientCommand); - retval = SV_WriteDownloadToClient(cl, &msg); + retval = SV_WriteDownloadToClient(cl, &msg); - if(retval) - { - MSG_WriteByte(&msg, svc_EOF); - SV_Netchan_Transmit(cl, &msg); - numDLs += retval; - } + if(retval) + { + MSG_WriteByte(&msg, svc_EOF); + SV_Netchan_Transmit(cl, &msg); + numDLs += retval; } } } @@ -884,43 +912,41 @@ Check to see if there is any VoIP queued for a client, and send if there is. */ void SV_WriteVoipToClient( client_t *cl, msg_t *msg ) { - voipServerPacket_t *packet = &cl->voipPacket[0]; - int totalbytes = 0; - int i; - - if (*cl->downloadName) { + if(*cl->downloadName) + { cl->queuedVoipPackets = 0; return; // no VoIP allowed if download is going, to save bandwidth. } - // Write as many VoIP packets as we reasonably can... - for (i = 0; i < cl->queuedVoipPackets; i++, packet++) { - totalbytes += packet->len; - if (totalbytes > MAX_DOWNLOAD_BLKSIZE) - break; + if(cl->queuedVoipPackets) + { + int totalbytes = 0; + int i; + voipServerPacket_t *packet; - // You have to start with a svc_EOF, so legacy clients drop the - // rest of this packet. Otherwise, those without VoIP support will - // see the svc_voip command, then panic and disconnect. - // Generally we don't send VoIP packets to legacy clients, but this - // serves as both a safety measure and a means to keep demo files - // compatible. - MSG_WriteByte( msg, svc_EOF ); - MSG_WriteByte( msg, svc_extension ); - MSG_WriteByte( msg, svc_voip ); - MSG_WriteShort( msg, packet->sender ); - MSG_WriteByte( msg, (byte) packet->generation ); - MSG_WriteLong( msg, packet->sequence ); - MSG_WriteByte( msg, packet->frames ); - MSG_WriteShort( msg, packet->len ); - MSG_WriteData( msg, packet->data, packet->len ); - } + // Write as many VoIP packets as we reasonably can... + for(i = cl->queuedVoipIndex; i < cl->queuedVoipPackets; i++) + { + packet = cl->voipPacket[i % ARRAY_LEN(cl->voipPacket)]; + + totalbytes += packet->len; + if (totalbytes > (msg->maxsize - msg->cursize) / 2) + break; + + MSG_WriteByte(msg, svc_voip); + MSG_WriteShort(msg, packet->sender); + MSG_WriteByte(msg, (byte) packet->generation); + MSG_WriteLong(msg, packet->sequence); + MSG_WriteByte(msg, packet->frames); + MSG_WriteShort(msg, packet->len); + MSG_WriteData(msg, packet->data, packet->len); + + Hunk_FreeTempMemory(packet); + } - // !!! FIXME: I hate this queue system. - cl->queuedVoipPackets -= i; - if (cl->queuedVoipPackets > 0) { - memmove( &cl->voipPacket[0], &cl->voipPacket[i], - sizeof (voipServerPacket_t) * i); + cl->queuedVoipPackets -= i; + cl->queuedVoipIndex += i; + cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket); } } #endif @@ -1092,7 +1118,7 @@ static void SV_VerifyPaks_f( client_t *cl ) { } else { cl->pureAuthentic = 0; - cl->nextSnapshotTime = -1; + cl->lastSnapshotTime = 0; cl->state = CS_ACTIVE; SV_SendClientSnapshot( cl ); SV_SendServerCommand( cl, "disconnect \"Unpure Client. " @@ -1161,23 +1187,38 @@ void SV_UserinfoChanged( client_t *cl ) { // snaps command val = Info_ValueForKey (cl->userinfo, "snaps"); - if (strlen(val)) { + + if(strlen(val)) + { i = atoi(val); - if ( i < 1 ) { + + if(i < 1) i = 1; - } else if ( i > sv_fps->integer ) { + else if(i > sv_fps->integer) i = sv_fps->integer; - } - cl->snapshotMsec = 1000/i; - } else { - cl->snapshotMsec = 50; + + i = 1000 / i; + } + else + i = 50; + + if(i != cl->snapshotMsec) + { + // Reset last sent snapshot so we avoid desync between server frame time and snapshot send time + cl->lastSnapshotTime = 0; + cl->snapshotMsec = i; } #ifdef USE_VOIP - // in the future, (val) will be a protocol version string, so only - // accept explicitly 1, not generally non-zero. - val = Info_ValueForKey (cl->userinfo, "cl_voip"); - cl->hasVoip = (atoi(val) == 1) ? qtrue : qfalse; +#ifdef LEGACY_PROTOCOL + if(cl->compat) + cl->hasVoip = qfalse; + else +#endif + { + val = Info_ValueForKey(cl->userinfo, "cl_voip"); + cl->hasVoip = atoi(val); + } #endif // TTimo @@ -1514,7 +1555,7 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) { const int recip2 = MSG_ReadLong(msg); const int recip3 = MSG_ReadLong(msg); const int packetsize = MSG_ReadShort(msg); - byte encoded[sizeof (cl->voipPacket[0].data)]; + byte encoded[sizeof(cl->voipPacket[0]->data)]; client_t *client = NULL; voipServerPacket_t *packet = NULL; int i; @@ -1588,13 +1629,15 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) { continue; // no room for another packet right now. } - packet = &client->voipPacket[client->queuedVoipPackets]; + packet = Hunk_AllocateTempMemory(sizeof(*packet)); packet->sender = sender; packet->frames = frames; packet->len = packetsize; packet->generation = generation; packet->sequence = sequence; memcpy(packet->data, encoded, packetsize); + + client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet; client->queuedVoipPackets++; } } @@ -1687,18 +1730,6 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { do { c = MSG_ReadByte( msg ); - // See if this is an extension command after the EOF, which means we - // got data that a legacy server should ignore. - if ((c == clc_EOF) && (MSG_LookaheadByte( msg ) == clc_extension)) { - MSG_ReadByte( msg ); // throw the clc_extension byte away. - c = MSG_ReadByte( msg ); // something legacy servers can't do! - // sometimes you get a clc_extension at end of stream...dangling - // bits in the huffman decoder giving a bogus value? - if (c == -1) { - c = clc_EOF; - } - } - if ( c == clc_EOF ) { break; } diff --git a/src/server/sv_init.c b/src/server/sv_init.c index e29cd2dc..d2e51d9f 100644 --- a/src/server/sv_init.c +++ b/src/server/sv_init.c @@ -700,7 +700,7 @@ void SV_FinalMessage( char *message ) { SV_SendServerCommand( cl, "disconnect \"%s\"", message ); } // force a snapshot to be sent - cl->nextSnapshotTime = -1; + cl->lastSnapshotTime = 0; SV_SendClientSnapshot( cl ); } } diff --git a/src/server/sv_main.c b/src/server/sv_main.c index f746c180..166e2241 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -1139,5 +1139,52 @@ void SV_Frame( int msec ) { SV_MasterHeartbeat(sv_heartbeat->string); } -//============================================================================ +/* +==================== +SV_RateMsec + +Return the number of msec until another message can be sent to +a client based on its rate settings +==================== +*/ + +#define UDPIP_HEADER_SIZE 28 +#define UDPIP6_HEADER_SIZE 48 +int SV_RateMsec(client_t *client) +{ + int rate, rateMsec; + int messageSize; + + messageSize = client->netchan.lastSentSize; + rate = client->rate; + + if(sv_maxRate->integer) + { + if(sv_maxRate->integer < 1000) + Cvar_Set( "sv_MaxRate", "1000" ); + if(sv_maxRate->integer < rate) + rate = sv_maxRate->integer; + } + + if(sv_minRate->integer) + { + if(sv_minRate->integer < 1000) + Cvar_Set("sv_minRate", "1000"); + if(sv_minRate->integer > rate) + rate = sv_minRate->integer; + } + + if(client->netchan.remoteAddress.type == NA_IP6) + messageSize += UDPIP6_HEADER_SIZE; + else + messageSize += UDPIP_HEADER_SIZE; + + rateMsec = messageSize * 1000 / ((int) (rate * com_timescale->value)); + rate = Sys_Milliseconds() - client->netchan.lastSentTime; + + if(rate > rateMsec) + return 0; + else + return rateMsec - rate; +} diff --git a/src/server/sv_net_chan.c b/src/server/sv_net_chan.c index 33d04b3f..79e294f0 100644 --- a/src/server/sv_net_chan.c +++ b/src/server/sv_net_chan.c @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/qcommon.h" #include "server.h" +#ifdef LEGACY_PROTOCOL /* ============== SV_Netchan_Encode @@ -128,38 +129,63 @@ static void SV_Netchan_Decode( client_t *client, msg_t *msg ) { *(msg->data + i) = *(msg->data + i) ^ key; } } +#endif + +/* +================= +SV_Netchan_TransmitNextInQueue +================= +*/ +void SV_Netchan_TransmitNextInQueue(client_t *client) +{ + netchan_buffer_t *netbuf; + + Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n"); + netbuf = client->netchan_start_queue; + +#ifdef LEGACY_PROTOCOL + if(client->compat) + SV_Netchan_Encode(client, &netbuf->msg); +#endif + + Netchan_Transmit(&client->netchan, netbuf->msg.cursize, netbuf->msg.data); + + // pop from queue + client->netchan_start_queue = netbuf->next; + if(!client->netchan_start_queue) + { + Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n"); + client->netchan_end_queue = &client->netchan_start_queue; + } + else + Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n"); + + Hunk_FreeTempMemory(netbuf); +} /* ================= SV_Netchan_TransmitNextFragment +Transmit the next fragment and the next queued packet +Return number of ms until next message can be sent based on throughput given by client rate, +-1 if no packet was sent. ================= */ -void SV_Netchan_TransmitNextFragment( client_t *client ) { - Netchan_TransmitNextFragment( &client->netchan ); - if (!client->netchan.unsentFragments) + +int SV_Netchan_TransmitNextFragment(client_t *client) +{ + if(client->netchan.unsentFragments) { - // make sure the netchan queue has been properly initialized (you never know) - if ((!client->netchan_end_queue) && (client->state >= CS_CONNECTED)) { - Com_Error(ERR_DROP, "netchan queue is not properly initialized in SV_Netchan_TransmitNextFragment"); - } - // the last fragment was transmitted, check wether we have queued messages - if (client->netchan_start_queue) { - netchan_buffer_t *netbuf; - Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n"); - netbuf = client->netchan_start_queue; - SV_Netchan_Encode( client, &netbuf->msg ); - Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data ); - // pop from queue - client->netchan_start_queue = netbuf->next; - if (!client->netchan_start_queue) { - Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n"); - client->netchan_end_queue = &client->netchan_start_queue; - } - else - Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n"); - Z_Free(netbuf); - } - } + Netchan_TransmitNextFragment(&client->netchan); + return SV_RateMsec(client); + } + else if(client->netchan_start_queue) + { + SV_Netchan_TransmitNextInQueue(client); + return SV_RateMsec(client); + } + + return -1; } @@ -174,22 +200,28 @@ then buffer them and make sure they get sent in correct order ================ */ -void SV_Netchan_Transmit( client_t *client, msg_t *msg) { //int length, const byte *data ) { +void SV_Netchan_Transmit( client_t *client, msg_t *msg) +{ MSG_WriteByte( msg, svc_EOF ); - if (client->netchan.unsentFragments) { + + if(client->netchan.unsentFragments || client->netchan_start_queue) + { netchan_buffer_t *netbuf; Com_DPrintf("#462 SV_Netchan_Transmit: unsent fragments, stacked\n"); - netbuf = (netchan_buffer_t *)Z_Malloc(sizeof(netchan_buffer_t)); + netbuf = (netchan_buffer_t *) Hunk_AllocateTempMemory(sizeof(netchan_buffer_t)); // store the msg, we can't store it encoded, as the encoding depends on stuff we still have to finish sending MSG_Copy(&netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg); netbuf->next = NULL; // insert it in the queue, the message will be encoded and sent later *client->netchan_end_queue = netbuf; client->netchan_end_queue = &(*client->netchan_end_queue)->next; - // emit the next fragment of the current message for now - Netchan_TransmitNextFragment(&client->netchan); - } else { - SV_Netchan_Encode( client, msg ); + } + else + { +#ifdef LEGACY_PROTOCOL + if(client->compat) + SV_Netchan_Encode(client, msg); +#endif Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } } @@ -204,7 +236,12 @@ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) { ret = Netchan_Process( &client->netchan, msg ); if (!ret) return qfalse; - SV_Netchan_Decode( client, msg ); + +#ifdef LEGACY_PROTOCOL + if(client->compat) + SV_Netchan_Decode(client, msg); +#endif + return qtrue; } diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c index 590cfd7e..d57acf5c 100644 --- a/src/server/sv_snapshot.c +++ b/src/server/sv_snapshot.c @@ -522,45 +522,6 @@ static void SV_BuildClientSnapshot( client_t *client ) { } } - -/* -==================== -SV_RateMsec - -Return the number of msec a given size message is supposed -to take to clear, based on the current rate -==================== -*/ -#define HEADER_RATE_BYTES 48 // include our header, IP header, and some overhead -static int SV_RateMsec( client_t *client, int messageSize ) { - int rate; - int rateMsec; - - // individual messages will never be larger than fragment size - if ( messageSize > 1500 ) { - messageSize = 1500; - } - rate = client->rate; - if ( sv_maxRate->integer ) { - if ( sv_maxRate->integer < 1000 ) { - Cvar_Set( "sv_MaxRate", "1000" ); - } - if ( sv_maxRate->integer < rate ) { - rate = sv_maxRate->integer; - } - } - if ( sv_minRate->integer ) { - if ( sv_minRate->integer < 1000 ) - Cvar_Set( "sv_minRate", "1000" ); - if ( sv_minRate->integer > rate ) - rate = sv_minRate->integer; - } - - rateMsec = ( messageSize + HEADER_RATE_BYTES ) * 1000 / ((int) (rate * com_timescale->value)); - - return rateMsec; -} - /* ======================= SV_SendMessageToClient @@ -568,48 +529,15 @@ SV_SendMessageToClient Called by SV_SendClientSnapshot and SV_SendClientGameState ======================= */ -void SV_SendMessageToClient( msg_t *msg, client_t *client ) { - int rateMsec; - +void SV_SendMessageToClient(msg_t *msg, client_t *client) +{ // record information about the message client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1; // send the datagram - SV_Netchan_Transmit( client, msg ); //msg->cursize, msg->data ); - - // set nextSnapshotTime based on rate and requested number of updates - - // local clients get snapshots every server frame - // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491 - // added sv_lanForceRate check - if ( client->netchan.remoteAddress.type == NA_LOOPBACK || (sv_lanForceRate->integer && Sys_IsLANAddress (client->netchan.remoteAddress)) ) { - client->nextSnapshotTime = svs.time + ((int) (1000.0 / sv_fps->integer * com_timescale->value)); - return; - } - - // normal rate / snapshotMsec calculation - rateMsec = SV_RateMsec(client, msg->cursize); - - if ( rateMsec < client->snapshotMsec * com_timescale->value) { - // never send more packets than this, no matter what the rate is at - rateMsec = client->snapshotMsec * com_timescale->value; - client->rateDelayed = qfalse; - } else { - client->rateDelayed = qtrue; - } - - client->nextSnapshotTime = svs.time + ((int) (rateMsec * com_timescale->value)); - - // don't pile up empty snapshots while connecting - if ( client->state != CS_ACTIVE ) { - // a gigantic connection message may have already put the nextSnapshotTime - // more than a second away, so don't shorten it - // do shorten if client is downloading - if (!*client->downloadName && client->nextSnapshotTime < svs.time + ((int) (1000.0 * com_timescale->value))) - client->nextSnapshotTime = svs.time + ((int) (1000 * com_timescale->value)); - } + SV_Netchan_Transmit(client, msg); } @@ -661,33 +589,47 @@ void SV_SendClientSnapshot( client_t *client ) { SV_SendClientMessages ======================= */ -void SV_SendClientMessages( void ) { - int i; +void SV_SendClientMessages(void) +{ + int i; client_t *c; // send a message to each connected client - for (i=0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++) { - if (!c->state) { + for(i=0; i < sv_maxclients->integer; i++) + { + c = &svs.clients[i]; + + if(!c->state) continue; // not connected - } - - if ( svs.time < c->nextSnapshotTime ) { - continue; // not time yet - } if(*c->downloadName) continue; // Client is downloading, don't send snapshots - // send additional message fragments if the last message - // was too large to send at once - if ( c->netchan.unsentFragments ) { - c->nextSnapshotTime = svs.time + - SV_RateMsec( c, c->netchan.unsentLength - c->netchan.unsentFragmentStart ); - SV_Netchan_TransmitNextFragment( c ); - continue; + if(!(c->netchan.remoteAddress.type == NA_LOOPBACK || + (sv_lanForceRate->integer && Sys_IsLANAddress(c->netchan.remoteAddress)))) + { + // rate control for clients not on LAN + + if(svs.time - c->lastSnapshotTime < c->snapshotMsec * com_timescale->value) + continue; // It's not time yet + + if(c->netchan.unsentFragments || c->netchan_start_queue) + { + c->rateDelayed = qtrue; + continue; // Drop this snapshot if the packet queue is still full + } + + if(SV_RateMsec(c) > 0) + { + // Not enough time since last packet passed through the line + c->rateDelayed = qtrue; + continue; + } } // generate and send a new message - SV_SendClientSnapshot( c ); + SV_SendClientSnapshot(c); + c->lastSnapshotTime = svs.time; + c->rateDelayed = qfalse; } } -- cgit