mirror of
https://github.com/UberGames/ioef.git
synced 2024-11-30 16:01:46 +00:00
- 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
This commit is contained in:
parent
a844c94af1
commit
ac30d86db0
15 changed files with 345 additions and 356 deletions
|
@ -840,8 +840,6 @@ void CL_WritePacket( void ) {
|
||||||
cl_voipSendTarget->modified = qfalse;
|
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_voip);
|
||||||
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
|
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
|
||||||
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
|
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
|
||||||
|
@ -863,8 +861,6 @@ void CL_WritePacket( void ) {
|
||||||
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
|
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
|
||||||
MSG_Bitstream (&fakemsg);
|
MSG_Bitstream (&fakemsg);
|
||||||
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
|
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
|
||||||
MSG_WriteByte (&fakemsg, svc_EOF);
|
|
||||||
MSG_WriteByte (&fakemsg, svc_extension);
|
|
||||||
MSG_WriteByte (&fakemsg, svc_voip);
|
MSG_WriteByte (&fakemsg, svc_voip);
|
||||||
MSG_WriteShort (&fakemsg, clc.clientNum);
|
MSG_WriteShort (&fakemsg, clc.clientNum);
|
||||||
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
|
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
|
||||||
|
@ -928,16 +924,6 @@ void CL_WritePacket( void ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CL_Netchan_Transmit (&clc.netchan, &buf);
|
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -235,7 +235,7 @@ void CL_Voip_f( void )
|
||||||
reason = "Not connected to a server";
|
reason = "Not connected to a server";
|
||||||
else if (!clc.speexInitialized)
|
else if (!clc.speexInitialized)
|
||||||
reason = "Speex not initialized";
|
reason = "Speex not initialized";
|
||||||
else if (!cl_connectedToVoipServer)
|
else if (!clc.voipEnabled)
|
||||||
reason = "Server doesn't support VoIP";
|
reason = "Server doesn't support VoIP";
|
||||||
else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
|
else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
|
||||||
reason = "running in single-player mode";
|
reason = "running in single-player mode";
|
||||||
|
@ -331,7 +331,7 @@ void CL_CaptureVoip(void)
|
||||||
qboolean dontCapture = qfalse;
|
qboolean dontCapture = qfalse;
|
||||||
if (clc.state != CA_ACTIVE)
|
if (clc.state != CA_ACTIVE)
|
||||||
dontCapture = qtrue; // not connected to a server.
|
dontCapture = qtrue; // not connected to a server.
|
||||||
else if (!cl_connectedToVoipServer)
|
else if (!clc.voipEnabled)
|
||||||
dontCapture = qtrue; // server doesn't support VoIP.
|
dontCapture = qtrue; // server doesn't support VoIP.
|
||||||
else if (clc.demoplaying)
|
else if (clc.demoplaying)
|
||||||
dontCapture = qtrue; // playing back a demo.
|
dontCapture = qtrue; // playing back a demo.
|
||||||
|
@ -1375,7 +1375,7 @@ void CL_Disconnect( qboolean showMainMenu ) {
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
// not connected to voip server anymore.
|
// not connected to voip server anymore.
|
||||||
cl_connectedToVoipServer = qfalse;
|
clc.voipEnabled = qfalse;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Stop recording any video
|
// Stop recording any video
|
||||||
|
@ -4397,7 +4397,7 @@ void CL_ShowIP_f(void) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
bool CL_CDKeyValidate
|
CL_CDKeyValidate
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
|
qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#include "../qcommon/qcommon.h"
|
#include "../qcommon/qcommon.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
|
||||||
|
#ifdef LEGACY_PROTOCOL
|
||||||
/*
|
/*
|
||||||
==============
|
==============
|
||||||
CL_Netchan_Encode
|
CL_Netchan_Encode
|
||||||
|
@ -125,14 +126,22 @@ static void CL_Netchan_Decode( msg_t *msg ) {
|
||||||
*(msg->data + i) = *(msg->data + i) ^ key;
|
*(msg->data + i) = *(msg->data + i) ^ key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
CL_Netchan_TransmitNextFragment
|
CL_Netchan_TransmitNextFragment
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
void CL_Netchan_TransmitNextFragment( netchan_t *chan ) {
|
qboolean CL_Netchan_TransmitNextFragment(netchan_t *chan)
|
||||||
Netchan_TransmitNextFragment( chan );
|
{
|
||||||
|
if(chan->unsentFragments)
|
||||||
|
{
|
||||||
|
Netchan_TransmitNextFragment(chan);
|
||||||
|
return qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -143,8 +152,18 @@ CL_Netchan_Transmit
|
||||||
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
|
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
|
||||||
MSG_WriteByte( msg, clc_EOF );
|
MSG_WriteByte( msg, clc_EOF );
|
||||||
|
|
||||||
CL_Netchan_Encode( msg );
|
#ifdef LEGACY_PROTOCOL
|
||||||
Netchan_Transmit( chan, msg->cursize, msg->data );
|
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 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return qfalse;
|
return qfalse;
|
||||||
|
|
||||||
CL_Netchan_Decode( msg );
|
#ifdef LEGACY_PROTOCOL
|
||||||
|
if(chan->compat)
|
||||||
|
CL_Netchan_Decode(msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
return qtrue;
|
return qtrue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ char *svc_strings[256] = {
|
||||||
"svc_download",
|
"svc_download",
|
||||||
"svc_snapshot",
|
"svc_snapshot",
|
||||||
"svc_EOF",
|
"svc_EOF",
|
||||||
"svc_extension",
|
|
||||||
"svc_voip",
|
"svc_voip",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,10 +329,6 @@ void CL_ParseSnapshot( msg_t *msg ) {
|
||||||
int cl_connectedToPureServer;
|
int cl_connectedToPureServer;
|
||||||
int cl_connectedToCheatServer;
|
int cl_connectedToCheatServer;
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
|
||||||
int cl_connectedToVoipServer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==================
|
==================
|
||||||
CL_SystemInfoChanged
|
CL_SystemInfoChanged
|
||||||
|
@ -363,14 +358,18 @@ void CL_SystemInfoChanged( void ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
// in the future, (val) will be a protocol version string, so only
|
#ifdef LEGACY_PROTOCOL
|
||||||
// accept explicitly 1, not generally non-zero.
|
if(clc.compat)
|
||||||
|
clc.voipEnabled = qfalse;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
s = Info_ValueForKey( systemInfo, "sv_voip" );
|
s = Info_ValueForKey( systemInfo, "sv_voip" );
|
||||||
if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
|
if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
|
||||||
cl_connectedToVoipServer = qfalse;
|
clc.voipEnabled = qfalse;
|
||||||
else
|
else
|
||||||
cl_connectedToVoipServer = (atoi( s ) == 1);
|
clc.voipEnabled = atoi(s);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s = Info_ValueForKey( systemInfo, "sv_cheats" );
|
s = Info_ValueForKey( systemInfo, "sv_cheats" );
|
||||||
|
@ -865,19 +864,6 @@ void CL_ParseServerMessage( msg_t *msg ) {
|
||||||
|
|
||||||
cmd = MSG_ReadByte( 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) {
|
if (cmd == svc_EOF) {
|
||||||
SHOWNET( msg, "END OF MESSAGE" );
|
SHOWNET( msg, "END OF MESSAGE" );
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -362,7 +362,7 @@ void SCR_DrawVoipMeter( void ) {
|
||||||
return; // not recording at the moment.
|
return; // not recording at the moment.
|
||||||
else if (clc.state != CA_ACTIVE)
|
else if (clc.state != CA_ACTIVE)
|
||||||
return; // not connected to a server.
|
return; // not connected to a server.
|
||||||
else if (!cl_connectedToVoipServer)
|
else if (!clc.voipEnabled)
|
||||||
return; // server doesn't support VoIP.
|
return; // server doesn't support VoIP.
|
||||||
else if (clc.demoplaying)
|
else if (clc.demoplaying)
|
||||||
return; // playing back a demo.
|
return; // playing back a demo.
|
||||||
|
|
|
@ -234,6 +234,7 @@ typedef struct {
|
||||||
unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations
|
unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
|
qboolean voipEnabled;
|
||||||
qboolean speexInitialized;
|
qboolean speexInitialized;
|
||||||
int speexFrameSize;
|
int speexFrameSize;
|
||||||
int speexSampleRate;
|
int speexSampleRate;
|
||||||
|
@ -517,7 +518,6 @@ extern int cl_connectedToPureServer;
|
||||||
extern int cl_connectedToCheatServer;
|
extern int cl_connectedToCheatServer;
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
extern int cl_connectedToVoipServer;
|
|
||||||
void CL_Voip_f( void );
|
void CL_Voip_f( void );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -622,7 +622,6 @@ void LAN_SaveServersToCache( void );
|
||||||
// cl_net_chan.c
|
// cl_net_chan.c
|
||||||
//
|
//
|
||||||
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data );
|
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 );
|
qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg );
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -3111,7 +3111,11 @@ void Com_Frame( void ) {
|
||||||
{
|
{
|
||||||
if(com_sv_running->integer)
|
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)
|
if(sv_dlRate->integer)
|
||||||
{
|
{
|
||||||
// Rate limiting. This is very imprecise for high
|
// Rate limiting. This is very imprecise for high
|
||||||
|
@ -3145,20 +3149,31 @@ void Com_Frame( void ) {
|
||||||
// all of the bandwidth. This will result in an
|
// all of the bandwidth. This will result in an
|
||||||
// effective maximum rate of 1MB/s per user, but the
|
// effective maximum rate of 1MB/s per user, but the
|
||||||
// low download window size limits this anyways.
|
// low download window size limits this anyways.
|
||||||
|
if(timeVal > 2)
|
||||||
timeVal = 2;
|
timeVal = 2;
|
||||||
|
|
||||||
dlNextRound = dlStart + deltaT + 1;
|
dlNextRound = dlStart + deltaT + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dlNextRound = dlStart + delayT;
|
dlNextRound = dlStart + delayT;
|
||||||
timeVal = delayT - deltaT;
|
delayT -= deltaT;
|
||||||
|
|
||||||
|
if(delayT < timeVal)
|
||||||
|
timeVal = delayT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
SV_SendDownloadMessages();
|
SV_SendDownloadMessages();
|
||||||
|
timeVal = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timeVal == 0)
|
||||||
|
timeVal = 1;
|
||||||
|
|
||||||
if(com_busyWait->integer)
|
if(com_busyWait->integer)
|
||||||
NET_Sleep(0);
|
NET_Sleep(0);
|
||||||
|
|
|
@ -99,92 +99,6 @@ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int
|
||||||
#endif
|
#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
|
Netchan_TransmitNextFragment
|
||||||
|
@ -225,7 +139,11 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) {
|
||||||
MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength );
|
MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength );
|
||||||
|
|
||||||
// send the datagram
|
// 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 ) {
|
if ( showpackets->integer ) {
|
||||||
Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n"
|
Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n"
|
||||||
|
@ -298,6 +216,10 @@ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
|
||||||
// send the datagram
|
// 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 ) {
|
if ( showpackets->integer ) {
|
||||||
Com_Printf( "%s send %4i : s=%i ack=%i\n"
|
Com_Printf( "%s send %4i : s=%i ack=%i\n"
|
||||||
, netsrcString[ chan->sock ]
|
, netsrcString[ chan->sock ]
|
||||||
|
|
|
@ -227,6 +227,8 @@ typedef struct {
|
||||||
byte unsentBuffer[MAX_MSGLEN];
|
byte unsentBuffer[MAX_MSGLEN];
|
||||||
|
|
||||||
int challenge;
|
int challenge;
|
||||||
|
int lastSentTime;
|
||||||
|
int lastSentSize;
|
||||||
|
|
||||||
#ifdef LEGACY_PROTOCOL
|
#ifdef LEGACY_PROTOCOL
|
||||||
qboolean compat;
|
qboolean compat;
|
||||||
|
@ -250,7 +252,7 @@ PROTOCOL
|
||||||
==============================================================
|
==============================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 69
|
#define PROTOCOL_VERSION 70
|
||||||
#define PROTOCOL_LEGACY_VERSION 68
|
#define PROTOCOL_LEGACY_VERSION 68
|
||||||
// 1.31 - 67
|
// 1.31 - 67
|
||||||
|
|
||||||
|
@ -296,9 +298,7 @@ enum svc_ops_e {
|
||||||
svc_snapshot,
|
svc_snapshot,
|
||||||
svc_EOF,
|
svc_EOF,
|
||||||
|
|
||||||
// svc_extension follows a svc_EOF, followed by another svc_* ...
|
// new commands, supported only by ioquake3 protocol but not legacy
|
||||||
// this keeps legacy clients compatible.
|
|
||||||
svc_extension,
|
|
||||||
svc_voip, // not wrapped in USE_VOIP, so this value is reserved.
|
svc_voip, // not wrapped in USE_VOIP, so this value is reserved.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -314,9 +314,7 @@ enum clc_ops_e {
|
||||||
clc_clientCommand, // [string] message
|
clc_clientCommand, // [string] message
|
||||||
clc_EOF,
|
clc_EOF,
|
||||||
|
|
||||||
// clc_extension follows a clc_EOF, followed by another clc_* ...
|
// new commands, supported only by ioquake3 protocol but not legacy
|
||||||
// this keeps legacy servers compatible.
|
|
||||||
clc_extension,
|
|
||||||
clc_voip, // not wrapped in USE_VOIP, so this value is reserved.
|
clc_voip, // not wrapped in USE_VOIP, so this value is reserved.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1042,7 +1040,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg );
|
||||||
int SV_FrameMsec(void);
|
int SV_FrameMsec(void);
|
||||||
qboolean SV_GameCommand( void );
|
qboolean SV_GameCommand( void );
|
||||||
int SV_SendDownloadMessages(void);
|
int SV_SendDownloadMessages(void);
|
||||||
|
int SV_SendQueuedMessages(void);
|
||||||
|
|
||||||
//
|
//
|
||||||
// UI interface
|
// UI interface
|
||||||
|
|
|
@ -34,6 +34,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#define MAX_ENT_CLUSTERS 16
|
#define MAX_ENT_CLUSTERS 16
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
|
#define VOIP_QUEUE_LENGTH 64
|
||||||
|
|
||||||
typedef struct voipServerPacket_s
|
typedef struct voipServerPacket_s
|
||||||
{
|
{
|
||||||
int generation;
|
int generation;
|
||||||
|
@ -162,7 +164,7 @@ typedef struct client_s {
|
||||||
int nextReliableTime; // svs.time when another reliable command will be allowed
|
int nextReliableTime; // svs.time when another reliable command will be allowed
|
||||||
int lastPacketTime; // svs.time when packet was last received
|
int lastPacketTime; // svs.time when packet was last received
|
||||||
int lastConnectTime; // svs.time when connection started
|
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
|
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
|
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
|
clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here
|
||||||
|
@ -183,8 +185,9 @@ typedef struct client_s {
|
||||||
qboolean hasVoip;
|
qboolean hasVoip;
|
||||||
qboolean muteAllVoip;
|
qboolean muteAllVoip;
|
||||||
qboolean ignoreVoipFromClient[MAX_CLIENTS];
|
qboolean ignoreVoipFromClient[MAX_CLIENTS];
|
||||||
voipServerPacket_t voipPacket[64]; // !!! FIXME: WAY too much memory!
|
voipServerPacket_t *voipPacket[VOIP_QUEUE_LENGTH];
|
||||||
int queuedVoipPackets;
|
int queuedVoipPackets;
|
||||||
|
int queuedVoipIndex;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int oldServerTime;
|
int oldServerTime;
|
||||||
|
@ -311,6 +314,7 @@ void SV_RemoveOperatorCommands (void);
|
||||||
|
|
||||||
|
|
||||||
void SV_MasterShutdown (void);
|
void SV_MasterShutdown (void);
|
||||||
|
int SV_RateMsec(client_t *client);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -460,6 +464,6 @@ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, con
|
||||||
// sv_net_chan.c
|
// sv_net_chan.c
|
||||||
//
|
//
|
||||||
void SV_Netchan_Transmit( client_t *client, msg_t *msg);
|
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 );
|
qboolean SV_Netchan_Process( client_t *client, msg_t *msg );
|
||||||
|
|
||||||
|
|
|
@ -557,7 +557,7 @@ gotnewcl:
|
||||||
Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );
|
Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );
|
||||||
|
|
||||||
newcl->state = CS_CONNECTED;
|
newcl->state = CS_CONNECTED;
|
||||||
newcl->nextSnapshotTime = svs.time;
|
newcl->lastSnapshotTime = 0;
|
||||||
newcl->lastPacketTime = svs.time;
|
newcl->lastPacketTime = svs.time;
|
||||||
newcl->lastConnectTime = svs.time;
|
newcl->lastConnectTime = svs.time;
|
||||||
|
|
||||||
|
@ -758,7 +758,7 @@ void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) {
|
||||||
client->gentity = ent;
|
client->gentity = ent;
|
||||||
|
|
||||||
client->deltaMessage = -1;
|
client->deltaMessage = -1;
|
||||||
client->nextSnapshotTime = svs.time; // generate a snapshot immediately
|
client->lastSnapshotTime = 0; // generate a snapshot immediately
|
||||||
|
|
||||||
if(cmd)
|
if(cmd)
|
||||||
memcpy(&client->lastUsercmd, cmd, sizeof(client->lastUsercmd));
|
memcpy(&client->lastUsercmd, cmd, sizeof(client->lastUsercmd));
|
||||||
|
@ -797,7 +797,7 @@ static void SV_CloseDownload( client_t *cl ) {
|
||||||
// Free the temporary buffer space
|
// Free the temporary buffer space
|
||||||
for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) {
|
for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) {
|
||||||
if (cl->downloadBlocks[i]) {
|
if (cl->downloadBlocks[i]) {
|
||||||
Z_Free( cl->downloadBlocks[i] );
|
Hunk_FreeTempMemory(cl->downloadBlocks[i]);
|
||||||
cl->downloadBlocks[i] = NULL;
|
cl->downloadBlocks[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1017,7 +1017,7 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg)
|
||||||
curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW);
|
curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW);
|
||||||
|
|
||||||
if (!cl->downloadBlocks[curindex])
|
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 );
|
cl->downloadBlockSize[curindex] = FS_Read( cl->downloadBlocks[curindex], MAX_DOWNLOAD_BLKSIZE, cl->download );
|
||||||
|
|
||||||
|
@ -1084,11 +1084,42 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
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
|
SV_SendDownloadMessages
|
||||||
|
|
||||||
Send download messages to all clients
|
Send one round of download messages to all clients
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1099,13 +1130,11 @@ int SV_SendDownloadMessages(void)
|
||||||
msg_t msg;
|
msg_t msg;
|
||||||
byte msgBuffer[MAX_MSGLEN];
|
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->state && *cl->downloadName)
|
||||||
{
|
|
||||||
if(cl->netchan.unsentFragments)
|
|
||||||
SV_Netchan_TransmitNextFragment(cl);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
MSG_Init(&msg, msgBuffer, sizeof(msgBuffer));
|
MSG_Init(&msg, msgBuffer, sizeof(msgBuffer));
|
||||||
MSG_WriteLong(&msg, cl->lastClientCommand);
|
MSG_WriteLong(&msg, cl->lastClientCommand);
|
||||||
|
@ -1120,7 +1149,6 @@ int SV_SendDownloadMessages(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return numDLs;
|
return numDLs;
|
||||||
}
|
}
|
||||||
|
@ -1135,43 +1163,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 )
|
void SV_WriteVoipToClient( client_t *cl, msg_t *msg )
|
||||||
{
|
{
|
||||||
voipServerPacket_t *packet = &cl->voipPacket[0];
|
if(*cl->downloadName)
|
||||||
int totalbytes = 0;
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
if (*cl->downloadName) {
|
|
||||||
cl->queuedVoipPackets = 0;
|
cl->queuedVoipPackets = 0;
|
||||||
return; // no VoIP allowed if download is going, to save bandwidth.
|
return; // no VoIP allowed if download is going, to save bandwidth.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(cl->queuedVoipPackets)
|
||||||
|
{
|
||||||
|
int totalbytes = 0;
|
||||||
|
int i;
|
||||||
|
voipServerPacket_t *packet;
|
||||||
|
|
||||||
// Write as many VoIP packets as we reasonably can...
|
// Write as many VoIP packets as we reasonably can...
|
||||||
for (i = 0; i < cl->queuedVoipPackets; i++, packet++) {
|
for(i = cl->queuedVoipIndex; i < cl->queuedVoipPackets; i++)
|
||||||
|
{
|
||||||
|
packet = cl->voipPacket[i % ARRAY_LEN(cl->voipPacket)];
|
||||||
|
|
||||||
totalbytes += packet->len;
|
totalbytes += packet->len;
|
||||||
if (totalbytes > MAX_DOWNLOAD_BLKSIZE)
|
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// You have to start with a svc_EOF, so legacy clients drop the
|
MSG_WriteByte(msg, svc_voip);
|
||||||
// rest of this packet. Otherwise, those without VoIP support will
|
MSG_WriteShort(msg, packet->sender);
|
||||||
// see the svc_voip command, then panic and disconnect.
|
MSG_WriteByte(msg, (byte) packet->generation);
|
||||||
// Generally we don't send VoIP packets to legacy clients, but this
|
MSG_WriteLong(msg, packet->sequence);
|
||||||
// serves as both a safety measure and a means to keep demo files
|
MSG_WriteByte(msg, packet->frames);
|
||||||
// compatible.
|
MSG_WriteShort(msg, packet->len);
|
||||||
MSG_WriteByte( msg, svc_EOF );
|
MSG_WriteData(msg, packet->data, packet->len);
|
||||||
MSG_WriteByte( msg, svc_extension );
|
|
||||||
MSG_WriteByte( msg, svc_voip );
|
Hunk_FreeTempMemory(packet);
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// !!! FIXME: I hate this queue system.
|
|
||||||
cl->queuedVoipPackets -= i;
|
cl->queuedVoipPackets -= i;
|
||||||
if (cl->queuedVoipPackets > 0) {
|
cl->queuedVoipIndex += i;
|
||||||
memmove( &cl->voipPacket[0], &cl->voipPacket[i],
|
cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket);
|
||||||
sizeof (voipServerPacket_t) * i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1343,7 +1369,7 @@ static void SV_VerifyPaks_f( client_t *cl ) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cl->pureAuthentic = 0;
|
cl->pureAuthentic = 0;
|
||||||
cl->nextSnapshotTime = -1;
|
cl->lastSnapshotTime = 0;
|
||||||
cl->state = CS_ACTIVE;
|
cl->state = CS_ACTIVE;
|
||||||
SV_SendClientSnapshot( cl );
|
SV_SendClientSnapshot( cl );
|
||||||
SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" );
|
SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" );
|
||||||
|
@ -1408,23 +1434,38 @@ void SV_UserinfoChanged( client_t *cl ) {
|
||||||
|
|
||||||
// snaps command
|
// snaps command
|
||||||
val = Info_ValueForKey (cl->userinfo, "snaps");
|
val = Info_ValueForKey (cl->userinfo, "snaps");
|
||||||
if (strlen(val)) {
|
|
||||||
|
if(strlen(val))
|
||||||
|
{
|
||||||
i = atoi(val);
|
i = atoi(val);
|
||||||
if ( i < 1 ) {
|
|
||||||
|
if(i < 1)
|
||||||
i = 1;
|
i = 1;
|
||||||
} else if ( i > sv_fps->integer ) {
|
else if(i > sv_fps->integer)
|
||||||
i = sv_fps->integer;
|
i = sv_fps->integer;
|
||||||
|
|
||||||
|
i = 1000 / i;
|
||||||
}
|
}
|
||||||
cl->snapshotMsec = 1000/i;
|
else
|
||||||
} else {
|
i = 50;
|
||||||
cl->snapshotMsec = 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
|
#ifdef USE_VOIP
|
||||||
// in the future, (val) will be a protocol version string, so only
|
#ifdef LEGACY_PROTOCOL
|
||||||
// accept explicitly 1, not generally non-zero.
|
if(cl->compat)
|
||||||
val = Info_ValueForKey (cl->userinfo, "cl_voip");
|
cl->hasVoip = qfalse;
|
||||||
cl->hasVoip = (atoi(val) == 1) ? qtrue : qfalse;
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
val = Info_ValueForKey(cl->userinfo, "cl_voip");
|
||||||
|
cl->hasVoip = atoi(val);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TTimo
|
// TTimo
|
||||||
|
@ -1759,7 +1800,7 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) {
|
||||||
const int recip2 = MSG_ReadLong(msg);
|
const int recip2 = MSG_ReadLong(msg);
|
||||||
const int recip3 = MSG_ReadLong(msg);
|
const int recip3 = MSG_ReadLong(msg);
|
||||||
const int packetsize = MSG_ReadShort(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;
|
client_t *client = NULL;
|
||||||
voipServerPacket_t *packet = NULL;
|
voipServerPacket_t *packet = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
@ -1833,13 +1874,15 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) {
|
||||||
continue; // no room for another packet right now.
|
continue; // no room for another packet right now.
|
||||||
}
|
}
|
||||||
|
|
||||||
packet = &client->voipPacket[client->queuedVoipPackets];
|
packet = Hunk_AllocateTempMemory(sizeof(*packet));
|
||||||
packet->sender = sender;
|
packet->sender = sender;
|
||||||
packet->frames = frames;
|
packet->frames = frames;
|
||||||
packet->len = packetsize;
|
packet->len = packetsize;
|
||||||
packet->generation = generation;
|
packet->generation = generation;
|
||||||
packet->sequence = sequence;
|
packet->sequence = sequence;
|
||||||
memcpy(packet->data, encoded, packetsize);
|
memcpy(packet->data, encoded, packetsize);
|
||||||
|
|
||||||
|
client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet;
|
||||||
client->queuedVoipPackets++;
|
client->queuedVoipPackets++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1932,18 +1975,6 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
||||||
do {
|
do {
|
||||||
c = MSG_ReadByte( msg );
|
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 ) {
|
if ( c == clc_EOF ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,7 +554,7 @@ void SV_SpawnServer( char *server, qboolean killBots ) {
|
||||||
client->gentity = ent;
|
client->gentity = ent;
|
||||||
|
|
||||||
client->deltaMessage = -1;
|
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 );
|
VM_Call( gvm, GAME_CLIENT_BEGIN, i );
|
||||||
}
|
}
|
||||||
|
@ -723,7 +723,7 @@ void SV_FinalMessage( char *message ) {
|
||||||
SV_SendServerCommand( cl, "disconnect \"%s\"", message );
|
SV_SendServerCommand( cl, "disconnect \"%s\"", message );
|
||||||
}
|
}
|
||||||
// force a snapshot to be sent
|
// force a snapshot to be sent
|
||||||
cl->nextSnapshotTime = -1;
|
cl->lastSnapshotTime = 0;
|
||||||
SV_SendClientSnapshot( cl );
|
SV_SendClientSnapshot( cl );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1156,5 +1156,52 @@ void SV_Frame( int msec ) {
|
||||||
SV_MasterHeartbeat(sv_heartbeat->string);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#include "../qcommon/qcommon.h"
|
#include "../qcommon/qcommon.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
#ifdef LEGACY_PROTOCOL
|
||||||
/*
|
/*
|
||||||
==============
|
==============
|
||||||
SV_Netchan_Encode
|
SV_Netchan_Encode
|
||||||
|
@ -127,38 +128,63 @@ static void SV_Netchan_Decode( client_t *client, msg_t *msg ) {
|
||||||
*(msg->data + i) = *(msg->data + i) ^ key;
|
*(msg->data + i) = *(msg->data + i) ^ key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
SV_Netchan_TransmitNextFragment
|
SV_Netchan_TransmitNextInQueue
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
void SV_Netchan_TransmitNextFragment( client_t *client ) {
|
void SV_Netchan_TransmitNextInQueue(client_t *client)
|
||||||
Netchan_TransmitNextFragment( &client->netchan );
|
{
|
||||||
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;
|
netchan_buffer_t *netbuf;
|
||||||
|
|
||||||
Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n");
|
Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n");
|
||||||
netbuf = client->netchan_start_queue;
|
netbuf = client->netchan_start_queue;
|
||||||
SV_Netchan_Encode( client, &netbuf->msg );
|
|
||||||
Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data );
|
#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
|
// pop from queue
|
||||||
client->netchan_start_queue = netbuf->next;
|
client->netchan_start_queue = netbuf->next;
|
||||||
if (!client->netchan_start_queue) {
|
if(!client->netchan_start_queue)
|
||||||
|
{
|
||||||
Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n");
|
Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n");
|
||||||
client->netchan_end_queue = &client->netchan_start_queue;
|
client->netchan_end_queue = &client->netchan_start_queue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n");
|
Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n");
|
||||||
Z_Free(netbuf);
|
|
||||||
|
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.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
|
||||||
|
int SV_Netchan_TransmitNextFragment(client_t *client)
|
||||||
|
{
|
||||||
|
if(client->netchan.unsentFragments)
|
||||||
|
{
|
||||||
|
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 @@ 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 );
|
MSG_WriteByte( msg, svc_EOF );
|
||||||
if (client->netchan.unsentFragments) {
|
|
||||||
|
if(client->netchan.unsentFragments || client->netchan_start_queue)
|
||||||
|
{
|
||||||
netchan_buffer_t *netbuf;
|
netchan_buffer_t *netbuf;
|
||||||
Com_DPrintf("#462 SV_Netchan_Transmit: unsent fragments, stacked\n");
|
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
|
// 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);
|
MSG_Copy(&netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg);
|
||||||
netbuf->next = NULL;
|
netbuf->next = NULL;
|
||||||
// insert it in the queue, the message will be encoded and sent later
|
// insert it in the queue, the message will be encoded and sent later
|
||||||
*client->netchan_end_queue = netbuf;
|
*client->netchan_end_queue = netbuf;
|
||||||
client->netchan_end_queue = &(*client->netchan_end_queue)->next;
|
client->netchan_end_queue = &(*client->netchan_end_queue)->next;
|
||||||
// emit the next fragment of the current message for now
|
}
|
||||||
Netchan_TransmitNextFragment(&client->netchan);
|
else
|
||||||
} else {
|
{
|
||||||
SV_Netchan_Encode( client, msg );
|
#ifdef LEGACY_PROTOCOL
|
||||||
|
if(client->compat)
|
||||||
|
SV_Netchan_Encode(client, msg);
|
||||||
|
#endif
|
||||||
Netchan_Transmit( &client->netchan, msg->cursize, msg->data );
|
Netchan_Transmit( &client->netchan, msg->cursize, msg->data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +235,12 @@ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) {
|
||||||
ret = Netchan_Process( &client->netchan, msg );
|
ret = Netchan_Process( &client->netchan, msg );
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return qfalse;
|
return qfalse;
|
||||||
SV_Netchan_Decode( client, msg );
|
|
||||||
|
#ifdef LEGACY_PROTOCOL
|
||||||
|
if(client->compat)
|
||||||
|
SV_Netchan_Decode(client, msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
return qtrue;
|
return qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -521,45 +521,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
|
SV_SendMessageToClient
|
||||||
|
@ -567,48 +528,15 @@ SV_SendMessageToClient
|
||||||
Called by SV_SendClientSnapshot and SV_SendClientGameState
|
Called by SV_SendClientSnapshot and SV_SendClientGameState
|
||||||
=======================
|
=======================
|
||||||
*/
|
*/
|
||||||
void SV_SendMessageToClient( msg_t *msg, client_t *client ) {
|
void SV_SendMessageToClient(msg_t *msg, client_t *client)
|
||||||
int rateMsec;
|
{
|
||||||
|
|
||||||
// record information about the message
|
// record information about the message
|
||||||
client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize;
|
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].messageSent = svs.time;
|
||||||
client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1;
|
client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1;
|
||||||
|
|
||||||
// send the datagram
|
// send the datagram
|
||||||
SV_Netchan_Transmit( client, msg ); //msg->cursize, msg->data );
|
SV_Netchan_Transmit(client, msg);
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -666,33 +594,47 @@ void SV_SendClientSnapshot( client_t *client ) {
|
||||||
SV_SendClientMessages
|
SV_SendClientMessages
|
||||||
=======================
|
=======================
|
||||||
*/
|
*/
|
||||||
void SV_SendClientMessages( void ) {
|
void SV_SendClientMessages(void)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
client_t *c;
|
client_t *c;
|
||||||
|
|
||||||
// send a message to each connected client
|
// send a message to each connected client
|
||||||
for (i=0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++) {
|
for(i=0; i < sv_maxclients->integer; i++)
|
||||||
if (!c->state) {
|
{
|
||||||
continue; // not connected
|
c = &svs.clients[i];
|
||||||
}
|
|
||||||
|
|
||||||
if ( svs.time < c->nextSnapshotTime ) {
|
if(!c->state)
|
||||||
continue; // not time yet
|
continue; // not connected
|
||||||
}
|
|
||||||
|
|
||||||
if(*c->downloadName)
|
if(*c->downloadName)
|
||||||
continue; // Client is downloading, don't send snapshots
|
continue; // Client is downloading, don't send snapshots
|
||||||
|
|
||||||
// send additional message fragments if the last message
|
if(!(c->netchan.remoteAddress.type == NA_LOOPBACK ||
|
||||||
// was too large to send at once
|
(sv_lanForceRate->integer && Sys_IsLANAddress(c->netchan.remoteAddress))))
|
||||||
if ( c->netchan.unsentFragments ) {
|
{
|
||||||
c->nextSnapshotTime = svs.time +
|
// rate control for clients not on LAN
|
||||||
SV_RateMsec( c, c->netchan.unsentLength - c->netchan.unsentFragmentStart );
|
|
||||||
SV_Netchan_TransmitNextFragment( c );
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// generate and send a new message
|
// generate and send a new message
|
||||||
SV_SendClientSnapshot( c );
|
SV_SendClientSnapshot(c);
|
||||||
|
c->lastSnapshotTime = svs.time;
|
||||||
|
c->rateDelayed = qfalse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue