/* net_packetlog.c packet logging/parsing - for debugging and educational purposes **EXPERIMENTAL** Copyright (C) 2000 Jukka Sorjonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_IO_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_UNISTD_H # include #endif // FIXME: we did support Quake1 protocol too... #define QUAKEWORLD #include #include #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/msg.h" #include "QF/va.h" #include "QF/vfs.h" #include "compat.h" #include "net.h" #include "protocol.h" #include "server.h" cvar_t *net_packetlog; cvar_t *net_loglevel; //extern server_t sv; extern qboolean is_server; int Net_LogStart (const char *fname); void Analyze_Server_Packet (const byte * data, int len); void Analyze_Client_Packet (const byte * data, int len); void Parse_Server_Packet (void); void Parse_Client_Packet (void); void Net_LogStop (void); // note: this is SUPPOSED to be duplicate, like many others const char *svc_string[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's // console buffer the string // should be \n terminated "svc_setangle", // [vec3] set the view angle to this // absolute value "svc_serverdata", // [long] version ... "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] "svc_damage", // [byte] impact [byte] blood [vec3] // from "svc_spawnstatic", "svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", // "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_smallkick", // Quake svc_cutscene "svc_bigkick", "svc_updateping", "svc_updateentertime", "svc_updatestatlong", "svc_muzzleflash", "svc_updateuserinfo", "svc_download", "svc_playerinfo", "svc_nails", "svc_chokecount", "svc_modellist", "svc_soundlist", "svc_packetentities", "svc_deltapacketentities", "svc_maxspeed", "svc_entgravity", "svc_setinfo", "svc_serverinfo", "svc_updatepl", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL" }; const char *clc_string[] = { "clc_bad", "clc_nop", "clc_disconnect", "clc_move", "clc_stringcmd", "clc_delta", "clc_tmove", "clc_upload" }; #ifndef svc_spawnbinary # define svc_spawnbinary 21 #endif // Quake1, obsolete for QW #define svc_time 7 // [float] server time #define svc_updatename 13 // [byte] [string] #define svc_version 4 // [long] server version #define svc_clientdata 15 // #define svc_updatecolors 17 // [byte] [byte] #define svc_particle 18 // [vec3] #define svc_signonnum 25 // [byte] used for the signon // sequence static VFile *_stdout; static VFile *Net_PacketLog; static const char **Net_sound_precache; static sizebuf_t _packet; static msg_t packet = {0, 0, &_packet}; /* NET_LogPrintf Prints packet to logfile, adds time stamp etc. */ void Net_LogPrintf (char *fmt, ...) { char text[2048]; va_list argptr; va_start (argptr, fmt); vsnprintf (text, sizeof (text), fmt, argptr); va_end (argptr); if (!Net_PacketLog) return; Qprintf (Net_PacketLog, "%s", text); Qflush (Net_PacketLog); } int Net_LogStart (const char *fname) { char e_path[MAX_OSPATH]; Qexpand_squiggle (fs_userpath->string, e_path); Con_Printf ("Opening packet logfile: %s\n", fname); Net_PacketLog = Qopen (va ("%s/%s", e_path, fname), "at"); if (!Net_PacketLog) return -1; return 0; } void Net_LogStop (void) { if (Net_PacketLog) Qclose (Net_PacketLog); Net_PacketLog = NULL; } void hex_dump_buf (unsigned char *buf, int len) { int pos = 0, llen, i; while (pos < len) { llen = (len - pos < 16 ? len - pos : 16); Net_LogPrintf ("%08x: ", pos); for (i = 0; i < llen; i++) Net_LogPrintf ("%02x ", buf[pos + i]); for (i = 0; i < 16 - llen; i++) Net_LogPrintf (" "); Net_LogPrintf (" | "); for (i = 0; i < llen; i++) Net_LogPrintf ("%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); for (i = 0; i < 16 - llen; i++) Net_LogPrintf (" "); Net_LogPrintf ("\n"); pos += llen; } } void ascii_dump_buf (unsigned char *buf, int len) { int pos = 0, llen, i; while (pos < len) { llen = (len - pos < 60 ? len - pos : 60); Net_LogPrintf ("%08x: ", pos); for (i = 0; i < llen; i++) Net_LogPrintf ("%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); Net_LogPrintf ("\n"); pos += llen; } } void Log_Incoming_Packet (const char *p, int len) { if (!net_loglevel->int_val) return; if (is_server) { Net_LogPrintf ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: " "<<<<<<<<<<<<<<<<<<<<<<<<\n", len); if (net_loglevel->int_val != 3) hex_dump_buf ((unsigned char *) p, len); if (net_loglevel->int_val > 1) Analyze_Client_Packet (p, len); } else { Net_LogPrintf ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: " ">>>>>>>>>>>>>>>>>>>>>>>>\n", len); if (net_loglevel->int_val != 3) hex_dump_buf ((unsigned char *) p, len);; if (net_loglevel->int_val > 1) Analyze_Server_Packet (p, len); } return; } void Log_Outgoing_Packet (const char *p, int len) { if (!net_loglevel->int_val) return; if (is_server) { Net_LogPrintf ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: " ">>>>>>>>>>>>>>>>>>>>>>>>\n", len); if (net_loglevel->int_val != 3) hex_dump_buf ((unsigned char *) p, len);; if (net_loglevel->int_val > 1) Analyze_Server_Packet (p, len); } else { Net_LogPrintf ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: " "<<<<<<<<<<<<<<<<<<<<<<<<\n", len); if (net_loglevel->int_val != 3) hex_dump_buf ((unsigned char *) p, len);; if (net_loglevel->int_val > 1) Analyze_Client_Packet (p, len); } return; } void Log_Delta(int bits) { entity_state_t to; int i; Net_LogPrintf ("\n\t"); // set everything to the state we are delta'ing from to.number = bits & 511; bits &= ~511; if (bits & U_MOREBITS) { // read in the low order bits i = MSG_ReadByte (&packet); bits |= i; } // LordHavoc: Endy neglected to mark this as being part of the QSG // version 2 stuff... if (bits & U_EXTEND1) { bits |= MSG_ReadByte (&packet) << 16; if (bits & U_EXTEND2) bits |= MSG_ReadByte (&packet) << 24; } to.flags = bits; if (bits & U_MODEL) Net_LogPrintf (" MdlIdx: %d", MSG_ReadByte (&packet)); if (bits & U_FRAME) { to.frame = MSG_ReadByte (&packet); Net_LogPrintf (" Frame: %d", to.frame); } if (bits & U_COLORMAP) Net_LogPrintf (" Colormap: %d", MSG_ReadByte (&packet)); if (bits & U_SKIN) Net_LogPrintf (" Skinnum: %d", MSG_ReadByte (&packet)); if (bits & U_EFFECTS) { to.effects = MSG_ReadByte (&packet); Net_LogPrintf (" Effects: %d", to.effects); } if (bits & U_ORIGIN1) Net_LogPrintf (" X: %f", MSG_ReadCoord (&packet)); if (bits & U_ANGLE1) Net_LogPrintf (" Pitch: %d", MSG_ReadAngle (&packet)); if (bits & U_ORIGIN2) Net_LogPrintf (" Y: %f", MSG_ReadCoord (&packet)); if (bits & U_ANGLE2) Net_LogPrintf (" Yaw: %d", MSG_ReadAngle (&packet)); if (bits & U_ORIGIN3) Net_LogPrintf (" Z: %f", MSG_ReadCoord (&packet)); if (bits & U_ANGLE3) Net_LogPrintf (" Roll: %d", MSG_ReadAngle (&packet)); // Ender (QSG - Begin) if (bits & U_ALPHA) Net_LogPrintf(" Alpha: %d", MSG_ReadByte (&packet)); if (bits & U_SCALE) Net_LogPrintf(" Scale: %d", MSG_ReadByte (&packet)); if (bits & U_EFFECTS2) Net_LogPrintf(" U_EFFECTS2: %d", (to.effects & 0xFF) | (MSG_ReadByte (&packet) << 8)); if (bits & U_GLOWSIZE) Net_LogPrintf(" GlowSize: %d", MSG_ReadByte (&packet)); if (bits & U_GLOWCOLOR) Net_LogPrintf(" ColorGlow: %d", MSG_ReadByte (&packet)); if (bits & U_COLORMOD) Net_LogPrintf(" Colormod: %d", MSG_ReadByte (&packet)); if (bits & U_FRAME2) Net_LogPrintf(" Uframe2: %d", ((to.frame & 0xFF) | (MSG_ReadByte (&packet) << 8))); // Ender (QSG - End) return; } void Analyze_Server_Packet (const byte * data, int len) { if (!Net_PacketLog) Net_PacketLog = _stdout; packet.message->data = (byte*)data; packet.message->cursize = len; MSG_BeginReading (&packet); Parse_Server_Packet (); if (Net_PacketLog == _stdout) Net_PacketLog = NULL; } void Parse_Server_Packet () { char *s; int c, i, ii, iii, mask1, mask2; long seq1, seq2; seq1 = MSG_ReadLong (&packet); if (packet.badread) return; if (seq1 == -1) { Net_LogPrintf ("Special Packet"); } else { seq2 = MSG_ReadLong (&packet); // FIXME: display seqs right when reliable Net_LogPrintf ("\nSeq: %ld Ack: %ld ", seq1 & 0x7FFFFFFF, seq2 & 0x7FFFFFFF); if ((seq1 >> 31) & 0x01) Net_LogPrintf ("SV_REL "); if ((seq2 >> 31) & 0x01) Net_LogPrintf ("SV_RELACK"); Net_LogPrintf ("\n"); while (1) { if (packet.badread) break; c = MSG_ReadByte (&packet); if (c == -1) break; // Net_LogPrintf("\n<%ld,%ld> ",seq1 & 0x7FFFFFFF,seq2 & 0x7FFFFFFF); Net_LogPrintf ("<%06x> [0x%02x] ", MSG_GetReadCount (&packet), c); if (c < 53) Net_LogPrintf ("%s: ", svc_string[c]); // else Net_LogPrintf("(UNK: %d): ",c); if (MSG_GetReadCount (&packet) > packet.message->cursize) return; switch (c) { case svc_bad: Net_LogPrintf (" - should not happen"); case svc_nop: Net_LogPrintf (" No operation"); break; case svc_disconnect: Net_LogPrintf (" "); break; case svc_updatestat: i = MSG_ReadByte (&packet); Net_LogPrintf (" index: %d value: %d", i, MSG_ReadByte (&packet)); break; case svc_version: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #endif break; case svc_setview: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #else MSG_ReadShort (&packet); #endif break; case svc_sound: i = MSG_ReadShort (&packet); Net_LogPrintf (": (%d) ", i); if (i & SND_VOLUME) Net_LogPrintf ("Volume %d ", MSG_ReadByte (&packet)); if (i & SND_ATTENUATION) Net_LogPrintf ("Ann: %f", (float) MSG_ReadByte (&packet) / 64.0); ii = MSG_ReadByte (&packet); Net_LogPrintf ("%d (%s) ", ii, Net_sound_precache[ii]); Net_LogPrintf ("Pos: "); for (ii = 0; ii < 3; ii++) Net_LogPrintf ("%f ", MSG_ReadCoord (&packet)); Net_LogPrintf ("Ent: %d ", (i >> 3) & 1023); Net_LogPrintf ("Channel %d ", i & 7); break; case svc_time: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**\n"); #else MSG_ReadFloat (&packet); #endif break; case svc_print: // FIXME: i==PRINT_CHAT Net_LogPrintf (" [%d]", MSG_ReadByte (&packet)); Net_LogPrintf (" %s", MSG_ReadString (&packet)); break; case svc_stufftext: Net_LogPrintf ("%s", MSG_ReadString (&packet)); break; case svc_setangle: for (i = 0; i < 3; i++) Net_LogPrintf ("%f ", MSG_ReadAngle (&packet)); break; #ifdef QUAKEWORLD case svc_serverdata: Net_LogPrintf ("Ver: %ld", MSG_ReadLong (&packet)); Net_LogPrintf (" Client ID: %ld", MSG_ReadLong (&packet)); Net_LogPrintf (" Dir: %s", MSG_ReadString (&packet)); Net_LogPrintf (" User ID: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Map: %s", MSG_ReadString (&packet)); for (i = 0; i < 10; i++) MSG_ReadFloat (&packet); break; #endif case svc_lightstyle: i = MSG_ReadByte (&packet); if (i >= MAX_LIGHTSTYLES) return; Net_LogPrintf ("%d %s", i, MSG_ReadString (&packet)); break; case svc_updatename: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #else Net_LogPrintf ("%d %s", MSG_ReadByte (&packet), MSG_ReadString ()); #endif break; case svc_updatefrags: Net_LogPrintf ("player: %d frags: %d", MSG_ReadByte (&packet), MSG_ReadShort (&packet)); break; case svc_clientdata: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #endif break; case svc_stopsound: Net_LogPrintf ("%d", MSG_ReadShort (&packet)); break; case svc_updatecolors: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #else Net_LogPrintf ("%d %d", MSG_ReadByte (&packet), MSG_ReadByte ()); #endif break; case svc_particle: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #else for (i = 0; i < 3; i++) Net_LogPrintf (" %f", MSG_ReadCoord (&packet)); for (i = 0; i < 3; i++) Net_LogPrintf (" %d", MSG_ReadChar (&packet)); Net_LogPrintf (" Count: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Color: %d", MSG_ReadByte (&packet)); #endif break; case svc_damage: // FIXME: parse damage Net_LogPrintf ("armor: %d health: %d", MSG_ReadByte (&packet), MSG_ReadByte (&packet)); Net_LogPrintf (" from %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); break; case svc_spawnstatic: Net_LogPrintf ("Model: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Frame: %d Color: %d Skin: %", MSG_ReadByte (&packet), MSG_ReadByte (&packet), MSG_ReadByte (&packet)); for (i = 0; i < 3; i++) Net_LogPrintf ("%d: %f %f", i + 1, MSG_ReadCoord (&packet), MSG_ReadAngle (&packet)); break; case svc_spawnbinary: Net_LogPrintf ("**OBSOLETE**"); break; case svc_spawnbaseline: Net_LogPrintf ("%d", MSG_ReadShort (&packet)); Net_LogPrintf (" idx: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Frame: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Colormap: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Skin: %d", MSG_ReadByte (&packet)); for (i = 0; i < 3; i++) { Net_LogPrintf (" %f", MSG_ReadCoord (&packet)); Net_LogPrintf (" %d", MSG_ReadAngle (&packet)); }; break; case svc_temp_entity: i = MSG_ReadByte (&packet); switch (i) { case 0: case 1: case 3: case 4: case 7: case 8: case 10: case 11: case 13: Net_LogPrintf (" origin %f %f %f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); break; case 5: case 6: case 9: Net_LogPrintf (" created by %d", MSG_ReadShort (&packet)); Net_LogPrintf (" origin: %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); Net_LogPrintf (" trace endpos: %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); break; case 2: case 12: Net_LogPrintf (" count: %d", MSG_ReadByte (&packet)); printf (" origin: %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); break; default: Net_LogPrintf (" unknown value %d for tempentity", i); break; } break; case svc_setpause: Net_LogPrintf (" %d", MSG_ReadByte (&packet)); break; case svc_signonnum: #ifdef QUAKEWORLD Net_LogPrintf ("**QW OBSOLETE**"); #else Net_LogPrintf ("%d", MSG_ReadByte (&packet)); #endif break; case svc_centerprint: Net_LogPrintf (MSG_ReadString (&packet)); break; case svc_killedmonster: break; case svc_foundsecret: break; case svc_spawnstaticsound: Net_LogPrintf ("pos %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); Net_LogPrintf ("%d %d %d", MSG_ReadByte (&packet), MSG_ReadByte (&packet), MSG_ReadByte (&packet)); break; case svc_intermission: for (i = 0; i < 3; i++) Net_LogPrintf ("%f ", MSG_ReadCoord (&packet)); Net_LogPrintf ("\n"); for (i = 0; i < 3; i++) Net_LogPrintf ("%f ", MSG_ReadAngle (&packet)); break; case svc_finale: Net_LogPrintf ("%s", MSG_ReadString (&packet)); break; case svc_cdtrack: Net_LogPrintf ("%d", MSG_ReadByte (&packet)); break; case svc_sellscreen: break; case svc_smallkick: break; case svc_bigkick: break; case svc_updateping: Net_LogPrintf ("Player: %d ", MSG_ReadByte (&packet)); Net_LogPrintf ("Ping: %d", MSG_ReadShort (&packet)); break; case svc_updateentertime: Net_LogPrintf ("Player: %d ", MSG_ReadByte (&packet)); Net_LogPrintf ("Time: %f", MSG_ReadFloat (&packet)); break; case svc_updatestatlong: i = MSG_ReadByte (&packet); Net_LogPrintf ("%d value: %ld", i, MSG_ReadLong (&packet)); break; case svc_muzzleflash: Net_LogPrintf ("%d", MSG_ReadShort (&packet)); break; case svc_updateuserinfo: Net_LogPrintf ("Player: %d ", MSG_ReadByte (&packet)); Net_LogPrintf ("ID: %ld ", MSG_ReadLong (&packet)); Net_LogPrintf ("Info: %s", MSG_ReadString (&packet)); break; case svc_download: ii = MSG_ReadShort (&packet); Net_LogPrintf ("%d bytes at %d", ii, MSG_ReadByte (&packet)); for (i = 0; i < ii; i++) MSG_ReadByte (&packet); break; case svc_playerinfo: Net_LogPrintf ("\n\tPlayer: %d", MSG_ReadByte (&packet)); mask1 = MSG_ReadShort (&packet); Net_LogPrintf (" Mask1: %d", mask1); Net_LogPrintf (" Origin: %f,%f,%f", MSG_ReadCoord (&packet), MSG_ReadCoord (&packet), MSG_ReadCoord (&packet)); Net_LogPrintf (" Frame: %d", MSG_ReadByte (&packet)); if (mask1 & PF_MSEC) Net_LogPrintf (" Ping: %d", MSG_ReadByte (&packet)); if (mask1 & PF_COMMAND) { mask2 = MSG_ReadByte (&packet); // command if (mask2 & 0x01) Net_LogPrintf (" Pitch: %f", MSG_ReadAngle16 (&packet)); if (mask2 & 0x80) Net_LogPrintf (" Yaw: %f", MSG_ReadAngle16 (&packet)); if (mask2 & 0x02) Net_LogPrintf (" Roll: %f", MSG_ReadAngle16 (&packet)); if (mask2 & 0x04) Net_LogPrintf (" Speed1: %d", MSG_ReadShort (&packet)); if (mask2 & 0x08) Net_LogPrintf (" Speed2: %d", MSG_ReadShort (&packet)); if (mask2 & 0x10) Net_LogPrintf (" Speed3: %d", MSG_ReadShort (&packet)); if (mask2 & 0x20) Net_LogPrintf (" Flag: %d", MSG_ReadByte (&packet)); if (mask2 & 0x40) Net_LogPrintf (" Impulse: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Msec: %d", MSG_ReadByte (&packet)); } if (mask1 & PF_VELOCITY1) Net_LogPrintf (" Xspd: %f", MSG_ReadCoord (&packet)); if (mask1 & PF_VELOCITY2) Net_LogPrintf (" Yspd: %f", MSG_ReadCoord (&packet)); if (mask1 & PF_VELOCITY3) Net_LogPrintf (" ZSpd: %f", MSG_ReadCoord (&packet)); if (mask1 & PF_MODEL) Net_LogPrintf (" Model: %d", MSG_ReadByte (&packet)); if (mask1 & PF_SKINNUM) Net_LogPrintf (" Skin: %d", MSG_ReadByte (&packet)); if (mask1 & PF_EFFECTS) Net_LogPrintf (" Effects: %d", MSG_ReadByte (&packet)); if (mask1 & PF_WEAPONFRAME) Net_LogPrintf (" Weapon frame: %d", MSG_ReadByte (&packet)); break; case svc_nails: ii = MSG_ReadByte (&packet); Net_LogPrintf (" %d (bits not parsed)", ii); for (i = 0; i < ii; i++) { for (iii = 0; iii < 6; iii++) MSG_ReadByte (&packet); } break; case svc_chokecount: Net_LogPrintf ("%d", MSG_ReadByte (&packet)); break; case svc_modellist: ii = MSG_ReadByte (&packet); Net_LogPrintf ("start %d", ii); for (i = ii; i < 256; i++) { s = MSG_ReadString (&packet); if (packet.badread) break; if (!s) break; if (strlen (s) == 0) break; Net_LogPrintf ("\n\tModel %d: %s", i, s); } i = MSG_ReadByte (&packet); if (i) Net_LogPrintf ("\n\tnext at %d", i); else Net_LogPrintf ("\n\t*End of modellist*"); break; case svc_soundlist: ii = MSG_ReadByte (&packet); Net_LogPrintf ("start %d", ii); for (i = ii; i < 256; i++) { s = MSG_ReadString (&packet); if (packet.badread) break; if (!s) break; if (strlen (s) == 0) break; Net_LogPrintf ("\n\tSound %d: %s", i, s); } i = MSG_ReadByte (&packet); if (i) Net_LogPrintf ("\n\tnext at %d", i); else Net_LogPrintf ("\n\t*End of sound list*"); break; case svc_packetentities: while (1) { mask1 = (unsigned short) MSG_ReadShort(&packet); if (packet.badread) { Net_LogPrintf ("Badread\n"); return; } if (!mask1) break; if (mask1 & U_REMOVE) Net_LogPrintf("UREMOVE "); Log_Delta(mask1); } break; case svc_deltapacketentities: Net_LogPrintf ("idx: %d", MSG_ReadByte (&packet)); return; break; case svc_maxspeed: Net_LogPrintf ("%f", MSG_ReadFloat (&packet)); break; case svc_entgravity: Net_LogPrintf ("%f", MSG_ReadFloat (&packet)); break; case svc_setinfo: Net_LogPrintf ("Player: %d ", MSG_ReadByte (&packet)); Net_LogPrintf ("Keyname: %s ", MSG_ReadString (&packet)); Net_LogPrintf ("Value: %s", MSG_ReadString (&packet)); break; case svc_serverinfo: Net_LogPrintf ("Name: %s Value: %s", MSG_ReadString (&packet), MSG_ReadString (&packet)); break; case svc_updatepl: Net_LogPrintf ("Player: %d Ploss: %d", MSG_ReadByte (&packet), MSG_ReadByte (&packet)); break; default: Net_LogPrintf ("**UNKNOWN**: [%d]", c); break; } Net_LogPrintf ("\n"); } } } void Analyze_Client_Packet (const byte * data, int len) { if (!Net_PacketLog) Net_PacketLog = _stdout; packet.message->data = (byte*)data; packet.message->cursize = len; MSG_BeginReading (&packet); Parse_Client_Packet (); if (Net_PacketLog == _stdout) Net_PacketLog = NULL; } void Parse_Client_Packet (void) { int mask, i, c, ii; long seq1, seq2; seq1 = MSG_ReadLong (&packet); if (seq1 == -1) { Net_LogPrintf ("Special: %s\n", MSG_ReadString (&packet)); return; } else { // FIXME: display seqs right when reliable seq2 = MSG_ReadLong (&packet); Net_LogPrintf ("\nSeq: %ld Ack: %ld ", seq1 & 0x7FFFFFFF, seq2 & 0x7FFFFFFF); /* if ((seq1 >>31) &0x01) Net_LogPrintf("CL_REL "); if ((seq2 >>31) &0x01) Net_LogPrintf("CL_RELACK "); */ Net_LogPrintf ("QP: %u\n", MSG_ReadShort (&packet)); while (1) { if (packet.badread) break; c = MSG_ReadByte (&packet); if (c == -1) break; // Net_LogPrintf("<%ld,%ld> ",seq1 & 0x7FFFFFFF,seq2 & 0x7FFFFFFF); Net_LogPrintf ("\n<%06x> [0x%02x] ", MSG_GetReadCount (&packet), c); if (c < 8) Net_LogPrintf ("%s: ", clc_string[c]); switch (c) { case clc_nop: break; case clc_delta: Net_LogPrintf ("%d", MSG_ReadByte (&packet)); break; case clc_move: Net_LogPrintf ("checksum = %02x ", MSG_ReadByte (&packet)); Net_LogPrintf ("PacketLoss: %d", MSG_ReadByte (&packet)); for (i = 0; i < 3; i++) { mask = MSG_ReadByte (&packet); Net_LogPrintf ("\n\t(%d) mask = %02x", i, mask); if (mask & 0x01) Net_LogPrintf (" Tilt: %f", MSG_ReadAngle16 (&packet)); if (mask & 0x80) Net_LogPrintf (" Yaw: %f", MSG_ReadAngle16 (&packet)); if (mask & 0x02) Net_LogPrintf (" Roll: %f", MSG_ReadAngle16 (&packet)); if (mask & 0x04) Net_LogPrintf (" Fwd: %d", MSG_ReadShort (&packet)); if (mask & 0x08) Net_LogPrintf (" Right: %d", MSG_ReadShort (&packet)); if (mask & 0x10) Net_LogPrintf (" Up: %d", MSG_ReadShort (&packet)); if (mask & 0x20) Net_LogPrintf (" Flags: %d", MSG_ReadByte (&packet)); if (mask & 0x40) Net_LogPrintf (" Impulse: %d", MSG_ReadByte (&packet)); Net_LogPrintf (" Msec: %d", MSG_ReadByte (&packet)); } break; case clc_stringcmd: Net_LogPrintf ("%s", MSG_ReadString (&packet)); break; case clc_tmove: for (i = 0; i < 3; i++) Net_LogPrintf ("%f ", MSG_ReadCoord (&packet)); break; case clc_upload: ii = MSG_ReadShort (&packet); Net_LogPrintf ("%d bytes at %d", ii, MSG_ReadByte (&packet)); for (i = 0; i < ii; i++) MSG_ReadByte (&packet); break; default: Net_LogPrintf ("**UNKNOWN**: [%d]", c); break; } Net_LogPrintf ("\n"); } } } void Net_PacketLog_f (cvar_t *var) { if (var->int_val) { Net_LogStart ("qfpacket.log"); } else { Net_LogStop (); } } void Net_PacketLog_Zap_f (void) { if (Net_PacketLog && Net_PacketLog != _stdout) { Con_Printf ("truncating packet logfile: %s\n", "qfpacket.log"); Qseek (Net_PacketLog, 0, 0); Qwrite (Net_PacketLog, 0, 0); } else { char e_path[MAX_OSPATH]; Qexpand_squiggle (fs_userpath->string, e_path); Con_Printf ("Deleting packet logfile: %s\n", "qfpacket.log"); unlink (va ("%s/%s", e_path, "qfpacket.log")); } } int Net_Log_Init (const char **sound_precache) { Net_sound_precache = sound_precache; _stdout = Qdopen (1, "wt"); // create a QFile of stdout net_packetlog = Cvar_Get ("net_packetlog", "0", CVAR_NONE, Net_PacketLog_f, "enable/disable packet logging"); // 0 = no logging // 1 = hex dump only // 2 = parse/hexdump // 3 = just parse // 4 = parse/hexdump, skip movement/empty messages net_loglevel = Cvar_Get ("net_loglevel", "2", CVAR_NONE, NULL, "Packet logging/parsing"); Cmd_AddCommand ("net_packetlog_zap", Net_PacketLog_Zap_f, "clear the packet log file"); return 0; }