From 5fb2c652f480efe23dfd426c0a75698e8a5ed160 Mon Sep 17 00:00:00 2001 From: divverent Date: Sat, 13 Jul 2013 10:15:35 +0000 Subject: [PATCH] New rate burst handling; cvars: cl_rate_burstsize, net_usesizelimit, net_burstreserve This supports some kind of packet size bursting to give better experience at small rates. Can be controlled by the client. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11966 d7cf8633-e32d-0410-b094-e92efae38249 ::stable-branch::merge=8d7c6efa6bdbfecaa3dbcc39b68c4469d272dc6b --- cl_input.c | 2 +- cl_main.c | 11 ++++++++--- cl_parse.c | 5 ++++- cl_screen.c | 31 +++++++++++++++++++---------- client.h | 1 + cvar.c | 2 ++ host.c | 6 +++--- host_cmd.c | 24 ++++++++++++++++++++++ netconn.c | 57 +++++++++++++++++++++++++++++++++++++++++++++-------- netconn.h | 6 +++++- server.h | 3 +++ sv_main.c | 28 ++++++++++++++++++++------ 12 files changed, 143 insertions(+), 33 deletions(-) diff --git a/cl_input.c b/cl_input.c index a750e68b..f76c15c4 100644 --- a/cl_input.c +++ b/cl_input.c @@ -2091,7 +2091,7 @@ void CL_SendMove(void) // 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) { diff --git a/cl_main.c b/cl_main.c index 180a90d5..76f27168 100644 --- a/cl_main.c +++ b/cl_main.c @@ -268,6 +268,11 @@ void CL_SetInfo(const char *key, const char *value, qboolean send, qboolean allo 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)); + } } } @@ -383,9 +388,9 @@ void CL_Disconnect(void) 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; } diff --git a/cl_parse.c b/cl_parse.c index 53bc140c..65809f42 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -368,7 +368,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, 10000, false); + NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false); } recursive = thisrecursive; @@ -1553,6 +1553,9 @@ static void CL_SendPlayerInfo(void) 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); diff --git a/cl_screen.c b/cl_screen.c index 640cc090..a37dfed4 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -244,16 +244,16 @@ static void SCR_CheckDrawCenterString (void) 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 @@ -267,12 +267,16 @@ static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, 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; @@ -280,11 +284,14 @@ static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, 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; @@ -310,7 +317,10 @@ static void SCR_DrawNetGraph_DrawGraph (int graphx, int graphy, int graphwidth, 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) { @@ -331,7 +341,7 @@ SCR_DrawNetGraph */ 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]; @@ -349,6 +359,7 @@ static void SCR_DrawNetGraph (void) 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); @@ -357,8 +368,8 @@ static void SCR_DrawNetGraph (void) 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) @@ -370,8 +381,8 @@ static void SCR_DrawNetGraph (void) 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++; } } diff --git a/client.h b/client.h index becccd5a..83b171cf 100644 --- a/client.h +++ b/client.h @@ -1472,6 +1472,7 @@ client_state_t; 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; diff --git a/cvar.c b/cvar.c index b88aafd0..06afc0d2 100644 --- a/cvar.c +++ b/cvar.c @@ -381,6 +381,8 @@ static void Cvar_SetQuick_Internal (cvar_t *var, const char *value) } 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")) diff --git a/host.c b/host.c index f4be51c7..0b97a454 100644 --- a/host.c +++ b/host.c @@ -477,9 +477,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, 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); } } diff --git a/host_cmd.c b/host_cmd.c index 6a0b9914..5e8daba3 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -1670,6 +1670,7 @@ static void Host_BottomColor_f(void) } 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; @@ -1691,6 +1692,27 @@ static void Host_Rate_f(void) 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 \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; +} /* ================== @@ -2997,6 +3019,8 @@ void Host_InitCommands (void) 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"); diff --git a/netconn.c b/netconn.c index aa8bf94a..abc3501e 100755 --- a/netconn.c +++ b/netconn.c @@ -76,6 +76,9 @@ static unsigned char sv_message_buf[NET_MAXMESSAGE]; 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."}; @@ -655,6 +658,7 @@ qboolean NetConn_CanSend(netconn_t *conn) 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 @@ -664,7 +668,24 @@ qboolean NetConn_CanSend(netconn_t *conn) } } -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]; @@ -676,6 +697,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers 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; @@ -865,12 +888,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers } } - // 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; } @@ -1191,6 +1209,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s { 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; @@ -1198,9 +1217,19 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s } 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 @@ -1270,6 +1299,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s { 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; @@ -1277,9 +1307,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s } 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; @@ -1308,6 +1341,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s 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) @@ -1366,7 +1401,10 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s { 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)); @@ -1468,7 +1506,7 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_ 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); } } @@ -3681,6 +3719,9 @@ void NetConn_Init(void) 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); diff --git a/netconn.h b/netconn.h index 69b0de51..6744318e 100755 --- a/netconn.h +++ b/netconn.h @@ -128,6 +128,7 @@ typedef struct netgraphitem_s int reliablebytes; int unreliablebytes; int ackbytes; + double cleartime; } netgraphitem_t; @@ -207,6 +208,7 @@ typedef struct netconn_s // 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 @@ -413,9 +415,11 @@ extern cvar_t cl_netport; 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); diff --git a/server.h b/server.h index 669174bc..0dfaf034 100644 --- a/server.h +++ b/server.h @@ -203,6 +203,9 @@ typedef struct client_s /// 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; diff --git a/sv_main.c b/sv_main.c index 89c14f00..0c162703 100644 --- a/sv_main.c +++ b/sv_main.c @@ -1105,9 +1105,6 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection) 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) @@ -2291,6 +2288,7 @@ static void SV_SendClientDatagram (client_t *client) 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 @@ -2338,13 +2336,31 @@ static void SV_SendClientDatagram (client_t *client) // // 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; } @@ -2427,7 +2443,7 @@ static void SV_SendClientDatagram (client_t *client) 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) } -- 2.39.2