[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