diff options
author | Thilo Schulz <arny@ats.s.bawue.de> | 2011-07-13 17:11:30 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-10 22:27:30 +0000 |
commit | 2bf2118d79994cfd805c3ffb5b421c6a65658b84 (patch) | |
tree | 867a9c4ac0d975e7aca4ee20714de8c97646ccde /src/server | |
parent | 5038ffeb97413a867d0bdadf761e2ba727781742 (diff) |
- 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
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/server.h | 10 | ||||
-rw-r--r-- | src/server/sv_client.c | 183 | ||||
-rw-r--r-- | src/server/sv_init.c | 2 | ||||
-rw-r--r-- | src/server/sv_main.c | 49 | ||||
-rw-r--r-- | src/server/sv_net_chan.c | 103 | ||||
-rw-r--r-- | src/server/sv_snapshot.c | 126 |
6 files changed, 267 insertions, 206 deletions
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 ); @@ -835,9 +835,40 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg) /* ================== +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; } } |