else
if(!strcasecmp(c, "pl"))
dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
+ else
+ if(!strcasecmp(c, "movementloss"))
+ dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
else
if(!strcasecmp(c, "entertime"))
dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
for (j = 0;j < NETGRAPH_PACKETS;j++)
if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
- packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+ packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
}
*/
void Host_Pings_f (void)
{
- int i, j, ping, packetloss;
+ int i, j, ping, packetloss, movementloss;
char temp[128];
if (!host_client->netconnection)
for (i = 0;i < svs.maxclients;i++)
{
packetloss = 0;
+ movementloss = 0;
if (svs.clients[i].netconnection)
+ {
for (j = 0;j < NETGRAPH_PACKETS;j++)
if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
- packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+ for (j = 0;j < NETGRAPH_PACKETS;j++)
+ if (svs.clients[i].movement_count[j] < 0)
+ movementloss++;
+ }
+ packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
+ movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
ping = (int)floor(svs.clients[i].ping*1000+0.5);
ping = bound(0, ping, 9999);
if (sv.protocol == PROTOCOL_QUAKEWORLD)
else
{
// write the string into the packet as multiple unterminated strings to avoid needing a local buffer
- dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
+ if(movementloss)
+ dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
+ else
+ dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
}
}
void Host_PingPLReport_f(void)
{
+ char **errbyte;
int i;
int l = Cmd_Argc();
if (l > cl.maxclients)
for (i = 0;i < l;i++)
{
cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
- cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
+ cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
+ if(errbyte && *errbyte == ',')
+ cl.scores[i].qw_movementloss = atoi(errbyte + 1);
+ else
+ cl.scores[i].qw_movementloss = 0;
}
}
// clear movement info until client enters the new level properly
memset(&client->cmd, 0, sizeof(client->cmd));
client->movesequence = 0;
+ client->movement_highestsequence_seen = 0;
+ memset(&client->movement_count, 0, sizeof(client->movement_count));
#ifdef NUM_PING_TIMES
for (i = 0;i < NUM_PING_TIMES;i++)
client->ping_times[i] = 0;
// (we have to buffer the moves because of old ones being repeated)
if (sv_numreadmoves < CL_MAX_USERCMDS)
sv_readmoves[sv_numreadmoves++] = *move;
+
+ // movement packet loss tracking
+ if(move->sequence)
+ {
+ if(move->sequence > host_client->movement_highestsequence_seen)
+ {
+ if(host_client->movement_highestsequence_seen)
+ {
+ // mark moves in between as lost
+ if(move->sequence - host_client->movement_highestsequence_seen < NETGRAPH_PACKETS)
+ for(i = host_client->movement_highestsequence_seen; i < move->sequence; ++i)
+ host_client->movement_count[i % NETGRAPH_PACKETS] = -1;
+ else
+ memset(host_client->movement_count, -1, sizeof(host_client->movement_count));
+ }
+ // mark THIS move as seen for the first time
+ host_client->movement_count[move->sequence % NETGRAPH_PACKETS] = 1;
+ // update highest sequence seen
+ host_client->movement_highestsequence_seen = move->sequence;
+ }
+ else
+ if(host_client->movement_count[move->sequence % NETGRAPH_PACKETS] >= 0)
+ ++host_client->movement_count[move->sequence % NETGRAPH_PACKETS];
+ }
+ else
+ {
+ host_client->movement_highestsequence_seen = 0;
+ memset(host_client->movement_count, 0, sizeof(host_client->movement_count));
+ }
}
void SV_ExecuteClientMoves(void)