From: havoc Date: Sun, 9 Jul 2006 08:34:39 +0000 (+0000) Subject: added ping and status command output parsing in the client, currently this is used... X-Git-Tag: xonotic-v0.1.0preview~3873 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=ae4a1dae90706789777f59e7c9c17cfdf1708d07;p=xonotic%2Fdarkplaces.git added ping and status command output parsing in the client, currently this is used to get pings in the scoreboard on Quake servers (and of course older DP servers), the server sends a pingplreport command after the human-readable ping report so that the packet loss display works (but only if the server supports this), changed client to send ping command instead of pings (at least until the next protocol version increase) and the client will not print ping report messages while the scoreboard is displayed. the status command parsing is not actually used at this time but could be the basis of iplog support git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6507 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_parse.c b/cl_parse.c index e9ef9b46..104696f1 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -2043,6 +2043,109 @@ void CL_ParseTempEntity(void) } } +// look for anything interesting like player IP addresses or ping reports +qboolean CL_ExaminePrintString(const char *text) +{ + const char *t; + if (!strcmp(text, "Client ping times:\n")) + { + cl.parsingtextmode = CL_PARSETEXTMODE_PING; + cl.parsingtextplayerindex = 0; + return !sb_showscores; + } + if (!strncmp(text, "host: ", 9)) + { + cl.parsingtextmode = CL_PARSETEXTMODE_STATUS; + cl.parsingtextplayerindex = 0; + return true; + } + if (cl.parsingtextmode == CL_PARSETEXTMODE_PING) + { + // if anything goes wrong, we'll assume this is not a ping report + cl.parsingtextmode = CL_PARSETEXTMODE_NONE; + t = text; + while (*t == ' ') + t++; + if (*t >= '0' && *t <= '9') + { + int ping = atoi(t); + while (*t >= '0' && *t <= '9') + t++; + if (*t == ' ') + { + int charindex = 0; + t++; + for (charindex = 0;cl.scores[cl.parsingtextplayerindex].name[charindex] == t[charindex];charindex++) + ; + if (cl.scores[cl.parsingtextplayerindex].name[charindex] == 0 && t[charindex] == '\n') + { + cl.scores[cl.parsingtextplayerindex].qw_ping = bound(0, ping, 9999); + for (cl.parsingtextplayerindex++;cl.parsingtextplayerindex < cl.maxclients && !cl.scores[cl.parsingtextplayerindex].name[0];cl.parsingtextplayerindex++) + ; + if (cl.parsingtextplayerindex < cl.maxclients) + { + // we parsed a valid ping entry, so expect another to follow + cl.parsingtextmode = CL_PARSETEXTMODE_PING; + } + return !sb_showscores; + } + } + } + } + if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS) + { + if (!strncmp(text, "players: ", 9)) + { + cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERID; + cl.parsingtextplayerindex = 0; + return true; + } + else if (!strstr(text, ": ")) + { + cl.parsingtextmode = CL_PARSETEXTMODE_NONE; // status report ended + return true; + } + } + if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS_PLAYERID) + { + // if anything goes wrong, we'll assume this is not a status report + cl.parsingtextmode = CL_PARSETEXTMODE_NONE; + if (text[0] == '#' && text[1] >= '0' && text[1] <= '9') + { + t = text + 1; + cl.parsingtextplayerindex = atoi(t); + while (*t >= '0' && *t <= '9') + t++; + if (*t == ' ') + { + cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERIP; + return true; + } + } + } + if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS_PLAYERIP) + { + // if anything goes wrong, we'll assume this is not a status report + cl.parsingtextmode = CL_PARSETEXTMODE_NONE; + if (text[0] == ' ') + { + t = text; + while (*t == ' ') + t++; + // botclient is perfectly valid, but we don't care about bots + if (strcmp(t, "botclient\n")) + { + lhnetaddress_t address; + LHNETADDRESS_FromString(&address, t, 0); + // TODO: log the IP address and player name? + } + cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERID; + return true; + } + } + return true; +} + #define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s\n", msg_readcount-1, x); //[515]: csqc @@ -2178,10 +2281,14 @@ void CL_ParseServerMessage(void) case qw_svc_print: i = MSG_ReadByte(); - if (i == 3) // chat - CSQC_AddPrintText(va("\1%s", MSG_ReadString())); //[515]: csqc - else - CSQC_AddPrintText(MSG_ReadString()); + temp = MSG_ReadString(); + if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports + { + if (i == 3) // chat + CSQC_AddPrintText(va("\1%s", temp)); //[515]: csqc + else + CSQC_AddPrintText(temp); + } break; case qw_svc_centerprint: @@ -2542,7 +2649,9 @@ void CL_ParseServerMessage(void) break; case svc_print: - CSQC_AddPrintText(MSG_ReadString()); //[515]: csqc + temp = MSG_ReadString(); + if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports + CSQC_AddPrintText(temp); //[515]: csqc break; case svc_centerprint: diff --git a/client.h b/client.h index a35160d7..4b85bcf2 100644 --- a/client.h +++ b/client.h @@ -595,6 +595,16 @@ typedef struct particle_s } particle_t; +typedef enum cl_parsingtextmode_e +{ + CL_PARSETEXTMODE_NONE, + CL_PARSETEXTMODE_PING, + CL_PARSETEXTMODE_STATUS, + CL_PARSETEXTMODE_STATUS_PLAYERID, + CL_PARSETEXTMODE_STATUS_PLAYERIP +} +cl_parsingtextmode_t; + // // the client_state_t structure is wiped completely at every // server signon @@ -773,6 +783,10 @@ typedef struct client_state_s // [cl.maxclients] scoreboard_t *scores; + // keep track of svc_print parsing state (analyzes ping reports and status reports) + cl_parsingtextmode_t parsingtextmode; + int parsingtextplayerindex; + // entity database stuff // latest received entity frame numbers #define LATESTFRAMENUMS 3 @@ -1125,6 +1139,8 @@ void R_NewExplosion(const vec3_t org); #include "cl_screen.h" +extern qboolean sb_showscores; + #define NUMCROSSHAIRS 32 extern cachepic_t *r_crosshairs[NUMCROSSHAIRS+1]; diff --git a/host_cmd.c b/host_cmd.c index 8e6930d3..f23c658d 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -55,7 +55,8 @@ void Host_Status_f (void) if (cmd_source == src_command) { - if (!sv.active) + // if running a client, try to send over network so the client's status report parser will see the report + if (cls.state == ca_connected) { Cmd_ForwardToServer (); return; @@ -65,6 +66,9 @@ void Host_Status_f (void) else print = SV_ClientPrintf; + if (!sv.active) + return; + for (players = 0, j = 0;j < svs.maxclients;j++) if (svs.clients[j].active) players++; @@ -88,7 +92,7 @@ void Host_Status_f (void) } else hours = 0; - print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->fields.server->frags, hours, minutes, seconds); + print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->fields.server->frags, hours, minutes, seconds); print (" %s\n", client->netconnection ? client->netconnection->address : "botclient"); } } @@ -213,6 +217,7 @@ Host_Ping_f ================== */ +void Host_Pings_f (void); // called by Host_Ping_f void Host_Ping_f (void) { int i; @@ -221,7 +226,8 @@ void Host_Ping_f (void) if (cmd_source == src_command) { - if (!sv.active) + // if running a client, try to send over network so the client's ping report parser will see the report + if (cls.state == ca_connected) { Cmd_ForwardToServer (); return; @@ -231,6 +237,9 @@ void Host_Ping_f (void) else print = SV_ClientPrintf; + if (!sv.active) + return; + print("Client ping times:\n"); for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++) { @@ -238,6 +247,9 @@ void Host_Ping_f (void) continue; print("%4i %s\n", (int)floor(client->ping*1000+0.5), client->name); } + + // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report) + Host_Pings_f(); } /* diff --git a/sbar.c b/sbar.c index 56f0813b..e3499217 100644 --- a/sbar.c +++ b/sbar.c @@ -1408,8 +1408,15 @@ void Sbar_DeathmatchOverlay (void) MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); MSG_WriteString(&cls.netcon->message, "pings"); } + else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5 || cls.protocol == PROTOCOL_DARKPLACES6 || cls.protocol == PROTOCOL_DARKPLACES7) + { + // these servers usually lack the pings command and so a less efficient "ping" command must be sent, which on modern DP servers will also reply with a pingplreport command after the ping listing + MSG_WriteByte(&cls.netcon->message, clc_stringcmd); + MSG_WriteString(&cls.netcon->message, "ping"); + } else { + // newer server definitely has pings command, so use it for more efficiency MSG_WriteByte(&cls.netcon->message, clc_stringcmd); MSG_WriteString(&cls.netcon->message, "pings"); }