// send the reliable message (forwarded commands) if there is one
if (buf.cursize || cls.netcon->message.cursize)
- NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), false);
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), cl_rate_burstsize.integer, false);
if (quemove)
{
MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate \"%s\"", value));
}
+ else if (!strcasecmp(key, "rate_burstsize"))
+ {
+ MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
+ MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate_burstsize \"%s\"", value));
+ }
}
}
Con_DPrint("Sending clc_disconnect\n");
MSG_WriteByte(&buf, clc_disconnect);
}
- NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, false);
- NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, false);
- NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, false);
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, 0, false);
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, 0, false);
+ NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, 10000, 0, false);
NetConn_Close(cls.netcon);
cls.netcon = NULL;
}
msg.data = buf;
msg.maxsize = sizeof(buf);
MSG_WriteChar(&msg, clc_nop);
- NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
+ NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
}
recursive = thisrecursive;
MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate %i", cl_rate.integer));
+ MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
+ MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate_burstsize %i", cl_rate_burstsize.integer));
+
if (cl_pmodel.integer)
{
MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
SCR_DrawCenterString ();
}
-static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
+static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, int graphheight, float graphscale, int graphlimit, const char *label, float textsize, int packetcounter, netgraphitem_t *netgraph)
{
netgraphitem_t *graph;
int j, x, y, numlines;
int totalbytes = 0;
char bytesstring[128];
- float g[NETGRAPH_PACKETS][6];
+ float g[NETGRAPH_PACKETS][7];
float *a;
float *b;
- r_vertexgeneric_t vertex[(NETGRAPH_PACKETS+2)*5*2];
+ r_vertexgeneric_t vertex[(NETGRAPH_PACKETS+2)*6*2];
r_vertexgeneric_t *v;
DrawQ_Fill(graphx, graphy, graphwidth, graphheight + textsize * 2, 0, 0, 0, 0.5, 0);
// draw the bar graph itself
g[j][3] = 1.0f;
g[j][4] = 1.0f;
g[j][5] = 1.0f;
+ g[j][6] = 1.0f;
if (graph->unreliablebytes == NETGRAPH_LOSTPACKET)
g[j][1] = 0.00f;
else if (graph->unreliablebytes == NETGRAPH_CHOKEDPACKET)
- g[j][2] = 0.96f;
+ g[j][2] = 0.90f;
else
{
+ if(netgraph[j].time >= netgraph[(j+NETGRAPH_PACKETS-1)%NETGRAPH_PACKETS].time)
+ if(graph->unreliablebytes + graph->reliablebytes + graph->ackbytes >= graphlimit * (netgraph[j].time - netgraph[(j+NETGRAPH_PACKETS-1)%NETGRAPH_PACKETS].time))
+ g[j][2] = 0.98f;
g[j][3] = 1.0f - graph->unreliablebytes * graphscale;
g[j][4] = g[j][3] - graph->reliablebytes * graphscale;
g[j][5] = g[j][4] - graph->ackbytes * graphscale;
if (realtime - graph->time < 1.0f)
totalbytes += graph->unreliablebytes + graph->reliablebytes + graph->ackbytes;
}
+ if(graph->cleartime >= 0)
+ g[j][6] = 0.5f + 0.5f * (2.0 / M_PI) * atan((M_PI / 2.0) * (graph->cleartime - graph->time));
g[j][1] = bound(0.0f, g[j][1], 1.0f);
g[j][2] = bound(0.0f, g[j][2], 1.0f);
g[j][3] = bound(0.0f, g[j][3], 1.0f);
g[j][4] = bound(0.0f, g[j][4], 1.0f);
g[j][5] = bound(0.0f, g[j][5], 1.0f);
+ g[j][6] = bound(0.0f, g[j][6], 1.0f);
}
// render the lines for the graph
numlines = 0;
VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[3], 0.0f);Vector4Set(v->color4f, 1.0f, 0.5f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[3], 0.0f);Vector4Set(v->color4f, 1.0f, 0.5f, 0.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
- numlines += 5;
+ VectorSet(v->vertex3f, graphx + graphwidth * a[0], graphy + graphheight * a[6], 0.0f);Vector4Set(v->color4f, 0.0f, 0.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
+ VectorSet(v->vertex3f, graphx + graphwidth * b[0], graphy + graphheight * b[6], 0.0f);Vector4Set(v->color4f, 0.0f, 0.0f, 1.0f, 1.0f);Vector2Set(v->texcoord2f, 0.0f, 0.0f);v++;
+
+ numlines += 6;
}
if (numlines > 0)
{
*/
static void SCR_DrawNetGraph (void)
{
- int i, separator1, separator2, graphwidth, graphheight, netgraph_x, netgraph_y, textsize, index, netgraphsperrow;
+ int i, separator1, separator2, graphwidth, graphheight, netgraph_x, netgraph_y, textsize, index, netgraphsperrow, graphlimit;
float graphscale;
netconn_t *c;
char vabuf[1024];
graphwidth = 120;
graphheight = 70;
graphscale = 1.0f / 1500.0f;
+ graphlimit = cl_rate.integer;
netgraphsperrow = (vid_conwidth.integer + separator2) / (graphwidth * 2 + separator1 + separator2);
netgraphsperrow = max(netgraphsperrow, 1);
netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
netgraph_y = (vid_conheight.integer - 48 - sbar_info_pos.integer + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
c = cls.netcon;
- SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, "incoming", textsize, c->incoming_packetcounter, c->incoming_netgraph);
- SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, "outgoing", textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
+ SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "incoming", textsize, c->incoming_packetcounter, c->incoming_netgraph);
+ SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "outgoing", textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
index++;
if (sv.active && shownetgraph.integer >= 2)
continue;
netgraph_x = (vid_conwidth.integer + separator2) - (1 + (index % netgraphsperrow)) * (graphwidth * 2 + separator1 + separator2);
netgraph_y = (vid_conheight.integer - 48 + separator2) - (1 + (index / netgraphsperrow)) * (graphheight + textsize + separator2);
- SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, va(vabuf, sizeof(vabuf), "%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
- SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, "" , textsize, c->incoming_packetcounter, c->incoming_netgraph);
+ SCR_DrawNetGraph_DrawGraph(netgraph_x , netgraph_y, graphwidth, graphheight, graphscale, graphlimit, va(vabuf, sizeof(vabuf), "%s", svs.clients[i].name), textsize, c->outgoing_packetcounter, c->outgoing_netgraph);
+ SCR_DrawNetGraph_DrawGraph(netgraph_x + graphwidth + separator1, netgraph_y, graphwidth, graphheight, graphscale, graphlimit, "" , textsize, c->incoming_packetcounter, c->incoming_netgraph);
index++;
}
}
extern cvar_t cl_name;
extern cvar_t cl_color;
extern cvar_t cl_rate;
+extern cvar_t cl_rate_burstsize;
extern cvar_t cl_pmodel;
extern cvar_t cl_playermodel;
extern cvar_t cl_playerskin;
}
else if (!strcmp(var->name, "_cl_rate"))
CL_SetInfo("rate", va(vabuf, sizeof(vabuf), "%i", var->integer), true, false, false, false);
+ else if (!strcmp(var->name, "_cl_rate_burstsize"))
+ CL_SetInfo("rate_burstsize", va(vabuf, sizeof(vabuf), "%i", var->integer), true, false, false, false);
else if (!strcmp(var->name, "_cl_playerskin"))
CL_SetInfo("playerskin", var->string, true, false, false, false);
else if (!strcmp(var->name, "_cl_playermodel"))
buf.data = bufdata;
buf.maxsize = sizeof(bufdata);
MSG_WriteByte(&buf, svc_disconnect);
- NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, false);
- NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, false);
- NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, false);
+ NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
+ NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
+ NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false);
}
}
}
cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
+cvar_t cl_rate_burstsize = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
static void Host_Rate_f(void)
{
int rate;
host_client->rate = rate;
}
+static void Host_Rate_BurstSize_f(void)
+{
+ int rate_burstsize;
+
+ if (Cmd_Argc() != 2)
+ {
+ Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
+ Con_Print("rate_burstsize <bytes>\n");
+ return;
+ }
+
+ rate_burstsize = atoi(Cmd_Argv(1));
+
+ if (cmd_source == src_command)
+ {
+ Cvar_SetValue ("_cl_rate_burstsize", rate_burstsize);
+ return;
+ }
+
+ host_client->rate_burstsize = rate_burstsize;
+}
/*
==================
Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
Cvar_RegisterVariable (&cl_rate);
Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
+ Cvar_RegisterVariable (&cl_rate_burstsize);
+ Cmd_AddCommand_WithClientCommand ("rate_burstsize", Host_Rate_BurstSize_f, Host_Rate_BurstSize_f, "change your network connection speed");
Cvar_RegisterVariable (&cl_pmodel);
Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
char cl_readstring[MAX_INPUTLINE];
char sv_readstring[MAX_INPUTLINE];
+cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
+cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
+cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes = NETGRAPH_NOPACKET;
+ conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
if (realtime > conn->cleartime)
return true;
else
}
}
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
+void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
+{
+ double bursttime = burstsize / (double)rate;
+
+ // delay later packets to obey rate limit
+ if (*cleartime < realtime - bursttime)
+ *cleartime = realtime - bursttime;
+ *cleartime = *cleartime + len / (double)rate;
+
+ // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
+ if (net_test.integer)
+ {
+ if (*cleartime < realtime)
+ *cleartime = realtime;
+ }
+}
+
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
{
int totallen = 0;
unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
+ conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
+
if (protocol == PROTOCOL_QUAKEWORLD)
{
int packetLen;
}
}
- // 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;
+ NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
return 0;
}
{
conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
+ conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
}
conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
+ conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
+ NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
+ // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
+ if (net_test.integer)
+ {
+ if (conn->cleartime < realtime)
+ conn->cleartime = realtime;
+ }
+
if (reliable_ack == conn->qw.reliable_sequence)
{
// received, now we will be able to send another reliable message
{
conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
+ conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
}
conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
conn->incoming_netgraph[conn->incoming_packetcounter].time = realtime;
+ conn->incoming_netgraph[conn->incoming_packetcounter].cleartime = conn->incoming_cleartime;
conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes = NETGRAPH_NOPACKET;
conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes = NETGRAPH_NOPACKET;
+ NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
conn->nq.unreliableReceiveSequence = sequence + 1;
conn->lastMessageTime = realtime;
conn->timeout = realtime + newtimeout;
else if (flags & NETFLAG_ACK)
{
conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
+ NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
if (sequence == (conn->nq.sendSequence - 1))
{
if (sequence == conn->nq.ackSequence)
{
unsigned char temppacket[8];
conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28;
+ NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28;
+
StoreBigLong(temppacket, 8 | NETFLAG_ACK);
StoreBigLong(temppacket + 4, sequence);
sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
msg.data = buf;
msg.maxsize = sizeof(buf);
MSG_WriteChar(&msg, clc_nop);
- NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
+ NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
}
}
Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
+ Cvar_RegisterVariable(&net_test);
+ Cvar_RegisterVariable(&net_usesizelimit);
+ Cvar_RegisterVariable(&net_burstreserve);
Cvar_RegisterVariable(&rcon_restricted_password);
Cvar_RegisterVariable(&rcon_restricted_commands);
Cvar_RegisterVariable(&rcon_secure_maxdiff);
int reliablebytes;
int unreliablebytes;
int ackbytes;
+ double cleartime;
}
netgraphitem_t;
// bandwidth estimator
double cleartime; // if realtime > nc->cleartime, free to go
+ double incoming_cleartime; // if realtime > nc->cleartime, free to go (netgraph cleartime simulation only)
// this tracks packet loss and packet sizes on the most recent packets
// used by shownetgraph feature
extern cvar_t sv_netport;
extern cvar_t net_address;
extern cvar_t net_address_ipv6;
+extern cvar_t net_usesizelimit;
+extern cvar_t net_burstreserve;
qboolean NetConn_CanSend(netconn_t *conn);
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables);
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables);
qboolean NetConn_HaveClientPorts(void);
qboolean NetConn_HaveServerPorts(void);
void NetConn_CloseClientPorts(void);
/// requested rate in bytes per second
int rate;
+ /// temporarily exceed rate by this amount of bytes
+ int rate_burstsize;
+
/// realtime this client connected
double connecttime;
client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
// updated by receiving "rate" command from client, this is also the default if not using a DP client
client->rate = 1000000000;
- // no limits for local player
- if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
- client->rate = 1000000000;
client->connecttime = realtime;
if (!sv.loadgame)
sizebuf_t msg;
int stats[MAX_CL_STATS];
static unsigned char sv_sendclientdatagram_buf[NET_MAXMESSAGE];
+ double timedelta;
// obey rate limit by limiting packet frequency if the packet size
// limiting fails
//
// at very low rates (or very small sys_ticrate) the packet size is
// not reduced below 128, but packets may be sent less often
- maxsize = (int)(clientrate * sys_ticrate.value);
+
+ // how long are bursts?
+ timedelta = host_client->rate_burstsize / (double)client->rate;
+
+ // how much of the burst do we keep reserved?
+ timedelta *= 1 - net_burstreserve.value;
+
+ // only try to use excess time
+ timedelta = bound(0, realtime - host_client->netconnection->cleartime, timedelta);
+
+ // but we know next packet will be in sys_ticrate, so we can use up THAT bandwidth
+ timedelta += sys_ticrate.value;
+
+ // note: packet overhead (not counted in maxsize) is 28 bytes
+ maxsize = (int)(clientrate * timedelta) - 28;
+
+ // put it in sound bounds
maxsize = bound(128, maxsize, 1400);
maxsize2 = 1400;
+
// csqc entities can easily exceed 128 bytes, so disable throttling in
// mods that use csqc (they are likely to use less bandwidth anyway)
- if (sv.csqc_progsize > 0)
+ if((net_usesizelimit.integer == 1) ? (sv.csqc_progsize > 0) : (net_usesizelimit.integer < 1))
maxsize = maxsize2;
+
break;
}
SV_WriteDemoMessage(client, &msg, false);
// send the datagram
- NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate, client->sendsignon == 2);
+ NetConn_SendUnreliableMessage (client->netconnection, &msg, sv.protocol, clientrate, client->rate_burstsize, client->sendsignon == 2);
if (client->sendsignon == 1 && !client->netconnection->message.cursize)
client->sendsignon = 2; // prevent reliable until client sends prespawn (this is the keepalive phase)
}