[quake3-commits] r2077 - in trunk/code: client qcommon server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Wed Jul 13 13:11:30 EDT 2011


Author: thilo
Date: 2011-07-13 13:11:30 -0400 (Wed, 13 Jul 2011)
New Revision: 2077

Modified:
   trunk/code/client/cl_input.c
   trunk/code/client/cl_main.c
   trunk/code/client/cl_net_chan.c
   trunk/code/client/cl_parse.c
   trunk/code/client/cl_scrn.c
   trunk/code/client/client.h
   trunk/code/qcommon/common.c
   trunk/code/qcommon/net_chan.c
   trunk/code/qcommon/qcommon.h
   trunk/code/server/server.h
   trunk/code/server/sv_client.c
   trunk/code/server/sv_init.c
   trunk/code/server/sv_main.c
   trunk/code/server/sv_net_chan.c
   trunk/code/server/sv_snapshot.c
Log:
- 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


Modified: trunk/code/client/cl_input.c
===================================================================
--- trunk/code/client/cl_input.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/cl_input.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -840,8 +840,6 @@
 			cl_voipSendTarget->modified = qfalse;
 		}
 
-		MSG_WriteByte (&buf, clc_EOF);  // placate legacy servers.
-		MSG_WriteByte (&buf, clc_extension);
 		MSG_WriteByte (&buf, clc_voip);
 		MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
 		MSG_WriteLong (&buf, clc.voipOutgoingSequence);
@@ -863,8 +861,6 @@
 			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);
@@ -928,16 +924,6 @@
 	}
 
 	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 );
-	}
 }
 
 /*

Modified: trunk/code/client/cl_main.c
===================================================================
--- trunk/code/client/cl_main.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/cl_main.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -235,7 +235,7 @@
 		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";
 	else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
 		reason = "running in single-player mode";
@@ -331,7 +331,7 @@
 		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.
@@ -1375,7 +1375,7 @@
 
 #ifdef USE_VOIP
 	// not connected to voip server anymore.
-	cl_connectedToVoipServer = qfalse;
+	clc.voipEnabled = qfalse;
 #endif
 
 	// Stop recording any video
@@ -4397,7 +4397,7 @@
 
 /*
 =================
-bool CL_CDKeyValidate
+CL_CDKeyValidate
 =================
 */
 qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {

Modified: trunk/code/client/cl_net_chan.c
===================================================================
--- trunk/code/client/cl_net_chan.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/cl_net_chan.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -24,6 +24,7 @@
 #include "../qcommon/qcommon.h"
 #include "client.h"
 
+#ifdef LEGACY_PROTOCOL
 /*
 ==============
 CL_Netchan_Encode
@@ -125,14 +126,22 @@
 		*(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;
 }
 
 /*
@@ -143,8 +152,18 @@
 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");
+	}
 }
 
 /*
@@ -159,7 +178,10 @@
 	if (!ret)
 		return qfalse;
 
-	CL_Netchan_Decode( msg );
+#ifdef LEGACY_PROTOCOL
+	if(chan->compat)
+		CL_Netchan_Decode(msg);
+#endif
 
 	return qtrue;
 }

Modified: trunk/code/client/cl_parse.c
===================================================================
--- trunk/code/client/cl_parse.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/cl_parse.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -34,7 +34,6 @@
 	"svc_download",
 	"svc_snapshot",
 	"svc_EOF",
-	"svc_extension",
 	"svc_voip",
 };
 
@@ -330,10 +329,6 @@
 int cl_connectedToPureServer;
 int cl_connectedToCheatServer;
 
-#ifdef USE_VOIP
-int cl_connectedToVoipServer;
-#endif
-
 /*
 ==================
 CL_SystemInfoChanged
@@ -363,15 +358,19 @@
 	}
 
 #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" );
-	if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
-		cl_connectedToVoipServer = qfalse;
+#ifdef LEGACY_PROTOCOL
+	if(clc.compat)
+		clc.voipEnabled = qfalse;
 	else
-		cl_connectedToVoipServer = (atoi( s ) == 1);
-
 #endif
+	{
+		s = Info_ValueForKey( systemInfo, "sv_voip" );
+		if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
+			clc.voipEnabled = qfalse;
+		else
+			clc.voipEnabled = atoi(s);
+	}
+#endif
 
 	s = Info_ValueForKey( systemInfo, "sv_cheats" );
 	cl_connectedToCheatServer = atoi( s );
@@ -865,19 +864,6 @@
 
 		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;

Modified: trunk/code/client/cl_scrn.c
===================================================================
--- trunk/code/client/cl_scrn.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/cl_scrn.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -362,7 +362,7 @@
 		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.

Modified: trunk/code/client/client.h
===================================================================
--- trunk/code/client/client.h	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/client/client.h	2011-07-13 17:11:30 UTC (rev 2077)
@@ -234,6 +234,7 @@
 	unsigned char	timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ];	// log of frame durations
 
 #ifdef USE_VOIP
+	qboolean voipEnabled;
 	qboolean speexInitialized;
 	int speexFrameSize;
 	int speexSampleRate;
@@ -517,7 +518,6 @@
 extern int cl_connectedToCheatServer;
 
 #ifdef USE_VOIP
-extern int cl_connectedToVoipServer;
 void CL_Voip_f( void );
 #endif
 
@@ -622,7 +622,6 @@
 // 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 );
 
 //

Modified: trunk/code/qcommon/common.c
===================================================================
--- trunk/code/qcommon/common.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/qcommon/common.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -3111,7 +3111,11 @@
 		{
 			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
@@ -3145,21 +3149,32 @@
 								// 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

Modified: trunk/code/qcommon/net_chan.c
===================================================================
--- trunk/code/qcommon/net_chan.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/qcommon/net_chan.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -99,93 +99,7 @@
 #endif
 }
 
-// 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
 
@@ -225,7 +139,11 @@
 	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"
@@ -298,6 +216,10 @@
 	// 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 ]

Modified: trunk/code/qcommon/qcommon.h
===================================================================
--- trunk/code/qcommon/qcommon.h	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/qcommon/qcommon.h	2011-07-13 17:11:30 UTC (rev 2077)
@@ -227,6 +227,8 @@
 	byte		unsentBuffer[MAX_MSGLEN];
 
 	int			challenge;
+	int		lastSentTime;
+	int		lastSentSize;
 
 #ifdef LEGACY_PROTOCOL
 	qboolean	compat;
@@ -250,7 +252,7 @@
 ==============================================================
 */
 
-#define	PROTOCOL_VERSION	69
+#define	PROTOCOL_VERSION	70
 #define PROTOCOL_LEGACY_VERSION	68
 // 1.31 - 67
 
@@ -296,9 +298,7 @@
 	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.
 };
 
@@ -314,9 +314,7 @@
 	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.
 };
 
@@ -1042,8 +1040,8 @@
 int SV_FrameMsec(void);
 qboolean SV_GameCommand( void );
 int SV_SendDownloadMessages(void);
+int SV_SendQueuedMessages(void);
 
-
 //
 // UI interface
 //

Modified: trunk/code/server/server.h
===================================================================
--- trunk/code/server/server.h	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/server.h	2011-07-13 17:11:30 UTC (rev 2077)
@@ -34,6 +34,8 @@
 #define	MAX_ENT_CLUSTERS	16
 
 #ifdef USE_VOIP
+#define VOIP_QUEUE_LENGTH 64
+
 typedef struct voipServerPacket_s
 {
 	int generation;
@@ -162,7 +164,7 @@
 	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
@@ -183,8 +185,9 @@
 	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;
@@ -311,6 +314,7 @@
 
 
 void SV_MasterShutdown (void);
+int SV_RateMsec(client_t *client);
 
 
 
@@ -460,6 +464,6 @@
 // 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 );
 

Modified: trunk/code/server/sv_client.c
===================================================================
--- trunk/code/server/sv_client.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/sv_client.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -557,7 +557,7 @@
 	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;
 	
@@ -758,7 +758,7 @@
 	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));
@@ -797,7 +797,7 @@
 	// 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;
 		}
 	}
@@ -1017,7 +1017,7 @@
 		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 );
 
@@ -1086,9 +1086,40 @@
 
 /*
 ==================
+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
 ==================
 */
 
@@ -1099,25 +1130,22 @@
 	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;
 			}
 		}
 	}
@@ -1135,43 +1163,41 @@
 */
 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;
 
-	// !!! FIXME: I hate this queue system.
-	cl->queuedVoipPackets -= i;
-	if (cl->queuedVoipPackets > 0) {
-		memmove( &cl->voipPacket[0], &cl->voipPacket[i],
-		         sizeof (voipServerPacket_t) * i);
+			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);
+		}
+
+		cl->queuedVoipPackets -= i;
+		cl->queuedVoipIndex += i;
+		cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket);
 	}
 }
 #endif
@@ -1343,7 +1369,7 @@
 		} 
 		else {
 			cl->pureAuthentic = 0;
-			cl->nextSnapshotTime = -1;
+			cl->lastSnapshotTime = 0;
 			cl->state = CS_ACTIVE;
 			SV_SendClientSnapshot( cl );
 			SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" );
@@ -1408,24 +1434,39 @@
 
 	// 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
 	// maintain the IP information
@@ -1759,7 +1800,7 @@
 	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;
@@ -1833,13 +1874,15 @@
 			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++;
 	}
 }
@@ -1932,18 +1975,6 @@
 	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;
 		}

Modified: trunk/code/server/sv_init.c
===================================================================
--- trunk/code/server/sv_init.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/sv_init.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -554,7 +554,7 @@
 					client->gentity = ent;
 
 					client->deltaMessage = -1;
-					client->nextSnapshotTime = svs.time;	// generate a snapshot immediately
+					client->lastSnapshotTime = 0;	// generate a snapshot immediately
 
 					VM_Call( gvm, GAME_CLIENT_BEGIN, i );
 				}
@@ -723,7 +723,7 @@
 					SV_SendServerCommand( cl, "disconnect \"%s\"", message );
 				}
 				// force a snapshot to be sent
-				cl->nextSnapshotTime = -1;
+				cl->lastSnapshotTime = 0;
 				SV_SendClientSnapshot( cl );
 			}
 		}

Modified: trunk/code/server/sv_main.c
===================================================================
--- trunk/code/server/sv_main.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/sv_main.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -1156,5 +1156,52 @@
 	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;
+}

Modified: trunk/code/server/sv_net_chan.c
===================================================================
--- trunk/code/server/sv_net_chan.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/sv_net_chan.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -24,6 +24,7 @@
 #include "../qcommon/qcommon.h"
 #include "server.h"
 
+#ifdef LEGACY_PROTOCOL
 /*
 ==============
 SV_Netchan_Encode
@@ -127,38 +128,63 @@
 		*(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;
 }
 
 
@@ -173,22 +199,28 @@
 ================
 */
 
-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 );
 	}
 }
@@ -203,7 +235,12 @@
 	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;
 }
 

Modified: trunk/code/server/sv_snapshot.c
===================================================================
--- trunk/code/server/sv_snapshot.c	2011-07-13 08:40:30 UTC (rev 2076)
+++ trunk/code/server/sv_snapshot.c	2011-07-13 17:11:30 UTC (rev 2077)
@@ -521,94 +521,22 @@
 	}
 }
 
-
 /*
-====================
-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
 
 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);
 }
 
 
@@ -666,33 +594,47 @@
 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;
 	}
 }



More information about the quake3-commits mailing list