[quake3-commits] r2073 - in trunk: . code/qcommon code/server
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Tue Jul 12 07:01:21 EDT 2011
Author: thilo
Date: 2011-07-12 07:01:20 -0400 (Tue, 12 Jul 2011)
New Revision: 2073
Modified:
trunk/README
trunk/code/qcommon/common.c
trunk/code/qcommon/qcommon.h
trunk/code/server/server.h
trunk/code/server/sv_client.c
trunk/code/server/sv_snapshot.c
Log:
- Greatly improve UDP downloading speed for clients
- Add download rate control cvar sv_dlRate
- Don't send snapshots to downloading clients
Modified: trunk/README
===================================================================
--- trunk/README 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/README 2011-07-12 11:01:20 UTC (rev 2073)
@@ -172,6 +172,8 @@
backend
s_muteWhenMinimized - mute sound when minimized
s_muteWhenUnfocused - mute sound when window is unfocused
+ sv_dlRate - bandwidth allotted to PK3 file downloads
+ via UDP, in kbyte/s
com_ansiColor - enable use of ANSI escape codes in the tty
com_altivec - enable use of altivec on PowerPC systems
@@ -500,6 +502,11 @@
that ioquake3 sets which is "ioQ3://{SERVER_IP}:{SERVER_PORT}". For,
example, Apache's mod_rewrite can restrict access based on HTTP_REFERER.
+ On a sidenote, downloading via UDP has been improved and yields higher data
+ rates now. You can configure the maximum bandwidth for UDP downloads via the
+ cvar sv_dlRate. Due to system-specific limits the download rate is capped
+ at about 1 Mbyte/s per client, so curl downloading may still be faster.
+
Using HTTP/FTP Download Support (Client)
Simply setting cl_allowDownload to 1 will enable HTTP/FTP downloads
assuming ioquake3 was compiled with USE_CURL=1 (the default).
Modified: trunk/code/qcommon/common.c
===================================================================
--- trunk/code/qcommon/common.c 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/code/qcommon/common.c 2011-07-12 11:01:20 UTC (rev 2073)
@@ -77,6 +77,7 @@
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;
@@ -2777,6 +2778,7 @@
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 );
@@ -3024,6 +3026,9 @@
int msec, minMsec;
int timeVal;
+ int numBlocks = 1;
+ int dlStart, deltaT, delayT;
+ static int dlNextRound = 0;
static int lastTime = 0, bias = 0;
int timeBeforeFirstEvents;
@@ -3084,14 +3089,71 @@
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;
if(msec >= minMsec)
Modified: trunk/code/qcommon/qcommon.h
===================================================================
--- trunk/code/qcommon/qcommon.h 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/code/qcommon/qcommon.h 2011-07-12 11:01:20 UTC (rev 2073)
@@ -192,8 +192,9 @@
#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
/*
@@ -1029,6 +1030,7 @@
void SV_PacketEvent( netadr_t from, msg_t *msg );
int SV_FrameMsec(void);
qboolean SV_GameCommand( void );
+int SV_SendDownloadMessages(void);
//
Modified: trunk/code/server/server.h
===================================================================
--- trunk/code/server/server.h 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/code/server/server.h 2011-07-12 11:01:20 UTC (rev 2073)
@@ -345,7 +345,7 @@
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 );
Modified: trunk/code/server/sv_client.c
===================================================================
--- trunk/code/server/sv_client.c 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/code/server/sv_client.c 2011-07-12 11:01:20 UTC (rev 2073)
@@ -852,21 +852,19 @@
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)
{
@@ -970,7 +968,7 @@
if(cl->download)
FS_FCloseFile(cl->download);
- return;
+ return 0;
}
Com_Printf( "clientDownload: %d : beginning \"%s\"\n", (int) (cl - svs.clients), cl->downloadName );
@@ -1015,81 +1013,85 @@
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;
- }
+ // 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 ( sv_minRate->integer ) {
- if ( sv_minRate->integer < 1000 )
- Cvar_Set( "sv_minRate", "1000" );
- if ( sv_minRate->integer > rate )
- rate = sv_minRate->integer;
- }
- 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
Modified: trunk/code/server/sv_snapshot.c
===================================================================
--- trunk/code/server/sv_snapshot.c 2011-07-12 00:34:25 UTC (rev 2072)
+++ trunk/code/server/sv_snapshot.c 2011-07-12 11:01:20 UTC (rev 2073)
@@ -647,9 +647,6 @@
// 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
@@ -683,6 +680,9 @@
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 ) {
@@ -696,4 +696,3 @@
SV_SendClientSnapshot( c );
}
}
-
More information about the quake3-commits
mailing list