implemented shownetgraph
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Apr 2007 13:08:38 +0000 (13:08 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 16 Apr 2007 13:08:38 +0000 (13:08 +0000)
major overhaul of rate limiting code
implemented packet-frequency rate limiting (like qw/q2/q3) as a fallback
when packet size limiting fails (such as packet sizes below 100 bytes
causing all sorts of issues, so now the minimum bound on maxsize is 100,
and another good example is reliable messages exceeding the rate limit)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7108 d7cf8633-e32d-0410-b094-e92efae38249

cl_input.c
cl_main.c
cl_parse.c
cl_screen.c
host.c
host_cmd.c
netconn.c
netconn.h
sv_main.c

index a1a7aad..c84b3c6 100644 (file)
@@ -1314,12 +1314,18 @@ void CL_SendMove(void)
                        return;
                cl.cmd.time = cl.mtime[0];
        }
+
        // don't let it fall behind if CL_SendMove hasn't been called recently
        // (such is the case when framerate is too low for instance)
        lastsendtime = bound(realtime, lastsendtime + packettime, realtime + packettime);
        // set the flag indicating that we sent a packet recently
        cl.movement_needupdate = false;
 
+       // don't send a new input packet if the connection is still saturated from
+       // the last one (or chat messages, etc)
+       // note: this behavior comes from QW
+       if (!NetConn_CanSend(cls.netcon))
+               return;
 
        buf.maxsize = sizeof(data);
        buf.cursize = 0;
@@ -1432,8 +1438,10 @@ void CL_SendMove(void)
                                checksumindex = buf.cursize;
                                MSG_WriteByte(&buf, 0);
                                // packet loss percentage
-                               for (j = 0, packetloss = 0;j < 100;j++)
-                                       packetloss += cls.netcon->packetlost[j];
+                               for (j = 0, packetloss = 0;j < NETGRAPH_PACKETS;j++)
+                                       if (cls.netcon->incoming_unreliablesize[i] == NETGRAPH_LOSTPACKET)
+                                               packetloss++;
+                               packetloss = packetloss * 100 / NETGRAPH_PACKETS;
                                MSG_WriteByte(&buf, packetloss);
                                // write most recent 3 moves
                                QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, &cl.movecmd[2]);
@@ -1564,7 +1572,7 @@ void CL_SendMove(void)
                {
                        if (cl.latestframenums[i] > 0)
                        {
-                               if (developer_networkentities.integer >= 1)
+                               if (developer_networkentities.integer >= 10)
                                        Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
                                MSG_WriteByte(&buf, clc_ackframe);
                                MSG_WriteLong(&buf, cl.latestframenums[i]);
@@ -1589,7 +1597,7 @@ void CL_SendMove(void)
        }
 
        // send the reliable message (forwarded commands) if there is one
-       NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
+       NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer));
 
        if (cls.netcon->message.overflowed)
        {
index a95388a..5ecb37e 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -338,9 +338,9 @@ void CL_Disconnect(void)
                        Con_DPrint("Sending clc_disconnect\n");
                        MSG_WriteByte(&buf, clc_disconnect);
                }
-               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
-               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
-               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol);
+               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000);
+               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000);
+               NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000);
                NetConn_Close(cls.netcon);
                cls.netcon = NULL;
        }
index c234fb7..779650b 100644 (file)
@@ -309,7 +309,7 @@ void CL_KeepaliveMessage (qboolean readmessages)
                msg.data = buf;
                msg.maxsize = sizeof(buf);
                MSG_WriteChar(&msg, clc_nop);
-               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol);
+               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000);
        }
 }
 
index 8483e19..869b893 100644 (file)
@@ -46,6 +46,7 @@ cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20",
 cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20", "fov of zoom window"};
 cvar_t scr_stipple = {0, "scr_stipple", "0", "interlacing-like stippling of the display"};
 cvar_t scr_refresh = {0, "scr_refresh", "1", "allows you to completely shut off rendering for benchmarking purposes"};
+cvar_t shownetgraph = {CVAR_SAVE, "shownetgraph", "0", "shows a graph of packet sizes and other information"};
 
 
 int jpeg_supported = false;
@@ -184,6 +185,86 @@ void SCR_CheckDrawCenterString (void)
        SCR_DrawCenterString ();
 }
 
+void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int barwidth, int barheight, int bardivide, const char *label, float textsize, int packetcounter, int numparameters, const int **parameters, const float parametercolors[][4])
+{
+       int j, k, x, y, index, offset, height;
+       // dim background
+       DrawQ_Pic (graphx, graphy, NULL, barwidth * NETGRAPH_PACKETS, barheight + textsize, 0, 0, 0, 0.5, 0);
+       // draw a label
+       DrawQ_String (graphx, graphy + barheight, label, 0, textsize, textsize, 1, 1, 1, 1, 0);
+       // draw the bar graph itself
+       for (j = 0;j < NETGRAPH_PACKETS;j++)
+       {
+               x = graphx + j * barwidth;
+               y = graphy + barheight;
+               index = (packetcounter + j) % NETGRAPH_PACKETS;
+               if (parameters[0][index] == NETGRAPH_LOSTPACKET)
+                       DrawQ_Pic(x, y - barheight, NULL, barwidth, barheight, 1, 0, 0, 1, 0);
+               else if (parameters[0][index] == NETGRAPH_CHOKEDPACKET)
+                       DrawQ_Pic(x, y - min(2, barheight), NULL, barwidth, min(2, barheight), 1, 1, 0, 1, 0);
+               else
+               {
+                       offset = 0;
+                       for (k = 0;k < numparameters;k++)
+                       {
+                               height = (parameters[k][index] + bardivide - 1) / bardivide;
+                               height = min(height, barheight - offset);
+                               offset += height;
+                               if (height)
+                                       DrawQ_Pic(x, y - offset, NULL, barwidth, height, parametercolors[k][0], parametercolors[k][1], parametercolors[k][2], parametercolors[k][3], 0);
+                       }
+               }
+       }
+}
+
+const float netgraphcolors[3][4] =
+{
+       {1  , 0.5, 0  , 1},
+       {1  , 1  , 1  , 1},
+       {0  , 1  , 0  , 1},
+};
+
+void SCR_DrawNetGraph_DrawConnection (netconn_t *conn, int graphx, int graphy, int barwidth, int barheight, int bardivide, const char *labelincoming, int separator, const char *labeloutgoing, float textsize)
+{
+       int numparameters;
+       const int *parameters[3];
+       numparameters = 3;
+       parameters[0] = conn->incoming_unreliablesize;
+       parameters[1] = conn->incoming_reliablesize;
+       parameters[2] = conn->incoming_acksize;
+       SCR_DrawNetGraph_DrawGraph(graphx, graphy, barwidth, barheight, bardivide, labelincoming, textsize, conn->incoming_packetcounter, numparameters, parameters, netgraphcolors);
+       parameters[0] = conn->outgoing_unreliablesize;
+       parameters[1] = conn->outgoing_reliablesize;
+       parameters[2] = conn->outgoing_acksize;
+       SCR_DrawNetGraph_DrawGraph(graphx + barwidth * NETGRAPH_PACKETS + separator, graphy, barwidth, barheight, bardivide, labeloutgoing, textsize, conn->outgoing_packetcounter, numparameters, parameters, netgraphcolors);
+}
+
+/*
+==============
+SCR_DrawNetGraph
+==============
+*/
+void SCR_DrawNetGraph (void)
+{
+       int separator, barwidth, barheight, bardivide, netgraph_x, netgraph_y, textsize;
+
+       if (cls.state != ca_connected)
+               return;
+       if (!cls.netcon)
+               return;
+       if (!shownetgraph.integer)
+               return;
+
+       separator = 4;
+       textsize = 8;
+       barwidth = 1;
+       barheight = 50;
+       bardivide = 20;
+       netgraph_x = 0;
+       netgraph_y = vid_conheight.integer - 48 - barheight - textsize;
+       SCR_DrawNetGraph_DrawConnection(cls.netcon, netgraph_x, netgraph_y, barwidth, barheight, bardivide, "incoming", separator, "outgoing", textsize);
+}
+
 /*
 ==============
 SCR_DrawTurtle
@@ -639,6 +720,7 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable(&scr_zoomwindow_fov);
        Cvar_RegisterVariable(&scr_stipple);
        Cvar_RegisterVariable(&scr_refresh);
+       Cvar_RegisterVariable(&shownetgraph);
 
        Cmd_AddCommand ("sizeup",SCR_SizeUp_f, "increase view size (increases viewsize cvar)");
        Cmd_AddCommand ("sizedown",SCR_SizeDown_f, "decrease view size (decreases viewsize cvar)");
@@ -1591,6 +1673,7 @@ void SCR_DrawScreen (void)
                SHOWLMP_drawall();
                SCR_CheckDrawCenterString();
        }
+       SCR_DrawNetGraph ();
        MR_Draw();
        CL_DrawVideo();
        R_Shadow_EditLights_DrawSelectedLightProperties();
diff --git a/host.c b/host.c
index b81f181..6d1788a 100644 (file)
--- a/host.c
+++ b/host.c
@@ -435,9 +435,9 @@ void SV_DropClient(qboolean crash)
                        buf.data = bufdata;
                        buf.maxsize = sizeof(bufdata);
                        MSG_WriteByte(&buf, svc_disconnect);
-                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol);
-                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol);
-                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol);
+                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000);
+                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000);
+                       NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000);
                }
                // break the net connection
                NetConn_Close(host_client->netconnection);
index bac0d14..2ea15b8 100644 (file)
@@ -2324,8 +2324,10 @@ void Host_Pings_f (void)
        {
                packetloss = 0;
                if (svs.clients[i].netconnection)
-                       for (j = 0;j < 100;j++)
-                               packetloss += svs.clients[i].netconnection->packetlost[j];
+                       for (j = 0;j < NETGRAPH_PACKETS;j++)
+                               if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+                                       packetloss++;
+               packetloss = packetloss * 100 / NETGRAPH_PACKETS;
                ping = (int)floor(svs.clients[i].ping*1000+0.5);
                ping = bound(0, ping, 9999);
                if (sv.protocol == PROTOCOL_QUAKEWORLD)
index c0c4c1e..833c50a 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -472,8 +472,31 @@ int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnet
        return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
 }
 
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol)
+qboolean NetConn_CanSend(netconn_t *conn)
 {
+       conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
+       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       conn->outgoing_reliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       conn->outgoing_acksize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       if (realtime > conn->cleartime)
+               return true;
+       else
+       {
+               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_CHOKEDPACKET;
+               return false;
+       }
+}
+
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate)
+{
+       int totallen = 0;
+
+       // if this packet was supposedly choked, but we find ourselves sending one
+       // anyway, make sure the size counting starts at zero
+       // (this mostly happens on level changes and disconnects and such)
+       if (conn->outgoing_unreliablesize[conn->outgoing_packetcounter] == NETGRAPH_CHOKEDPACKET)
+               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
                int packetLen;
@@ -514,16 +537,22 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
                        return -1;
                }
+
+               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+
                // add the reliable message if there is one
                if (sendreliable)
                {
+                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength;
                        memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
                        packetLen += conn->sendMessageLength;
                        conn->qw.last_reliable_sequence = conn->qw.outgoing_sequence;
                }
+
                // add the unreliable message if possible
                if (packetLen + data->cursize <= 1400)
                {
+                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += data->cursize;
                        memcpy(sendbuffer + packetLen, data->data, data->cursize);
                        packetLen += data->cursize;
                }
@@ -533,10 +562,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                packetsSent++;
                unreliableMessagesSent++;
 
-               // delay later packets to obey rate limit
-               conn->qw.cleartime = max(conn->qw.cleartime, realtime) + packetLen * conn->qw.rate;
-
-               return 0;
+               totallen += packetLen + 18;
        }
        else
        {
@@ -566,11 +592,15 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        header[1] = BigLong(conn->nq.sendSequence - 1);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
+                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
+
                        if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
                        {
                                conn->lastSendTime = realtime;
                                packetsReSent++;
                        }
+
+                       totallen += packetLen + 18;
                }
 
                // if we have a new reliable message to send, do so
@@ -613,15 +643,19 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->nq.sendSequence++;
 
+                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
+
                        NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
                        conn->lastSendTime = realtime;
                        packetsSent++;
                        reliableMessagesSent++;
+
+                       totallen += packetLen + 18;
                }
 
                // if we have an unreliable message to send, do so
-               if (data->cursize)
+               //if (data->cursize)
                {
                        packetLen = NET_HEADERSIZE + data->cursize;
 
@@ -638,13 +672,26 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->nq.unreliableSendSequence++;
 
+                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+
                        NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
                        packetsSent++;
                        unreliableMessagesSent++;
+
+                       if (data->cursize)
+                               totallen += packetLen + 18;
                }
-               return 0;
        }
+
+       // delay later packets to obey rate limit
+       if (conn->cleartime < realtime - 0.1)
+               conn->cleartime = realtime - 0.1;
+       conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
+       if (conn->cleartime < realtime)
+               conn->cleartime = realtime;
+
+       return 0;
 }
 
 void NetConn_CloseClientPorts(void)
@@ -850,6 +897,7 @@ void NetConn_UpdateSockets(void)
 
 static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
 {
+       int originallength = length;
        if (length < 8)
                return 0;
 
@@ -895,12 +943,16 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        //Con_DPrintf("Dropped %u datagram(s)\n", count);
                        while (count--)
                        {
-                               conn->packetlost[conn->packetlostcounter] = true;
-                               conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+                               conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
+                               conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
+                               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
                        }
                }
-               conn->packetlost[conn->packetlostcounter] = false;
-               conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+               conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
+               conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
+               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
                if (reliable_ack == conn->qw.reliable_sequence)
                {
                        // received, now we will be able to send another reliable message
@@ -950,12 +1002,16 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                                //Con_DPrintf("Dropped %u datagram(s)\n", count);
                                                while (count--)
                                                {
-                                                       conn->packetlost[conn->packetlostcounter] = true;
-                                                       conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+                                                       conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
+                                                       conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
+                                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
                                                }
                                        }
-                                       conn->packetlost[conn->packetlostcounter] = false;
-                                       conn->packetlostcounter = (conn->packetlostcounter + 1) % 100;
+                                       conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
+                                       conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
+                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
                                        conn->nq.unreliableReceiveSequence = sequence + 1;
                                        conn->lastMessageTime = realtime;
                                        conn->timeout = realtime + newtimeout;
@@ -974,6 +1030,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        }
                        else if (flags & NETFLAG_ACK)
                        {
+                               conn->incoming_acksize[conn->incoming_packetcounter] += originallength;
                                if (sequence == (conn->nq.sendSequence - 1))
                                {
                                        if (sequence == conn->nq.ackSequence)
@@ -1032,6 +1089,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        else if (flags & NETFLAG_DATA)
                        {
                                unsigned int temppacket[2];
+                               conn->incoming_reliablesize[conn->incoming_packetcounter] += originallength;
+                               conn->outgoing_acksize[conn->outgoing_packetcounter] += 8;
                                temppacket[0] = BigLong(8 | NETFLAG_ACK);
                                temppacket[1] = BigLong(sequence);
                                NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
@@ -1105,7 +1164,7 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
                msg.data = buf;
                msg.maxsize = sizeof(buf);
                MSG_WriteChar(&msg, clc_nop);
-               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol);
+               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000);
        }
 }
 
index 9b54313..603d987 100755 (executable)
--- a/netconn.h
+++ b/netconn.h
@@ -169,10 +169,6 @@ typedef struct netconn_s
 
                int                     qport;
 
-       // bandwidth estimator
-               double          cleartime;                      // if realtime > nc->cleartime, free to go
-               double          rate;                           // seconds / byte
-
        // sequencing variables
                int                     incoming_sequence;
                int                     incoming_acknowledged;
@@ -186,9 +182,23 @@ typedef struct netconn_s
        }
        qw;
 
-       // this tracks which of the last 100 received packet sequence numbers were lost
-       int packetlostcounter;
-       unsigned char packetlost[100];
+       // bandwidth estimator
+       double          cleartime;                      // if realtime > nc->cleartime, free to go
+
+       // this tracks packet loss and packet sizes on the most recent packets
+       // used by shownetgraph feature
+#define NETGRAPH_PACKETS 100
+#define NETGRAPH_NOPACKET 0
+#define NETGRAPH_LOSTPACKET -1
+#define NETGRAPH_CHOKEDPACKET -2
+       int incoming_packetcounter;
+       int incoming_reliablesize[NETGRAPH_PACKETS];
+       int incoming_unreliablesize[NETGRAPH_PACKETS];
+       int incoming_acksize[NETGRAPH_PACKETS];
+       int outgoing_packetcounter;
+       int outgoing_reliablesize[NETGRAPH_PACKETS];
+       int outgoing_unreliablesize[NETGRAPH_PACKETS];
+       int outgoing_acksize[NETGRAPH_PACKETS];
 
        char address[128];
 } netconn_t;
@@ -331,7 +341,8 @@ extern cvar_t sv_netport;
 extern cvar_t net_address;
 //extern cvar_t net_netaddress_ipv6;
 
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol);
+qboolean NetConn_CanSend(netconn_t *conn);
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate);
 void NetConn_CloseClientPorts(void);
 void NetConn_OpenClientPorts(void);
 void NetConn_CloseServerPorts(void);
index ebd85b8..2fdbee4 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -1357,36 +1357,42 @@ SV_SendClientDatagram
 static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE]; // FIXME?
 void SV_SendClientDatagram (client_t *client)
 {
-       int rate, maxrate, maxsize, maxsize2, downloadsize;
+       int clientrate, maxrate, maxsize, maxsize2, downloadsize;
        sizebuf_t msg;
        int stats[MAX_CL_STATS];
 
+       // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
+       maxrate = max(NET_MINRATE, sv_maxrate.integer);
+       if (sv_maxrate.integer != maxrate)
+               Cvar_SetValueQuick(&sv_maxrate, maxrate);
+       // clientrate determines the 'cleartime' of a packet
+       // (how long to wait before sending another, based on this packet's size)
+       clientrate = bound(NET_MINRATE, client->rate, maxrate);
+
        if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP && !sv_ratelimitlocalplayer.integer)
        {
-               // for good singleplayer, send huge packets
+               // for good singleplayer, send huge packets and never limit frequency
+               clientrate = 1000000000;
                maxsize = sizeof(sv_sendclientdatagram_buf);
                maxsize2 = sizeof(sv_sendclientdatagram_buf);
        }
        else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
        {
-               // no rate limiting support on older protocols because dp protocols
-               // 1-4 kick the client off if they overflow, and quake protocol shows
-               // less than the full entity set if rate limited
+               // no packet size limit support on older protocols because DP1-4 kick
+               // the client off if they overflow, and quake protocol shows less than
+               // the full entity set if rate limited
                maxsize = 1400;
                maxsize2 = 1400;
        }
        else
        {
-               // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates
-               maxrate = max(NET_MINRATE, sv_maxrate.integer);
-               if (sv_maxrate.integer != maxrate)
-                       Cvar_SetValueQuick(&sv_maxrate, maxrate);
-
+               // DP5 and later protocols support packet size limiting which is a
+               // better method than limiting packet frequency as QW does
+               //
                // this rate limiting does not understand sys_ticrate 0
                // (but no one should be running that on a server!)
-               rate = bound(NET_MINRATE, client->rate, maxrate);
-               rate = (int)(rate * sys_ticrate.value);
-               maxsize = bound(50, rate, 1400);
+               maxsize = (int)(clientrate * sys_ticrate.value);
+               maxsize = bound(100, maxsize, 1400);
                maxsize2 = 1400;
        }
 
@@ -1399,7 +1405,16 @@ void SV_SendClientDatagram (client_t *client)
        msg.maxsize = maxsize;
        msg.cursize = 0;
 
-       if (host_client->spawned)
+       // obey rate limit by limiting packet frequency if the packet size
+       // limiting fails
+       // (usually this is caused by reliable messages)
+       if (!NetConn_CanSend(client->netconnection))
+       {
+               // send the datagram
+               NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate);
+               return;
+       }
+       else if (host_client->spawned)
        {
                MSG_WriteByte (&msg, svc_time);
                MSG_WriteFloat (&msg, sv.time);
@@ -1457,7 +1472,7 @@ void SV_SendClientDatagram (client_t *client)
        }
 
 // send the datagram
-       NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol);
+       NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate);
 }
 
 /*