summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThilo Schulz <arny@ats.s.bawue.de>2011-07-13 17:11:30 +0000
committerTim Angus <tim@ngus.net>2013-01-10 22:27:30 +0000
commit2bf2118d79994cfd805c3ffb5b421c6a65658b84 (patch)
tree867a9c4ac0d975e7aca4ee20714de8c97646ccde /src
parent5038ffeb97413a867d0bdadf761e2ba727781742 (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')
-rw-r--r--src/client/cl_input.c54
-rw-r--r--src/client/cl_main.c6
-rw-r--r--src/client/cl_net_chan.c32
-rw-r--r--src/client/cl_parse.c22
-rw-r--r--src/client/cl_scrn.c2
-rw-r--r--src/client/client.h3
-rw-r--r--src/qcommon/common.c21
-rw-r--r--src/qcommon/net_chan.c96
-rw-r--r--src/qcommon/qcommon.h12
-rw-r--r--src/server/server.h10
-rw-r--r--src/server/sv_client.c183
-rw-r--r--src/server/sv_init.c2
-rw-r--r--src/server/sv_main.c49
-rw-r--r--src/server/sv_net_chan.c103
-rw-r--r--src/server/sv_snapshot.c126
15 files changed, 372 insertions, 349 deletions
diff --git a/src/client/cl_input.c b/src/client/cl_input.c
index f9ec9d04..736378ba 100644
--- a/src/client/cl_input.c
+++ b/src/client/cl_input.c
@@ -801,8 +801,46 @@ void CL_WritePacket( void ) {
#ifdef USE_VOIP
if (clc.voipOutgoingDataSize > 0) { // only send if data.
- MSG_WriteByte (&buf, clc_EOF); // placate legacy servers.
- MSG_WriteByte (&buf, clc_extension);
+ // Move cl_voipSendTarget from a string to the bitmasks if needed.
+ if (cl_voipSendTarget->modified) {
+ char buffer[32];
+ const char *target = cl_voipSendTarget->string;
+
+ if (Q_stricmp(target, "attacker") == 0) {
+ int player = VM_Call( cgvm, CG_LAST_ATTACKER );
+ Com_sprintf(buffer, sizeof (buffer), "%d", player);
+ target = buffer;
+ } else if (Q_stricmp(target, "crosshair") == 0) {
+ int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
+ Com_sprintf(buffer, sizeof (buffer), "%d", player);
+ target = buffer;
+ }
+
+ if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) {
+ const int all = 0x7FFFFFFF;
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all;
+ } else if (Q_stricmp(target, "none") == 0) {
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
+ } else {
+ const char *ptr = target;
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
+ do {
+ if ((*ptr == ',') || (*ptr == '\0')) {
+ const int val = atoi(target);
+ target = ptr + 1;
+ if ((val >= 0) && (val < 31)) {
+ clc.voipTarget1 |= (1 << (val-0));
+ } else if ((val >= 31) && (val < 62)) {
+ clc.voipTarget2 |= (1 << (val-31));
+ } else if ((val >= 62) && (val < 93)) {
+ clc.voipTarget3 |= (1 << (val-62));
+ }
+ }
+ } while (*(ptr++));
+ }
+ cl_voipSendTarget->modified = qfalse;
+ }
+
MSG_WriteByte (&buf, clc_voip);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
@@ -824,8 +862,6 @@ void CL_WritePacket( void ) {
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);
@@ -889,16 +925,6 @@ void CL_WritePacket( void ) {
}
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 );
- }
}
/*
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index 621d3cd6..1f95b7e0 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -289,7 +289,7 @@ void CL_Voip_f( void )
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";
if (reason != NULL) {
@@ -383,7 +383,7 @@ void CL_CaptureVoip(void)
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.
@@ -1429,7 +1429,7 @@ void CL_Disconnect( qboolean showMainMenu ) {
#ifdef USE_VOIP
// not connected to voip server anymore.
- cl_connectedToVoipServer = qfalse;
+ clc.voipEnabled = qfalse;
#endif
// Stop recording any video
diff --git a/src/client/cl_net_chan.c b/src/client/cl_net_chan.c
index fbfc4dc9..0d057d1d 100644
--- a/src/client/cl_net_chan.c
+++ b/src/client/cl_net_chan.c
@@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../qcommon/qcommon.h"
#include "client.h"
+#ifdef LEGACY_PROTOCOL
/*
==============
CL_Netchan_Encode
@@ -126,14 +127,22 @@ static void CL_Netchan_Decode( msg_t *msg ) {
*(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;
}
/*
@@ -144,8 +153,18 @@ CL_Netchan_Transmit
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");
+ }
}
/*
@@ -160,7 +179,10 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
if (!ret)
return qfalse;
- CL_Netchan_Decode( msg );
+#ifdef LEGACY_PROTOCOL
+ if(chan->compat)
+ CL_Netchan_Decode(msg);
+#endif
return qtrue;
}
diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c
index 1b0e2802..147c3156 100644
--- a/src/client/cl_parse.c
+++ b/src/client/cl_parse.c
@@ -35,7 +35,6 @@ char *svc_strings[256] = {
"svc_download",
"svc_snapshot",
"svc_EOF",
- "svc_extension",
"svc_voip",
};
@@ -331,10 +330,6 @@ void CL_ParseSnapshot( msg_t *msg ) {
int cl_connectedToPureServer;
int cl_connectedToCheatServer;
-#ifdef USE_VOIP
-int cl_connectedToVoipServer;
-#endif
-
/*
==================
CL_SystemInfoChanged
@@ -364,10 +359,8 @@ void CL_SystemInfoChanged( void ) {
}
#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" );
- cl_connectedToVoipServer = (atoi( s ) == 1);
+ clc.voipEnabled = atoi(s);
#endif
s = Info_ValueForKey( systemInfo, "sv_cheats" );
@@ -862,19 +855,6 @@ void CL_ParseServerMessage( msg_t *msg ) {
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;
diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c
index c616640a..afafea25 100644
--- a/src/client/cl_scrn.c
+++ b/src/client/cl_scrn.c
@@ -343,7 +343,7 @@ void SCR_DrawVoipMeter( void ) {
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.
diff --git a/src/client/client.h b/src/client/client.h
index 3e243df2..3db4e27f 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -237,6 +237,7 @@ typedef struct {
unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations
#ifdef USE_VOIP
+ qboolean voipEnabled;
qboolean speexInitialized;
int speexFrameSize;
int speexSampleRate;
@@ -521,7 +522,6 @@ extern int cl_connectedToPureServer;
extern int cl_connectedToCheatServer;
#ifdef USE_VOIP
-extern int cl_connectedToVoipServer;
void CL_Voip_f( void );
#endif
@@ -627,7 +627,6 @@ void LAN_SaveServersToCache( void );
// 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 );
//
diff --git a/src/qcommon/common.c b/src/qcommon/common.c
index da0e8967..8732eab6 100644
--- a/src/qcommon/common.c
+++ b/src/qcommon/common.c
@@ -2948,7 +2948,11 @@ void Com_Frame( void ) {
{
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
@@ -2982,21 +2986,32 @@ void Com_Frame( void ) {
// 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
diff --git a/src/qcommon/net_chan.c b/src/qcommon/net_chan.c
index d9c6760b..8f2d8de7 100644
--- a/src/qcommon/net_chan.c
+++ b/src/qcommon/net_chan.c
@@ -96,92 +96,6 @@ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int
chan->challenge = challenge;
}
-// 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
@@ -219,7 +133,11 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) {
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"
@@ -289,6 +207,10 @@ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
// 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 ]
diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h
index 28834168..49501d52 100644
--- a/src/qcommon/qcommon.h
+++ b/src/qcommon/qcommon.h
@@ -225,6 +225,8 @@ typedef struct {
byte unsentBuffer[MAX_MSGLEN];
int challenge;
+ int lastSentTime;
+ int lastSentSize;
} netchan_t;
void Netchan_Init( int qport );
@@ -277,9 +279,7 @@ enum svc_ops_e {
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.
};
@@ -295,9 +295,7 @@ enum clc_ops_e {
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.
};
@@ -1013,7 +1011,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg );
int SV_FrameMsec(void);
qboolean SV_GameCommand( void );
int SV_SendDownloadMessages(void);
-
+int SV_SendQueuedMessages(void);
//
// UI interface
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;
}
}