diff --git a/code/client/cl_main.cpp b/code/client/cl_main.cpp index 5e759a1e..839761d0 100644 --- a/code/client/cl_main.cpp +++ b/code/client/cl_main.cpp @@ -56,6 +56,8 @@ cvar_t *cl_showTimeDelta; cvar_t *cl_freezeDemo; cvar_t *cl_shownet; +cvar_t *cl_netprofile; +cvar_t *cl_netprofileoverlay; cvar_t *cl_showSend; cvar_t *cl_timedemo; cvar_t *cl_timedemoLog; @@ -118,6 +120,7 @@ cvar_t *cl_r_fullscreen; cvar_t *cl_consoleKeys; cvar_t *name; +cvar_t *cl_rate; clientActive_t cl; clientConnection_t clc; @@ -1057,7 +1060,7 @@ void CL_RequestMotd( void ) { Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); Info_SetValueForKey( info, "version", com_version->string ); - NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); + CL_NET_OutOfBandPrint( cls.updateServer, "getmotd \"%s\"\n", info ); } /* @@ -1140,7 +1143,7 @@ void CL_RequestAuthorization( void ) { fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO ); - NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, "getKeyAuthorize %i %s", fs->integer, nums ); + CL_NET_OutOfBandPrint(cls.authorizeServer, "getKeyAuthorize %i %s", fs->integer, nums ); } /* @@ -1476,7 +1479,11 @@ void CL_Rcon_f( void ) { } } - NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to); + NET_SendPacket (NS_CLIENT, strlen(message) + 1, message, to); + + if (cl_netprofile->integer) { + NetProfileAddPacket(&cls.netprofile.inPackets, strlen(message) + 1, NETPROF_PACKET_MESSAGE); + } } /* @@ -1979,12 +1986,12 @@ void CL_CheckForResend( void ) { // if ( !Sys_IsLANAddress( clc.serverAddress ) ) { // CL_RequestAuthorization(); // } - NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge"); + CL_NET_OutOfBandPrint(clc.serverAddress, "getchallenge"); break; case CA_AUTHORIZING: // resend the cd key authorization gcd_compute_response(cl_cdkey, Cmd_Argv(1), cls.gcdResponse, CDResponseMethod_REAUTH); - NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "authorizeThis %s", cls.gcdResponse); + CL_NET_OutOfBandPrint(clc.serverAddress, "authorizeThis %s", cls.gcdResponse); break; case CA_CHALLENGING: /* @@ -2247,6 +2254,10 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { char *s; char *c; const char *reason; + + if (cl_netprofile->integer) { + NetProfileAddPacket(&cls.netprofile.inPackets, msg->cursize, NETPROF_PACKET_MESSAGE); + } MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 @@ -2331,7 +2342,7 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { // echo request from server if ( !Q_stricmp(c, "echo") ) { - NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); + CL_NET_OutOfBandPrint(from, "%s", Cmd_Argv(1) ); return; } @@ -2339,7 +2350,7 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { if ( !Q_stricmp(c, "getKey") ) { clc.state = CA_AUTHORIZING; gcd_compute_response(cl_cdkey, Cmd_Argv(1), cls.gcdResponse, CDResponseMethod_NEWAUTH); - NET_OutOfBandPrint(NS_CLIENT, from, "authorizeThis %s", cls.gcdResponse); + CL_NET_OutOfBandPrint(from, "authorizeThis %s", cls.gcdResponse); return; } @@ -2666,6 +2677,31 @@ void CL_Frame ( int msec ) { cls.timeScaled = com_timescale->integer != 1; + // + // Added in 2.0: network profiling + // + if (cl_netprofile->integer) { + if (!cls.netprofile.initialized) { + memset(&cls.netprofile, 0, sizeof(cls.netprofile)); + cls.netprofile.initialized = qtrue; + } + + if (cls.netprofile.rate != cl_rate->integer) { + cls.netprofile.rate = cl_rate->integer; + + if (cls.netprofile.rate < 1000) { + cls.netprofile.rate = 1000; + } else if (cls.netprofile.rate > 90000) { + cls.netprofile.rate = 90000; + } + } + + cls.netprofile.outPackets.updateTime = Com_Milliseconds(); + cls.netprofile.inPackets.updateTime = cls.netprofile.outPackets.updateTime; + } else { + cls.netprofile.initialized = qfalse; + } + // see if we need to update any userinfo CL_CheckUserinfo(); @@ -3330,6 +3366,8 @@ void CL_Init( void ) { cl_master = Cvar_Get ("cl_master", MASTER_SERVER_NAME, CVAR_ARCHIVE); cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); + cl_netprofile = Cvar_Get("cl_netprofile", "0", CVAR_TEMP); + cl_netprofileoverlay = Cvar_Get("cl_netprofileoverlay", "0", CVAR_TEMP); cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP ); cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP ); @@ -3445,7 +3483,7 @@ void CL_Init( void ) { // userinfo name = Cvar_Get ("name", "UnnamedSoldier", CVAR_USERINFO | CVAR_ARCHIVE ); - Cvar_Get ("rate", "5000", CVAR_USERINFO | CVAR_ARCHIVE ); + cl_rate = Cvar_Get ("rate", "5000", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("password", "", CVAR_USERINFO); Cvar_Get ("dm_playermodel", "american_army", CVAR_USERINFO | CVAR_ARCHIVE ); @@ -3830,7 +3868,7 @@ int CL_ServerStatus( const char *serverAddress, char *serverStatusString, int ma serverStatus->retrieved = qfalse; serverStatus->time = 0; serverStatus->startTime = Com_Milliseconds(); - NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + CL_NET_OutOfBandPrint(to, "getstatus" ); return qfalse; } } @@ -3842,7 +3880,7 @@ int CL_ServerStatus( const char *serverAddress, char *serverStatusString, int ma serverStatus->retrieved = qfalse; serverStatus->startTime = Com_Milliseconds(); serverStatus->time = 0; - NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + CL_NET_OutOfBandPrint(to, "getstatus" ); return qfalse; } return qfalse; @@ -3981,9 +4019,18 @@ void CL_LocalServers_f( void ) { to.type = NA_BROADCAST; NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + + if (cl_netprofile->integer) { + NetProfileAddPacket(&cls.netprofile.inPackets, strlen( message ), NETPROF_PACKET_MESSAGE); + } + // Added in OPM (from ioquake3) to.type = NA_MULTICAST6; NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + + if (cl_netprofile->integer) { + NetProfileAddPacket(&cls.netprofile.inPackets, strlen(message), NETPROF_PACKET_MESSAGE); + } } } } @@ -4087,7 +4134,7 @@ void CL_GlobalServers_f( void ) { Q_strcat(command, sizeof(command), Cmd_Argv(i)); } - NET_OutOfBandPrint( NS_SERVER, to, "%s", command ); + CL_NET_OutOfBandPrint( to, "%s", command ); } @@ -4307,7 +4354,7 @@ void CL_Ping_f( void ) { CL_SetServerInfoByAddress(pingptr->adr, NULL, 0); - NET_OutOfBandPrint( NS_CLIENT, to, "\x02getinfo xxx" ); + CL_NET_OutOfBandPrint( to, "\x02getinfo xxx" ); } /* @@ -4477,7 +4524,7 @@ qboolean CL_UpdateVisiblePings_f(int source) { memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t)); cl_pinglist[j].start = Sys_Milliseconds(); cl_pinglist[j].time = 0; - NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" ); + CL_NET_OutOfBandPrint( cl_pinglist[j].adr, "getinfo xxx" ); slots++; } } @@ -4565,7 +4612,7 @@ void CL_ServerStatus_f(void) { return; } - NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" ); + CL_NET_OutOfBandPrint( *toptr, "getstatus" ); serverStatus = CL_GetServerStatus( *toptr ); serverStatus->address = *toptr; diff --git a/code/client/cl_net_chan.cpp b/code/client/cl_net_chan.cpp index 1433c434..102d5524 100644 --- a/code/client/cl_net_chan.cpp +++ b/code/client/cl_net_chan.cpp @@ -132,7 +132,7 @@ CL_Netchan_TransmitNextFragment ================= */ void CL_Netchan_TransmitNextFragment( netchan_t *chan ) { - Netchan_TransmitNextFragment( chan ); + Netchan_TransmitNextFragment( chan, cl_netprofile->integer ? &cls.netprofile.inPackets : NULL ); } /* @@ -144,7 +144,7 @@ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); CL_Netchan_Encode( msg ); - Netchan_Transmit( chan, msg->cursize, msg->data ); + Netchan_Transmit( chan, msg->cursize, msg->data, cl_netprofile->integer ? &cls.netprofile.inPackets : NULL ); } extern int oldsize; @@ -158,10 +158,30 @@ CL_Netchan_Process qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { int ret; - ret = Netchan_Process( chan, msg ); + ret = Netchan_Process( chan, msg, cl_netprofile->integer ? &cls.netprofile.outPackets : NULL ); if (!ret) return qfalse; CL_Netchan_Decode( msg ); newsize += msg->cursize; return qtrue; } + +/* +================= +CL_NET_OutOfBandPrint +================= +*/ +void CL_NET_OutOfBandPrint(netadr_t adr, const char* format, ...) { + va_list argptr; + char string[MAX_MSGLEN]; + + va_start(argptr, format); + Q_vsnprintf(string, sizeof(string), format, argptr); + va_end(argptr); + + NET_OutOfBandPrint(NS_CLIENT, adr, "%s", string); + + if (cl_netprofile->integer) { + NetProfileAddPacket(&cls.netprofile.inPackets, strlen(string), NETPROF_PACKET_MESSAGE); + } +} diff --git a/code/client/cl_ui.cpp b/code/client/cl_ui.cpp index 4997e31a..ba6bb837 100644 --- a/code/client/cl_ui.cpp +++ b/code/client/cl_ui.cpp @@ -3041,14 +3041,14 @@ void CL_PingServers_f(void) if (!noudp->integer) { adr.type = NA_BROADCAST; adr.port = BigShort(12203); - NET_OutOfBandPrint(NS_SERVER, adr, "info %i", 8); + CL_NET_OutOfBandPrint(adr, "info %i", 8); } noipx = Cvar_Get("noipx", "0", CVAR_INIT); if (!noipx->integer) { adr.type = NA_BROADCAST_IPX; adr.port = BigShort(12203); - NET_OutOfBandPrint(NS_SERVER, adr, "info %i", 8); + CL_NET_OutOfBandPrint(adr, "info %i", 8); } for (i = 0; i < 16; i++) { @@ -3063,7 +3063,7 @@ void CL_PingServers_f(void) adr.port = BigShort(12203); } - NET_OutOfBandPrint(NS_SERVER, adr, "info %i", 8); + CL_NET_OutOfBandPrint(adr, "info %i", 8); } else { Com_Printf("Bad address: %s\n", adrstring); } diff --git a/code/client/cl_uiview3d.cpp b/code/client/cl_uiview3d.cpp index f42f3e7a..234e89a7 100644 --- a/code/client/cl_uiview3d.cpp +++ b/code/client/cl_uiview3d.cpp @@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "cl_ui.h" #include "../qcommon/localization.h" +#include "../server/server.h" + CLASS_DECLARATION(UIWidget, View3D, NULL) { {&W_Activated, &View3D::OnActivate }, {&W_Deactivated, &View3D::OnDeactivate}, @@ -241,13 +243,262 @@ void View3D::DrawSoundOverlay(void) setFont("verdana-14"); m_font->setColor(UWhite); - // FIXME: TODO + // FIXME: Unimplemented if (sound_overlay->integer) { Com_Printf("sound_overlay isn't supported with OpenAL/SDL right now.\n"); Cvar_Set("sound_overlay", "0"); } } +void DisplayServerNetProfileInfo(UIFont *font, float y, netprofclient_t *netprofile) +{ + font->Print(104, y, va("%i", netprofile->inPackets.packetsPerSec)); + font->Print(144, y, va("%i", netprofile->outPackets.packetsPerSec)); + font->Print(184, y, va("%i", netprofile->inPackets.packetsPerSec + netprofile->outPackets.packetsPerSec)); + font->Print(234, y, va("%i", netprofile->inPackets.percentFragmented)); + font->Print(264, y, va("%i", netprofile->outPackets.percentFragmented)); + font->Print( + 294, + y, + va("%i", + (unsigned int)((float)(netprofile->outPackets.numFragmented + netprofile->inPackets.numFragmented) + / (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed) + )), + -1, + qfalse + ); + font->Print(334, y, va("%i", netprofile->inPackets.percentDropped)); + font->Print(364, y, va("%i", netprofile->outPackets.percentDropped)); + font->Print( + 394, + y, + va("%i", + (unsigned int)((float)(netprofile->outPackets.numDropped + netprofile->inPackets.numDropped) + / (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed) + )), + -1, + qfalse + ); + font->Print(434, y, va("%i", netprofile->inPackets.percentDropped)); + font->Print(464, y, va("%i", netprofile->outPackets.percentDropped)); + font->Print( + 494, + y, + va("%i", + (unsigned int)((float)(netprofile->outPackets.totalLengthConnectionLess + + netprofile->inPackets.totalLengthConnectionLess) + / (float)(netprofile->outPackets.totalSize + netprofile->inPackets.totalSize))), + -1, + qfalse + ); + font->Print(534, y, va("%i", netprofile->inPackets.bytesPerSec)); + font->Print(594, y, va("%i", netprofile->outPackets.bytesPerSec)); + font->Print(654, y, va("%i", netprofile->outPackets.bytesPerSec + netprofile->inPackets.bytesPerSec)); + font->Print(714, y, va("%i", netprofile->rate)); +} + +void DisplayClientNetProfile(UIFont *font, float x, float y, netprofclient_t *netprofile) +{ + float columns[5]; + float fontHeight; + float columnHeight; + + fontHeight = font->getHeight(qfalse); + columns[0] = x + 120; + columns[1] = x + 230; + columns[2] = x + 330; + columns[3] = x + 430; + columns[4] = x + 530; + columnHeight = y; + + font->Print(x, y, va("Rate: %i", netprofile->rate)); + + columnHeight += fontHeight * 1.5; + font->Print(x, columnHeight, "Data Type"); + font->Print(columns[0], columnHeight, "Packets per Sec"); + font->Print(columns[1], columnHeight, "% Fragmented"); + font->Print(columns[2], columnHeight, "% Dropped"); + font->Print(columns[3], columnHeight, "% OOB data"); + font->Print(columns[4], columnHeight, "Data per Sec"); + + columnHeight += fontHeight * 0.5; + font->Print(x, columnHeight, "----------"); + font->Print(columns[0], columnHeight, "----------"); + font->Print(columns[1], columnHeight, "----------"); + font->Print(columns[2], columnHeight, "----------"); + font->Print(columns[3], columnHeight, "----------"); + font->Print(columns[4], columnHeight, "----------"); + + columnHeight += fontHeight; + font->Print(x, columnHeight, "Data In"); + font->Print(columns[0], columnHeight, va("%i", netprofile->outPackets.packetsPerSec)); + font->Print(columns[1], columnHeight, va("%i%%", netprofile->outPackets.percentFragmented)); + font->Print(columns[2], columnHeight, va("%i%%", netprofile->outPackets.percentDropped)); + font->Print(columns[3], columnHeight, va("%i%%", netprofile->outPackets.percentConnectionLess)); + font->Print(columns[4], columnHeight, va("%i", netprofile->outPackets.bytesPerSec)); + + columnHeight += fontHeight; + font->Print(x, columnHeight, "Data Out"); + font->Print(columns[0], columnHeight, va("%i", netprofile->inPackets.packetsPerSec)); + font->Print(columns[1], columnHeight, va("%i%%", netprofile->inPackets.percentFragmented)); + font->Print(columns[2], columnHeight, va("%i%%", netprofile->inPackets.percentDropped)); + font->Print(columns[3], columnHeight, va("%i%%", netprofile->inPackets.percentConnectionLess)); + font->Print(columns[4], columnHeight, va("%i", netprofile->inPackets.bytesPerSec)); + + columnHeight += fontHeight; + + font->Print(x, columnHeight, "Total Data"); + + font->Print( + columns[0], + columnHeight, + va("%i", netprofile->inPackets.packetsPerSec + netprofile->outPackets.packetsPerSec) + ); + font->Print( + columns[1], + columnHeight, + va("%i%%", + (unsigned int)((float)(netprofile->outPackets.numFragmented + netprofile->inPackets.numFragmented) + / (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed) + )) + ); + font->Print( + columns[2], + columnHeight, + va("%i%%", + (unsigned int)((float)(netprofile->outPackets.numDropped + netprofile->inPackets.numDropped) + / (double)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed + ))) + ); + font->Print( + columns[3], + columnHeight, + va("%i%%", + (unsigned int)((float)(netprofile->outPackets.totalLengthConnectionLess + + netprofile->inPackets.totalLengthConnectionLess) + / (float)(netprofile->outPackets.totalSize + netprofile->inPackets.totalSize))) + ); + font->Print( + columns[4], + columnHeight, + va("%i", netprofile->inPackets.bytesPerSec + netprofile->outPackets.bytesPerSec) + ); +} + +void View3D::DrawNetProfile(void) +{ + float fontHeight; + float yOffset; + int i; + + if (sv_netprofileoverlay->integer && sv_netprofile->integer && com_sv_running->integer) { + float columnHeight; + float valueHeight; + float separatorHeight; + float categoryHeight; + float currentHeight; + netprofclient_t netproftotal; + + setFont("verdana-14"); + m_font->setColor(UWhite); + + fontHeight = m_font->getHeight(qfalse); + yOffset = sv_netprofileoverlay->integer + 8; + + if (svs.netprofile.rate) { + m_font->Print(8, yOffset, va("Server Net Profile Max Rate: %i", svs.netprofile.rate), -1, qfalse); + } else { + m_font->Print(8, yOffset, "Server Net Profile Max Rate: none", -1, qfalse); + } + + columnHeight = fontHeight + fontHeight + yOffset; + valueHeight = columnHeight + fontHeight; + separatorHeight = fontHeight * 1.5 + columnHeight; + categoryHeight = fontHeight * 0.5 + columnHeight; + + m_font->Print(8, categoryHeight, "Data Source"); + m_font->Print(8, separatorHeight, "---------------"); + m_font->Print(104, columnHeight, "Packets per Sec"); + m_font->Print(104, valueHeight, "In"); + m_font->Print(144, valueHeight, "Out"); + m_font->Print(184, valueHeight, "Total"); + m_font->Print(104, separatorHeight, "---"); + m_font->Print(144, separatorHeight, "-----"); + m_font->Print(184, separatorHeight, "------"); + m_font->Print(234, columnHeight, "% Fragmented"); + m_font->Print(234, valueHeight, "In"); + m_font->Print(264, valueHeight, "Out"); + m_font->Print(294, valueHeight, "Total"); + m_font->Print(234, separatorHeight, "---"); + m_font->Print(264, separatorHeight, "-----"); + m_font->Print(294, separatorHeight, "------"); + m_font->Print(334, columnHeight, "% Dropped"); + m_font->Print(334, valueHeight, "In"); + m_font->Print(364, valueHeight, "Out"); + m_font->Print(394, valueHeight, "Total"); + m_font->Print(334, separatorHeight, "---"); + m_font->Print(364, separatorHeight, "-----"); + m_font->Print(394, separatorHeight, "------"); + m_font->Print(434, columnHeight, "% OOB Data"); + m_font->Print(434, valueHeight, "In"); + m_font->Print(464, valueHeight, "Out"); + m_font->Print(494, valueHeight, "Total"); + m_font->Print(434, separatorHeight, "---"); + m_font->Print(464, separatorHeight, "-----"); + m_font->Print(494, separatorHeight, "------"); + m_font->Print(534, columnHeight, "Data per Sec"); + m_font->Print(534, valueHeight, "In"); + m_font->Print(594, valueHeight, "Out"); + m_font->Print(654, valueHeight, "Total"); + m_font->Print(534, separatorHeight, "---"); + m_font->Print(594, separatorHeight, "-----"); + m_font->Print(654, separatorHeight, "------"); + m_font->Print(714, categoryHeight, "Rate"); + m_font->Print(714, separatorHeight, "------"); + + currentHeight = fontHeight * 2.5 + columnHeight; + SV_NET_CalcTotalNetProfile(&netproftotal, qfalse); + m_font->Print(8, columnHeight + fontHeight * 2.5, "Total"); + DisplayServerNetProfileInfo(m_font, currentHeight, &netproftotal); + + currentHeight += fontHeight * 1.5; + m_font->Print(8, currentHeight, "Clientless"); + DisplayServerNetProfileInfo(m_font, currentHeight, &svs.netprofile); + + currentHeight += fontHeight; + + for (i = 0; i < svs.iNumClients; i++) { + client_t *client = &svs.clients[i]; + if (client->state != CS_ACTIVE || !client->gentity) { + continue; + } + + if (client->netchan.remoteAddress.type == NA_LOOPBACK) { + m_font->Print(8.0, currentHeight, va("#%i-Loopback", i), -1, 0); + + } else { + m_font->Print(8.0, currentHeight, va("Client #%i", i), -1, 0); + } + + DisplayServerNetProfileInfo(m_font, currentHeight, &client->netprofile); + currentHeight = currentHeight + fontHeight; + } + } else if (cl_netprofileoverlay->integer && cl_netprofile->integer && com_cl_running->integer) { + setFont("verdana-14"); + m_font->setColor(UWhite); + + fontHeight = m_font->getHeight(qfalse); + yOffset = cl_netprofileoverlay->integer + 16; + + m_font->Print(16, yOffset, "Client Net Profile", -1, qfalse); + + NetProfileCalcStats(&cls.netprofile.outPackets, 500); + NetProfileCalcStats(&cls.netprofile.inPackets, 500); + + DisplayClientNetProfile(m_font, 16, yOffset + fontHeight * 2, &cls.netprofile); + } +} + void View3D::Draw2D(void) { if (!cls.no_menus) { @@ -271,6 +522,7 @@ void View3D::Draw2D(void) if (!cls.no_menus) { DrawSoundOverlay(); + DrawNetProfile(); DrawSubtitleOverlay(); } } diff --git a/code/client/cl_uiview3d.h b/code/client/cl_uiview3d.h index fc908a9f..ef11c986 100644 --- a/code/client/cl_uiview3d.h +++ b/code/client/cl_uiview3d.h @@ -51,6 +51,9 @@ protected: void OnDeactivate(Event *ev); void DrawSubtitleOverlay(void); + // Added in 2.0 + void DrawNetProfile(void); + public: View3D(); diff --git a/code/client/client.h b/code/client/client.h index b33b123a..92509338 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -383,8 +383,8 @@ typedef struct { void* savedCgameState; size_t savedCgameStateSize; char gcdResponse[73]; - int unknown3_0; - int currentRate; + // Added in 2.0 + netprofclient_t netprofile; qhandle_t charSetShader; qhandle_t whiteShader; qhandle_t consoleShader; @@ -415,6 +415,8 @@ extern cvar_t *cl_connect_timeout; extern cvar_t *cl_maxpackets; extern cvar_t *cl_packetdup; extern cvar_t *cl_shownet; +extern cvar_t *cl_netprofile; +extern cvar_t *cl_netprofileoverlay; extern cvar_t *cl_showSend; extern cvar_t *cl_timeNudge; extern cvar_t *cl_showTimeDelta; @@ -732,6 +734,7 @@ void LAN_SaveServersToCache( void ); void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data ); void CL_Netchan_TransmitNextFragment( netchan_t *chan ); qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ); +void CL_NET_OutOfBandPrint( netadr_t adr, const char* format, ... ); // // cl_avi.c diff --git a/code/gamespy/sv_gamespy.c b/code/gamespy/sv_gamespy.c index 58a245a5..e72c26b7 100644 --- a/code/gamespy/sv_gamespy.c +++ b/code/gamespy/sv_gamespy.c @@ -382,7 +382,7 @@ void AuthenticateCallback(int gameid, int localid, int authenticated, char* errm challenge->cdkeyState = 2; challenge->pingTime = svs.time; - NET_OutOfBandPrint(NS_SERVER, challenge->adr, "challengeResponse %i", challenge->challenge); + SV_NET_OutOfBandPrint(&svs.netprofile, challenge->adr, "challengeResponse %i", challenge->challenge); } else { @@ -396,7 +396,7 @@ void AuthenticateCallback(int gameid, int localid, int authenticated, char* errm Com_Printf("%s failed cdkey authorization\n", buf); challenge->cdkeyState = 3; // tell the client about the reason - NET_OutOfBandPrint(NS_SERVER, challenge->adr, "droperror\nServer rejected connection:\n%s", errmsg); + SV_NET_OutOfBandPrint(&svs.netprofile, challenge->adr, "droperror\nServer rejected connection:\n%s", errmsg); } } @@ -437,18 +437,18 @@ void SV_GamespyAuthorize(netadr_t from, const char* response) Com_DPrintf("authorize server timed out\n"); challenge->cdkeyState = CDKS_AUTHENTICATED; challenge->pingTime = svs.time; - NET_OutOfBandPrint(NS_SERVER, from, "challengeResponse %i", challenge->challenge); + SV_NET_OutOfBandPrint(&svs.netprofile, from, "challengeResponse %i", challenge->challenge); } break; case CDKS_AUTHENTICATED: - NET_OutOfBandPrint(NS_SERVER, from, "challengeResponse %i", challenge->challenge); + SV_NET_OutOfBandPrint(&svs.netprofile, from, "challengeResponse %i", challenge->challenge); break; case CDKS_FAILED: // authentication server told the cdkey was invalid Com_sprintf(buf, sizeof(buf), "%d.%d.%d.%d", challenge->adr.ip[0], challenge->adr.ip[1], challenge->adr.ip[2], challenge->adr.ip[3]); Com_Printf("%s failed cdkey authorization\n", buf); // reject the client - NET_OutOfBandPrint(NS_SERVER, from, "droperror\nServer rejected connection:\nInvalid CD Key"); + SV_NET_OutOfBandPrint(&svs.netprofile, from, "droperror\nServer rejected connection:\nInvalid CD Key"); break; default: break; diff --git a/code/qcommon/net_chan.c b/code/qcommon/net_chan.c index 63a9ccf8..3824bc6e 100644 --- a/code/qcommon/net_chan.c +++ b/code/qcommon/net_chan.c @@ -99,6 +99,126 @@ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int #endif } +/* +============== +NetProfileAddPacket +============== +*/ +void NetProfileAddPacket(netprofpacketlist_t* list, size_t size, int type) +{ + list->index = (list->index + 1) % 64; + list->packets[list->index].updateTime = Com_Milliseconds(); + list->packets[list->index].size = size; + list->packets[list->index].flags = type; +} + +/* +============== +NetProfileSetPacketFlags +============== +*/ +void NetProfileSetPacketFlags(netprofpacketlist_t* list, int flags) +{ + list->packets[list->index].flags |= flags; +} + +/* +============== +NetProfileCalcStats +============== +*/ +void NetProfileCalcStats(netprofpacketlist_t* list, int time) +{ + netprofpacket_t* packet; + int i; + float frequency; + + if (list->updateTime < time + list->lastCalcTime) { + return; + } + + list->lastCalcTime = list->updateTime; + list->lowestUpdateTime = list->updateTime; + list->highestUpdateTime = 0; + list->totalProcessed = 0; + list->totalSize = 0; + list->numFragmented = 0; + list->numDropped = 0; + list->numConnectionLess = 0; + list->totalLengthConnectionLess = 0; + + for (i = 0; i < ARRAY_LEN(list->packets); i++) { + packet = &list->packets[i]; + if (!packet->size) { + continue; + } + + if (list->updateTime > packet->updateTime + 1000) { + packet->size = 0; + continue; + } + + if (list->lowestUpdateTime > packet->updateTime) { + list->lowestUpdateTime = packet->updateTime; + } + if (list->highestUpdateTime < packet->updateTime) { + list->highestUpdateTime = packet->updateTime; + } + + list->totalProcessed++; + list->totalSize += packet->size; + + if (packet->flags & NETPROF_PACKET_FRAGMENTED) { + list->numFragmented++; + } + if (packet->flags & NETPROF_PACKET_DROPPED) { + list->numDropped++; + } + if (packet->flags & NETPROF_PACKET_MESSAGE) { + list->numConnectionLess++; + list->totalLengthConnectionLess += packet->size; + } + } + + if (!list->totalProcessed) { + list->packetsPerSec = 0; + list->highestUpdateTime = list->lowestUpdateTime; + list->bytesPerSec = 0; + list->percentFragmented = 0; + list->percentDropped = 0; + list->percentConnectionLess = 0; + + return; + } + + if (list->lowestUpdateTime == list->highestUpdateTime) { + list->latency = 0.0; + frequency = 1.0; + } else { + list->latency = (list->highestUpdateTime - list->lowestUpdateTime) / 1000.0; + frequency = 1.0 / list->latency; + } + + list->packetsPerSec = list->totalProcessed * frequency; + list->bytesPerSec = list->totalSize * frequency; + + if (list->numFragmented) { + list->percentFragmented = (float)list->numFragmented / list->totalProcessed * 100.0; + } else { + list->percentFragmented = 0; + } + + if (list->numDropped) { + list->percentDropped = (float)list->numDropped / list->totalProcessed * 100.0; + } else { + list->percentDropped = 0; + } + + if (list->totalLengthConnectionLess) { + list->percentConnectionLess = (float)list->totalLengthConnectionLess / list->totalSize * 100.0; + } +} + // TTimo: unused, commenting out to make gcc happy #if 0 /* @@ -192,7 +312,7 @@ Netchan_TransmitNextFragment Send one fragment of the current message ================= */ -void Netchan_TransmitNextFragment( netchan_t *chan ) { +void Netchan_TransmitNextFragment( netchan_t *chan, netprofpacketlist_t *packetlist ) { msg_t send; byte send_buf[MAX_PACKETLEN]; size_t fragmentLength; @@ -228,6 +348,10 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) { , chan->unsentFragmentStart, fragmentLength); } + if (packetlist) { + NetProfileAddPacket(packetlist, send.cursize, NETPROF_PACKET_FRAGMENTED); + } + chan->unsentFragmentStart += fragmentLength; // this exit condition is a little tricky, because a packet @@ -249,7 +373,7 @@ Sends a message to a connection, fragmenting if necessary A 0 length will still generate a packet. ================ */ -void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data ) { +void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data, netprofpacketlist_t *packetlist ) { msg_t send; byte send_buf[MAX_PACKETLEN]; @@ -265,7 +389,7 @@ void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data ) { Com_Memcpy( chan->unsentBuffer, data, length ); // only send the first fragment now - Netchan_TransmitNextFragment( chan ); + Netchan_TransmitNextFragment( chan, packetlist ); return; } @@ -293,6 +417,10 @@ void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data ) { , chan->outgoingSequence - 1 , chan->incomingSequence ); } + + if (packetlist) { + NetProfileAddPacket(packetlist, send.cursize, 0); + } } /* @@ -307,7 +435,7 @@ final fragment of a multi-part message, the entire thing will be copied out. ================= */ -qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { +qboolean Netchan_Process( netchan_t *chan, msg_t *msg, netprofpacketlist_t *packetlist ) { int sequence; int qport; int fragmentStart, fragmentLength; @@ -357,6 +485,10 @@ qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { } } + if (packetlist) { + NetProfileAddPacket(packetlist, msg->cursize, fragmented ? NETPROF_PACKET_FRAGMENTED : 0); + } + // // discard out of order or duplicated packets // diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 60dc4a32..3d349e61 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -299,14 +299,61 @@ typedef struct { #endif } netchan_t; +/* +Packet profiling +*/ + +#define NETPROF_PACKET_FRAGMENTED 1 +#define NETPROF_PACKET_DROPPED 2 +#define NETPROF_PACKET_MESSAGE 4 + +#define NETPROX_MAX_PACKETS 64 + +typedef struct { + int updateTime; + int size; + int flags; +} netprofpacket_t; + +typedef struct { + int updateTime; + int index; + netprofpacket_t packets[NETPROX_MAX_PACKETS]; + int lastCalcTime; + int lowestUpdateTime; + int highestUpdateTime; + int totalProcessed; + int totalSize; + int numFragmented; + int numDropped; + int numConnectionLess; + int totalLengthConnectionLess; + int packetsPerSec; + int bytesPerSec; + int percentFragmented; + int percentDropped; + int percentConnectionLess; + float latency; +} netprofpacketlist_t; + +typedef struct { + qboolean initialized; + int rate; + netprofpacketlist_t outPackets; + netprofpacketlist_t inPackets; +} netprofclient_t; + void Netchan_Init( int qport ); void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge, qboolean compat); -void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data ); -void Netchan_TransmitNextFragment( netchan_t *chan ); +void Netchan_Transmit( netchan_t *chan, size_t length, const byte *data, netprofpacketlist_t *packetlist ); +void Netchan_TransmitNextFragment( netchan_t *chan, netprofpacketlist_t *packetlist ); -qboolean Netchan_Process( netchan_t *chan, msg_t *msg ); +qboolean Netchan_Process( netchan_t *chan, msg_t *msg, netprofpacketlist_t *packetlist ); +void NetProfileAddPacket(netprofpacketlist_t* list, size_t size, int flags); +void NetProfileSetPacketFlags(netprofpacketlist_t* list, int flags); +void NetProfileCalcStats(netprofpacketlist_t* list, int time); /* ============================================================== diff --git a/code/server/server.h b/code/server/server.h index 554ab9c8..d14b2ca2 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -192,7 +192,9 @@ typedef struct client_s { int snapshotMsec; // requests a snapshot every snapshotMsec unless rate choked int pureAuthentic; qboolean gotCP; // TTimo - additional flag to distinguish between a bad pure checksum, and no cp command at all - netchan_t netchan; + netchan_t netchan; + // Added in 2.0 + netprofclient_t netprofile; // TTimo // queuing outgoing fragmented messages to send them properly, without udp packet bursts // in case large fragmented messages are stacking up @@ -319,6 +321,8 @@ typedef struct { #ifndef DEDICATED soundsystemsavegame_t soundSystem; #endif + // Added in 2.0 + netprofclient_t netprofile; } serverStatic_t; #define SERVER_MAXBANS 1024 @@ -621,6 +625,10 @@ void SV_Netchan_Transmit( client_t *client, msg_t *msg); int SV_Netchan_TransmitNextFragment( client_t *client ); qboolean SV_Netchan_Process( client_t *client, msg_t *msg ); void SV_Netchan_FreeQueue(client_t *client); +void SV_NET_OutOfBandPrint(netprofclient_t* netprof, netadr_t adr, const char* format, ...); +void SV_NET_UpdateClientNetProfileInfo(netprofclient_t* netprofile, int rate); +void SV_NET_UpdateAllNetProfileInfo(); +void SV_NET_CalcTotalNetProfile(netprofclient_t* netprofile, qboolean server); // // sv_gamespy.c diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index 984b1b03..c0a76e46 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -502,7 +502,7 @@ static void SV_Ban_f( void ) { // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { - NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, + SV_NET_OutOfBandPrint( &svs.netprofile, svs.authorizeAddress, "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); @@ -556,7 +556,7 @@ static void SV_BanNum_f( void ) { // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { - NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, + SV_NET_OutOfBandPrint( &svs.netprofile, svs.authorizeAddress, "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); @@ -1573,6 +1573,120 @@ void SV_LoadLastGame_f( void ) Cbuf_AddText( va( "loadgame %s\n", Cvar_Get( "g_lastsave", "", 0 )->string ) ); } +/* +================= +SV_NetProfileDump_PrintProf +================= +*/ +void SV_NetProfileDump_PrintProf(fileHandle_t file, netprofclient_t* netprofile) +{ + size_t totalProcessed; + char buffer[2048]; + + totalProcessed = netprofile->outPackets.totalProcessed + netprofile->inPackets.totalProcessed; + + Com_sprintf( + buffer, + sizeof(buffer), + "%4i %4i %4i | %3i %3i %3i | %3i %3i %3i | %3i %3i %3i | %7i %7i %7i\n", + netprofile->inPackets.packetsPerSec, + netprofile->outPackets.packetsPerSec, + netprofile->outPackets.packetsPerSec + netprofile->inPackets.packetsPerSec, + netprofile->inPackets.percentFragmented, + netprofile->outPackets.percentFragmented, + (unsigned int)((float)(netprofile->outPackets.numFragmented + netprofile->inPackets.numFragmented) / totalProcessed), + netprofile->inPackets.percentDropped, + netprofile->outPackets.percentDropped, + (unsigned int)((float)(netprofile->outPackets.numDropped + netprofile->inPackets.numDropped) / totalProcessed), + netprofile->inPackets.percentDropped, + netprofile->outPackets.percentDropped, + (unsigned int)((float)(netprofile->outPackets.totalLengthConnectionLess + netprofile->inPackets.totalLengthConnectionLess) / (float)(netprofile->outPackets.totalSize + netprofile->inPackets.totalSize)), + netprofile->inPackets.bytesPerSec, + netprofile->outPackets.bytesPerSec, + netprofile->outPackets.bytesPerSec + netprofile->inPackets.bytesPerSec + ); + + FS_Write(buffer, strlen(buffer), file); +} + +/* +================= +SV_NetProfileDump_f +================= +*/ +void SV_NetProfileDump_f(void) +{ + static fileHandle_t hFile = 0; + client_t *client; + int i; + char buffer[2048]; + netprofclient_t netproftotal; + + if (!hFile) { + hFile = FS_FOpenTextFileWrite("netprofile.log"); + FS_ForceFlush(hFile); + + Com_sprintf(buffer, sizeof(buffer), "NetProfile.log\n\n"); + FS_Write(buffer, strlen(buffer), hFile); + + Com_sprintf(buffer, sizeof(buffer), "Log Format:\n"); + FS_Write(buffer, strlen(buffer), hFile); + + Com_sprintf( + buffer, + sizeof(buffer), + "Source: Packets per Second In/Out/Total | %%Fragments In/Out/Total | %%Dropped In/Out/Total | %%OOB Data In/Out/Total" + " | Data per Second In/Out/Total\n" + "\n"); + FS_Write(buffer, strlen(buffer), hFile); + } + + if (!sv_netprofile->integer) { + return; + } + +#ifndef DEDICATED + if (!com_cl_running->integer || com_sv_running->integer) { +#else + if (com_sv_running->integer) { +#endif + Com_sprintf(buffer, sizeof(buffer), "------------------\nServer Net Profile\n"); + FS_Write(buffer, strlen(buffer), hFile); + SV_NET_CalcTotalNetProfile(&netproftotal, 1); + + Com_sprintf(buffer, sizeof(buffer), "Total: "); + FS_Write(buffer, strlen(buffer), hFile); + SV_NetProfileDump_PrintProf(hFile, &netproftotal); + + Com_sprintf(buffer, sizeof(buffer), "Clientless: "); + FS_Write(buffer, strlen(buffer), hFile); + SV_NetProfileDump_PrintProf(hFile, &svs.netprofile); + + for (i = 0; i < svs.iNumClients; i++) { + client = &svs.clients[i]; + if (client->state != CS_ACTIVE || !client->gentity) { + continue; + } + + if (client->netchan.remoteAddress.type == NA_LOOPBACK) { + Com_sprintf(buffer, sizeof(buffer), "#%2i-Loopback: "); + } else { + Com_sprintf(buffer, sizeof(buffer), "Client #%2i: "); + } + + FS_Write(buffer, strlen(buffer), hFile); + SV_NetProfileDump_PrintProf(hFile, &client->netprofile); + } + } +#ifndef DEDICATED + else if (com_cl_running->integer && cl_netprofile->integer) { + Com_sprintf(buffer, sizeof(buffer), "------------------\nClient Net Profile\n"); + FS_Write(buffer, strlen(buffer), hFile); + SV_NetProfileDump_PrintProf(hFile, &cls.netprofile); + } +#endif +} + /* ================= SV_ReloadMap_f @@ -1730,6 +1844,8 @@ void SV_AddOperatorCommands(void) { Cmd_AddCommand("difficultyMedium", SV_MediumMode_f); Cmd_AddCommand("difficultyHard", SV_HardMode_f); + // Added in 2.0 + Cmd_AddCommand("netprofiledump", SV_NetProfileDump_f); // Added in 2.30 Cmd_AddCommand("reloadmap", SV_ReloadMap_f); diff --git a/code/server/sv_client.c b/code/server/sv_client.c index b849f90d..31c42697 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -89,7 +89,7 @@ void SV_GetChallenge(netadr_t from) // it's also way more cool this way :) if ( Sys_IsLANAddress( from ) ) { challenge->pingTime = svs.time; - NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "challengeResponse %i", challenge->challenge ); return; } @@ -97,7 +97,7 @@ void SV_GetChallenge(netadr_t from) // mohaa below 2.0 doesn't handle gamespy key // so send the challenge response directly challenge->pingTime = svs.time; - NET_OutOfBandPrint(NS_SERVER, from, "challengeResponse %i", challenge->challenge); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "challengeResponse %i", challenge->challenge); return; } @@ -106,12 +106,12 @@ void SV_GetChallenge(netadr_t from) // however, it could be an useful feature for a far future // where players could be authenticated challenge->pingTime = svs.time; - NET_OutOfBandPrint(NS_SERVER, from, "challengeResponse %i", challenge->challenge); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "challengeResponse %i", challenge->challenge); return; } // check client's cd key - NET_OutOfBandPrint(NS_SERVER, from, "getKey %s", challenge->gsChallenge); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "getKey %s", challenge->gsChallenge); challenge->pingTime = svs.time; } @@ -158,21 +158,21 @@ void SV_AuthorizeIpPacket( netadr_t from ) { if ( !Q_stricmp( s, "demo" ) ) { // they are a demo client trying to connect to a real server - NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nServer is not a demo server\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "print\nServer is not a demo server\n" ); // clear the challenge record so it won't timeout and let them through Com_Memset( challengeptr, 0, sizeof( *challengeptr ) ); return; } if ( !Q_stricmp( s, "accept" ) ) { - NET_OutOfBandPrint(NS_SERVER, challengeptr->adr, + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "challengeResponse %d %d %d", challengeptr->challenge, challengeptr->clientChallenge, com_protocol->integer); return; } if ( !Q_stricmp( s, "unknown" ) ) { if (!r) { - NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nAwaiting CD key authorization\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "print\nAwaiting CD key authorization\n" ); } else { - NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r); + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "print\n%s\n", r); } // clear the challenge record so it won't timeout and let them through Com_Memset( challengeptr, 0, sizeof( *challengeptr ) ); @@ -181,9 +181,9 @@ void SV_AuthorizeIpPacket( netadr_t from ) { // authorization failed if (!r) { - NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nSomeone is using this CD Key\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "print\nSomeone is using this CD Key\n" ); } else { - NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r ); + SV_NET_OutOfBandPrint( &svs.netprofile, challengeptr->adr, "print\n%s\n", r ); } // clear the challenge record so it won't timeout and let them through @@ -317,7 +317,7 @@ void SV_DirectConnect( netadr_t from ) { // Check whether this client is banned. if(SV_IsBanned(&from, qfalse)) { - NET_OutOfBandPrint(NS_SERVER, from, "droperror\nYou are banned from this server.\n"); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nYou are banned from this server.\n"); return; } @@ -330,7 +330,7 @@ void SV_DirectConnect( netadr_t from ) { // Added in 2.30 // Only allow breakthrough clients when targeting mohaab if (strcmp(clientType, "Breakthrough")) { - NET_OutOfBandPrint(NS_SERVER, from, "droperror\nRequires Medal of Honor Allied Assault Breakthrough\n"); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nRequires Medal of Honor Allied Assault Breakthrough\n"); Com_DPrintf(" rejected connect - only Breakthrough clients allowed.\n"); return; } @@ -346,7 +346,7 @@ void SV_DirectConnect( netadr_t from ) { { if(version != com_protocol->integer) { - NET_OutOfBandPrint(NS_SERVER, from, "droperror\nServer uses protocol version %i " + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nServer uses protocol version %i " "(yours is %i).\n", com_protocol->integer, version); Com_DPrintf(" rejected connect from version %i\n", version); return; @@ -379,7 +379,7 @@ void SV_DirectConnect( netadr_t from ) { else ip = (char *)NET_AdrToString( from ); if( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) { - NET_OutOfBandPrint( NS_SERVER, from, + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nUserinfo string length exceeded. " "Try removing setu cvars from your config.\n" ); return; @@ -403,7 +403,7 @@ void SV_DirectConnect( netadr_t from ) { if (i == MAX_CHALLENGES) { - NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for your address.\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "print\nNo or bad challenge for your address.\n" ); return; } @@ -420,13 +420,13 @@ void SV_DirectConnect( netadr_t from ) { // never reject a LAN client based on ping if ( !Sys_IsLANAddress( from ) ) { if ( sv_minPing->value && ping < sv_minPing->value ) { - NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "print\nServer is for high pings only\n" ); Com_DPrintf ("Client %i rejected on a too low ping\n", i); challengeptr->wasrefused = qtrue; return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { - NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "print\nServer is for low pings only\n" ); Com_DPrintf ("Client %i rejected on a too high ping\n", i); challengeptr->wasrefused = qtrue; return; @@ -509,7 +509,7 @@ void SV_DirectConnect( netadr_t from ) { } } else { - NET_OutOfBandPrint( NS_SERVER, from, "droperror\nServer is full.\n" ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\nServer is full.\n" ); Com_DPrintf ("Rejected a connection.\n"); return; } @@ -548,7 +548,7 @@ gotnewcl: // get the game a chance to reject this connection or modify the userinfo denied = ge->ClientConnect( clientNum, qtrue, qfalse ); if ( denied ) { - NET_OutOfBandPrint( NS_SERVER, from, "droperror\n%s\n", denied ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "droperror\n%s\n", denied ); Com_DPrintf ( "Game rejected a connection: %s.\n", denied ); return; } @@ -560,8 +560,12 @@ gotnewcl: SV_UserinfoChanged( newcl ); + if (sv_netprofile->integer) { + SV_NET_UpdateClientNetProfileInfo(&newcl->netprofile, newcl->rate); + } + // send the connect packet to the client - NET_OutOfBandPrint(NS_SERVER, from, "connectResponse %d", challenge); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "connectResponse %d", challenge); Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name ); diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 1368e4b6..bd3893a6 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -530,7 +530,7 @@ void SVC_Status( netadr_t from ) { } } - NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "statusResponse\n%s\n%s", infostring, status ); } /* @@ -618,7 +618,7 @@ void SVC_Info( netadr_t from ) { Info_SetValueForKey(infostring, "gamever", com_target_version->string); Info_SetValueForKey(infostring, "serverType", va("%i", com_target_game->integer)); - NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "infoResponse\n%s", infostring ); } /* @@ -628,7 +628,7 @@ SVC_FlushRedirect ================ */ static void SV_FlushRedirect( char *outputbuf ) { - NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf ); + SV_NET_OutOfBandPrint( &svs.netprofile, svs.redirectAddress, "print\n%s", outputbuf ); } /* @@ -720,6 +720,10 @@ void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) { char *s; char *c; + if (sv_netprofile->integer) { + NetProfileAddPacket(&svs.netprofile.inPackets, msg->cursize, NETPROF_PACKET_MESSAGE); + } + MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 marker @@ -818,7 +822,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg ) { // if we received a sequenced packet from an address we don't recognize, // send an out of band disconnect packet to it - NET_OutOfBandPrint( NS_SERVER, from, "disconnect" ); + SV_NET_OutOfBandPrint( &svs.netprofile, from, "disconnect" ); } @@ -1023,6 +1027,8 @@ void SV_Frame( int msec ) { return; } + SV_NET_UpdateAllNetProfileInfo(); + // allow pause if only the local client is connected if ( SV_CheckPaused() ) { return; diff --git a/code/server/sv_net_chan.c b/code/server/sv_net_chan.c index 66832630..6b93be9c 100644 --- a/code/server/sv_net_chan.c +++ b/code/server/sv_net_chan.c @@ -169,7 +169,7 @@ void SV_Netchan_TransmitNextInQueue(client_t *client) SV_Netchan_Encode(client, &netbuf->msg, netbuf->clientCommandString); #endif - Netchan_Transmit(&client->netchan, netbuf->msg.cursize, netbuf->msg.data); + Netchan_Transmit(&client->netchan, netbuf->msg.cursize, netbuf->msg.data, sv_netprofile->integer ? &client->netprofile.outPackets : NULL); // pop from queue client->netchan_start_queue = netbuf->next; @@ -197,7 +197,7 @@ int SV_Netchan_TransmitNextFragment(client_t *client) { if(client->netchan.unsentFragments) { - Netchan_TransmitNextFragment(&client->netchan); + Netchan_TransmitNextFragment(&client->netchan, sv_netprofile->integer ? &client->netprofile.outPackets : NULL); return SV_RateMsec(client); } else if(client->netchan_start_queue) @@ -250,7 +250,7 @@ void SV_Netchan_Transmit( client_t *client, msg_t *msg) if(client->compat) SV_Netchan_Encode(client, msg, client->lastClientCommandString); #endif - Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); + Netchan_Transmit(&client->netchan, msg->cursize, msg->data, sv_netprofile->integer ? &client->netprofile.outPackets : NULL); } } @@ -261,7 +261,7 @@ Netchan_SV_Process */ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) { int ret; - ret = Netchan_Process( &client->netchan, msg ); + ret = Netchan_Process( &client->netchan, msg, sv_netprofile->integer ? &client->netprofile.inPackets : NULL ); if (!ret) return qfalse; @@ -273,3 +273,170 @@ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) { return qtrue; } +/* +================= +SV_NET_OutOfBandPrint +================= +*/ +void SV_NET_OutOfBandPrint(netprofclient_t* netprof, netadr_t adr, const char* format, ...) { + va_list argptr; + char string[MAX_MSGLEN]; + + va_start(argptr, format); + Q_vsnprintf(string, sizeof(string), format, argptr); + va_end(argptr); + + NET_OutOfBandPrint(NS_SERVER, adr, "%s", string); + + if (sv_netprofile->integer && netprof) { + NetProfileAddPacket(&netprof->outPackets, strlen(string), NETPROF_PACKET_MESSAGE); + } +} + +/* +================= +SV_NET_UpdateClientNetProfileInfo +================= +*/ +void SV_NET_UpdateClientNetProfileInfo(netprofclient_t* netprofile, int rate) { + int time; + + if (!netprofile->initialized) { + memset(netprofile, 0, sizeof(*netprofile)); + netprofile->initialized = qtrue; + } + + if (netprofile->rate != rate) { + netprofile->rate = rate; + } + + // set the update time + time = Com_Milliseconds(); + + netprofile->outPackets.updateTime = time; + netprofile->inPackets.updateTime = time; +} + +/* +================= +SV_NET_UpdateAllNetProfileInfo +================= +*/ +void SV_NET_UpdateAllNetProfileInfo() { + client_t *client; + int i; + + if (!sv_netprofile->integer) { + // + // Clear all netprofile fields + // + + svs.netprofile.initialized = qfalse; + + for (i = 0; i < svs.iNumClients; i++) { + svs.clients[i].netprofile.initialized = qfalse; + } + + return; + } + + SV_NET_UpdateClientNetProfileInfo(&svs.netprofile, sv_maxRate->integer); + + for (i = 0; i < svs.iNumClients; i++) { + client = &svs.clients[i]; + if (client->state != CS_ACTIVE || !client->gentity) { + client->netprofile.initialized = qfalse; + continue; + } + + SV_NET_UpdateClientNetProfileInfo(&client->netprofile, client->rate); + } +} + +/* +================= +SV_NET_CalcTotalNetProfile +================= +*/ +void SV_NET_CalcTotalNetProfile(netprofclient_t* netprofile, qboolean server) { + client_t *client; + int i; + int numValidClients; + + numValidClients = 0; + memset(netprofile, 0, sizeof(*netprofile)); + + if (server) { + NetProfileCalcStats(&svs.netprofile.outPackets, 0); + NetProfileCalcStats(&svs.netprofile.inPackets, 0); + } else { + NetProfileCalcStats(&svs.netprofile.outPackets, 500); + NetProfileCalcStats(&svs.netprofile.inPackets, 500); + } + + for (i = 0; i < svs.iNumClients; i++) { + client = &svs.clients[i]; + if (client->state != CS_ACTIVE || !client->gentity) { + client->netprofile.initialized = qfalse; + continue; + } + + if (server) { + NetProfileCalcStats(&client->netprofile.outPackets, 0); + NetProfileCalcStats(&client->netprofile.inPackets, 0); + } else { + NetProfileCalcStats(&client->netprofile.outPackets, 500); + NetProfileCalcStats(&client->netprofile.inPackets, 500); + } + + numValidClients++; + + netprofile->rate += client->netprofile.rate; + netprofile->outPackets.totalSize += client->netprofile.outPackets.totalSize; + netprofile->outPackets.bytesPerSec += client->netprofile.outPackets.bytesPerSec; + netprofile->outPackets.numDropped += client->netprofile.outPackets.numDropped; + netprofile->outPackets.percentDropped += client->netprofile.outPackets.percentDropped; + netprofile->outPackets.percentFragmented += client->netprofile.outPackets.percentFragmented; + netprofile->outPackets.numFragmented += client->netprofile.outPackets.numFragmented; + netprofile->outPackets.totalLengthConnectionLess += client->netprofile.outPackets.totalLengthConnectionLess; + netprofile->outPackets.percentConnectionLess += client->netprofile.outPackets.percentConnectionLess; + netprofile->outPackets.totalProcessed += client->netprofile.outPackets.totalProcessed; + netprofile->outPackets.packetsPerSec += client->netprofile.outPackets.packetsPerSec; + netprofile->inPackets.totalSize += client->netprofile.inPackets.totalSize; + netprofile->inPackets.bytesPerSec += client->netprofile.inPackets.bytesPerSec; + netprofile->inPackets.numDropped += client->netprofile.inPackets.numDropped; + netprofile->inPackets.percentDropped += client->netprofile.inPackets.percentDropped; + netprofile->inPackets.percentFragmented += client->netprofile.inPackets.percentFragmented; + netprofile->inPackets.numFragmented += client->netprofile.inPackets.numFragmented; + netprofile->inPackets.totalLengthConnectionLess += client->netprofile.inPackets.totalLengthConnectionLess; + netprofile->inPackets.percentConnectionLess += client->netprofile.inPackets.percentConnectionLess; + netprofile->inPackets.totalProcessed += client->netprofile.inPackets.totalProcessed; + netprofile->inPackets.packetsPerSec += client->netprofile.inPackets.packetsPerSec; + } + + if (numValidClients && netprofile->rate) { + netprofile->rate /= numValidClients; + } + + netprofile->outPackets.totalSize += svs.netprofile.outPackets.totalSize; + netprofile->outPackets.bytesPerSec += svs.netprofile.outPackets.bytesPerSec; + netprofile->outPackets.numDropped += svs.netprofile.outPackets.numDropped; + netprofile->outPackets.percentDropped += svs.netprofile.outPackets.percentDropped; + netprofile->outPackets.percentFragmented += svs.netprofile.outPackets.percentFragmented; + netprofile->outPackets.numFragmented += svs.netprofile.outPackets.numFragmented; + netprofile->outPackets.totalLengthConnectionLess += svs.netprofile.outPackets.totalLengthConnectionLess; + netprofile->outPackets.percentConnectionLess += svs.netprofile.outPackets.percentConnectionLess; + netprofile->outPackets.totalProcessed += svs.netprofile.outPackets.totalProcessed; + netprofile->outPackets.packetsPerSec += svs.netprofile.outPackets.packetsPerSec; + netprofile->inPackets.totalSize += svs.netprofile.inPackets.totalSize; + netprofile->inPackets.bytesPerSec += svs.netprofile.inPackets.bytesPerSec; + netprofile->inPackets.numDropped += svs.netprofile.inPackets.numDropped; + netprofile->inPackets.percentDropped += svs.netprofile.inPackets.percentDropped; + netprofile->inPackets.percentFragmented += svs.netprofile.inPackets.percentFragmented; + netprofile->inPackets.numFragmented += svs.netprofile.inPackets.numFragmented; + netprofile->inPackets.totalLengthConnectionLess += svs.netprofile.inPackets.totalLengthConnectionLess; + netprofile->inPackets.percentConnectionLess += svs.netprofile.inPackets.percentConnectionLess; + netprofile->inPackets.totalProcessed += svs.netprofile.inPackets.totalProcessed; + netprofile->inPackets.packetsPerSec += svs.netprofile.inPackets.packetsPerSec; +} +