summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThilo Schulz <arny@ats.s.bawue.de>2011-07-12 11:01:20 +0000
committerTim Angus <tim@ngus.net>2013-01-10 21:57:40 +0000
commit3bd2ba9e590cb882b684b1eb4b2650b1a4c94f7f (patch)
tree277a7efa735dfa9854d5e41199b65ef48f0043c2
parentc22cbd3e5552c0599dad9d60c1ea80a56b48f883 (diff)
- Greatly improve UDP downloading speed for clients - Add download rate control cvar sv_dlRate - Don't send snapshots to downloading clients
-rw-r--r--src/qcommon/common.c66
-rw-r--r--src/qcommon/qcommon.h6
-rw-r--r--src/server/server.h2
-rw-r--r--src/server/sv_client.c134
-rw-r--r--src/server/sv_snapshot.c7
5 files changed, 140 insertions, 75 deletions
diff --git a/src/qcommon/common.c b/src/qcommon/common.c
index 4a0e48ad..c9f9816d 100644
--- a/src/qcommon/common.c
+++ b/src/qcommon/common.c
@@ -77,6 +77,7 @@ cvar_t *cl_paused;
cvar_t *sv_paused;
cvar_t *cl_packetdelay;
cvar_t *sv_packetdelay;
+cvar_t *sv_dlRate;
cvar_t *com_cameraMode;
cvar_t *com_ansiColor;
cvar_t *com_unfocused;
@@ -2649,6 +2650,7 @@ void Com_Init( char *commandLine ) {
sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM);
cl_packetdelay = Cvar_Get ("cl_packetdelay", "0", CVAR_CHEAT);
sv_packetdelay = Cvar_Get ("sv_packetdelay", "0", CVAR_CHEAT);
+ sv_dlRate = Cvar_Get ("sv_dlRate", "1000", CVAR_ARCHIVE);
com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM);
com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM);
com_buildScript = Cvar_Get( "com_buildScript", "0", 0 );
@@ -2873,6 +2875,9 @@ void Com_Frame( void ) {
int msec, minMsec;
int timeVal;
+ int numBlocks = 1;
+ int dlStart, deltaT, delayT;
+ static int dlNextRound = 0;
static int lastTime = 0, bias = 0;
int timeBeforeFirstEvents;
@@ -2933,13 +2938,70 @@ void Com_Frame( void ) {
minMsec = 1;
timeVal = 0;
+
do
{
// Busy sleep the last millisecond for better timeout precision
- if(com_busyWait->integer || timeVal < 2)
+ if(timeVal < 2)
NET_Sleep(0);
else
- NET_Sleep(timeVal - 1);
+ {
+ if(com_sv_running->integer)
+ {
+ // Send out download messages now that we're idle
+ if(sv_dlRate->integer)
+ {
+ // Rate limiting. This is very imprecise for high
+ // download rates due to millisecond timedelta resolution
+ dlStart = Sys_Milliseconds();
+ deltaT = dlNextRound - dlStart;
+
+ if(deltaT > 0)
+ {
+ if(deltaT < timeVal)
+ timeVal = deltaT + 1;
+ }
+ else
+ {
+ numBlocks = SV_SendDownloadMessages();
+
+ if(numBlocks)
+ {
+ // There are active downloads
+ deltaT = Sys_Milliseconds() - dlStart;
+
+ delayT = 1000 * numBlocks * MAX_DOWNLOAD_BLKSIZE;
+ delayT /= sv_dlRate->integer * 1024;
+
+ if(delayT <= deltaT + 1)
+ {
+ // Sending the last round of download messages
+ // took too long for given rate, don't wait for
+ // next round, but always enforce a 1ms delay
+ // between DL message rounds so we don't hog
+ // 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;
+ dlNextRound = dlStart + deltaT + 1;
+ }
+ else
+ {
+ dlNextRound = dlStart + delayT;
+ timeVal = delayT - deltaT;
+ }
+ }
+ }
+ }
+ else
+ SV_SendDownloadMessages();
+ }
+
+ if(com_busyWait->integer)
+ NET_Sleep(0);
+ else
+ NET_Sleep(timeVal - 1);
+ }
msec = Sys_Milliseconds() - com_frameTime;
diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h
index 8a7dbe7f..165dccae 100644
--- a/src/qcommon/qcommon.h
+++ b/src/qcommon/qcommon.h
@@ -190,8 +190,9 @@ void NET_Sleep(int msec);
#define MAX_MSGLEN 16384 // max length of a message, which may
// be fragmented into multiple packets
-#define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames
-#define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks
+#define MAX_DOWNLOAD_WINDOW 48 // ACK window of 48 download chunks. Cannot set this higher, or clients
+ // will overflow the reliable commands buffer
+#define MAX_DOWNLOAD_BLKSIZE 1024 // 896 byte block chunks
/*
@@ -1008,6 +1009,7 @@ void SV_Frame( int msec );
void SV_PacketEvent( netadr_t from, msg_t *msg );
int SV_FrameMsec(void);
qboolean SV_GameCommand( void );
+int SV_SendDownloadMessages(void);
//
diff --git a/src/server/server.h b/src/server/server.h
index 697d8a30..1434a87f 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -332,7 +332,7 @@ void SV_DropClient( client_t *drop, const char *reason );
void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK );
void SV_ClientThink (client_t *cl, usercmd_t *cmd);
-void SV_WriteDownloadToClient( client_t *cl , msg_t *msg );
+int SV_WriteDownloadToClient(client_t *cl , msg_t *msg);
#ifdef USE_VOIP
void SV_WriteVoipToClient( client_t *cl, msg_t *msg );
diff --git a/src/server/sv_client.c b/src/server/sv_client.c
index 082c0417..8ff3f0a1 100644
--- a/src/server/sv_client.c
+++ b/src/server/sv_client.c
@@ -646,21 +646,19 @@ static void SV_BeginDownload_f( client_t *cl ) {
SV_WriteDownloadToClient
Check to see if the client wants a file, open it if needed and start pumping the client
-Fill up msg with data
+Fill up msg with data, return number of download blocks added
==================
*/
-void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
+int SV_WriteDownloadToClient(client_t *cl, msg_t *msg)
{
int curindex;
- int rate;
- int blockspersnap;
int unreferenced = 1;
char errorMessage[1024];
char pakbuf[MAX_QPATH], *pakptr;
int numRefPaks;
if (!*cl->downloadName)
- return; // Nothing being downloaded
+ return 0; // Nothing being downloaded
if(!cl->download)
{
@@ -736,7 +734,7 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
if(cl->download)
FS_FCloseFile(cl->download);
- return;
+ return 0;
}
Com_Printf( "clientDownload: %d : beginning \"%s\"\n", (int) (cl - svs.clients), cl->downloadName );
@@ -781,81 +779,85 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
cl->downloadEOF = qtrue; // We have added the EOF block
}
- // Loop up to window size times based on how many blocks we can fit in the
- // client snapMsec and rate
+ if (cl->downloadClientBlock == cl->downloadCurrentBlock)
+ return 0; // Nothing to transmit
- // based on the rate, how many bytes can we fit in the snapMsec time of the client
- // normal rate / snapshotMsec calculation
- rate = cl->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;
+ // Write out the next section of the file, if we have already reached our window,
+ // automatically start retransmitting
+ if (cl->downloadXmitBlock == cl->downloadCurrentBlock)
+ {
+ // We have transmitted the complete window, should we start resending?
+ if (svs.time - cl->downloadSendTime > 1000)
+ cl->downloadXmitBlock = cl->downloadClientBlock;
+ else
+ return 0;
}
- if (!rate) {
- blockspersnap = 1;
- } else {
- blockspersnap = ( (rate * cl->snapshotMsec) / 1000 + MAX_DOWNLOAD_BLKSIZE ) /
- MAX_DOWNLOAD_BLKSIZE;
- }
+ // Send current block
+ curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW);
- if (blockspersnap < 0)
- blockspersnap = 1;
+ MSG_WriteByte( msg, svc_download );
+ MSG_WriteShort( msg, cl->downloadXmitBlock );
- while (blockspersnap--) {
+ // block zero is special, contains file size
+ if ( cl->downloadXmitBlock == 0 )
+ MSG_WriteLong( msg, cl->downloadSize );
- // Write out the next section of the file, if we have already reached our window,
- // automatically start retransmitting
+ MSG_WriteShort( msg, cl->downloadBlockSize[curindex] );
- if (cl->downloadClientBlock == cl->downloadCurrentBlock)
- return; // Nothing to transmit
+ // Write the block
+ if(cl->downloadBlockSize[curindex])
+ MSG_WriteData(msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex]);
- if (cl->downloadXmitBlock == cl->downloadCurrentBlock) {
- // We have transmitted the complete window, should we start resending?
+ Com_DPrintf( "clientDownload: %d : writing block %d\n", (int) (cl - svs.clients), cl->downloadXmitBlock );
- //FIXME: This uses a hardcoded one second timeout for lost blocks
- //the timeout should be based on client rate somehow
- if (svs.time - cl->downloadSendTime > 1000)
- cl->downloadXmitBlock = cl->downloadClientBlock;
- else
- return;
- }
+ // Move on to the next block
+ // It will get sent with next snap shot. The rate will keep us in line.
+ cl->downloadXmitBlock++;
+ cl->downloadSendTime = svs.time;
- // Send current block
- curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW);
+ return 1;
+}
- MSG_WriteByte( msg, svc_download );
- MSG_WriteShort( msg, cl->downloadXmitBlock );
+/*
+==================
+SV_SendDownloadMessages
- // block zero is special, contains file size
- if ( cl->downloadXmitBlock == 0 )
- MSG_WriteLong( msg, cl->downloadSize );
-
- MSG_WriteShort( msg, cl->downloadBlockSize[curindex] );
+Send download messages to all clients
+==================
+*/
- // Write the block
- if ( cl->downloadBlockSize[curindex] ) {
- MSG_WriteData( msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex] );
+int SV_SendDownloadMessages(void)
+{
+ int i, numDLs = 0, retval;
+ client_t *cl;
+ msg_t msg;
+ byte msgBuffer[MAX_MSGLEN];
+
+ for(i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++)
+ {
+ 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);
+
+ retval = SV_WriteDownloadToClient(cl, &msg);
+
+ if(retval)
+ {
+ MSG_WriteByte(&msg, svc_EOF);
+ SV_Netchan_Transmit(cl, &msg);
+ numDLs += retval;
+ }
+ }
}
-
- Com_DPrintf( "clientDownload: %d : writing block %d\n", (int) (cl - svs.clients), cl->downloadXmitBlock );
-
- // Move on to the next block
- // It will get sent with next snap shot. The rate will keep us in line.
- cl->downloadXmitBlock++;
-
- cl->downloadSendTime = svs.time;
}
+
+ return numDLs;
}
#ifdef USE_VOIP
diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c
index 10820d07..590cfd7e 100644
--- a/src/server/sv_snapshot.c
+++ b/src/server/sv_snapshot.c
@@ -642,9 +642,6 @@ void SV_SendClientSnapshot( client_t *client ) {
// and the playerState_t
SV_WriteSnapshotToClient( client, &msg );
- // Add any download data if the client is downloading
- SV_WriteDownloadToClient( client, &msg );
-
#ifdef USE_VOIP
SV_WriteVoipToClient( client, &msg );
#endif
@@ -678,6 +675,9 @@ void SV_SendClientMessages( void ) {
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 ) {
@@ -691,4 +691,3 @@ void SV_SendClientMessages( void ) {
SV_SendClientSnapshot( c );
}
}
-