]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
client: improve server connection status messages
authorbones_was_here <bones_was_here@xonotic.au>
Fri, 1 Dec 2023 06:18:45 +0000 (16:18 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sat, 9 Dec 2023 10:13:34 +0000 (20:13 +1000)
Always logs various errors and rejections that previously were only
visible with developer cvar(s) set.  Among other things this fixes
silent failure when trying to join a full server.
Includes IP address in more messages.

Uses CON_ERROR and CON_WARN colours for various errors and warns.

Moves status message generation to the code that sets the relevant
states.  This allows more information to be included, makes the code
easier to follow and reduces polling.

Fixes some cases where a message was always clobbered before it could be
displayed.

Merges the built-in menu and loading screen connection status generation
and buffering.

Simplifies challenge request retry counting.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
cl_main.c
cl_parse.c
cl_screen.c
cl_screen.h
crypto.c
crypto.h
menu.c
menu.h
netconn.c

index 218f7b2451ca7cfca81b05c471d265d0ce52975d..6b557283ad5ed540ca9714818cbcb8027e5ca364 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -447,10 +447,14 @@ void CL_DisconnectEx(qbool kicked, const char *fmt, ... )
 
                NetConn_Close(cls.netcon);
                cls.netcon = NULL;
-               if(fmt)
-                       Con_Printf("Disconnect: %s\n", reason);
+
+               // It's possible for a server to disconnect a player with an empty reason
+               // which is checked here rather than above so we don't print "Disconnect by user".
+               if(fmt && reason[0] != '\0')
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Disconnect: %s", reason);
                else
-                       Con_Printf("Disconnected\n");
+                       strlcpy(cl_connect_status, "Disconnected", sizeof(cl_connect_status));
+               Con_Printf("%s\n", cl_connect_status);
        }
        cls.state = ca_disconnected;
        cl.islocalgame = false;
@@ -492,7 +496,7 @@ static void CL_Reconnect_f(cmd_state_t *cmd)
                if (temp[0])
                        CL_EstablishConnection(temp, -1);
                else
-                       Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
+                       Con_Printf(CON_WARN "Reconnect to what server?  (you have not connected to a server yet)\n");
                return;
        }
        // if connected, do something based on protocol
@@ -572,18 +576,13 @@ void CL_EstablishConnection(const char *address, int firstarg)
        if (Sys_CheckParm("-benchmark"))
                return;
 
-       // clear menu's connect error message
-#ifdef CONFIG_MENU
-       M_Update_Return_Reason("");
-#endif
-
        // make sure the client ports are open before attempting to connect
        NetConn_UpdateSockets();
 
        if (LHNETADDRESS_FromString(&cls.connect_address, address, 26000) && (cls.connect_mysocket = NetConn_ChooseClientSocketForAddress(&cls.connect_address)))
        {
                cls.connect_trying = true;
-               cls.connect_remainingtries = 3;
+               cls.connect_remainingtries = 10;
                cls.connect_nextsendtime = 0;
 
                // only NOW, set connect_userinfo
@@ -601,17 +600,13 @@ void CL_EstablishConnection(const char *address, int firstarg)
                        *cls.connect_userinfo = 0;
                }
 
-#ifdef CONFIG_MENU
-               M_Update_Return_Reason("Trying to connect...");
-#endif
+               strlcpy(cl_connect_status, "Connect: pending...", sizeof(cl_connect_status));
                SCR_BeginLoadingPlaque(false);
        }
        else
        {
-               Con_Print("Unable to find a suitable network socket to connect to server.\n");
-#ifdef CONFIG_MENU
-               M_Update_Return_Reason("No network");
-#endif
+               Con_Printf(CON_ERROR "Connect: failed, unable to find a network socket suitable to reach %s\n", address);
+               strlcpy(cl_connect_status, "Connect: failed, no network", sizeof(cl_connect_status));
        }
 }
 
index 374b6ed6a564f61a2b68cbdb992123265659e947..ae96302b42cceb6cac3335f0ac661e8af06b5f95 100644 (file)
@@ -199,6 +199,13 @@ static void QW_CL_NextUpload_f(cmd_state_t *cmd);
 //static qbool QW_CL_IsUploading(void);
 static void QW_CL_StopUpload_f(cmd_state_t *cmd);
 
+static inline void CL_SetSignonStage_WithMsg(int signon_stage)
+{
+       cls.signon = signon_stage;
+       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: signon stage %i of %i", cls.signon, SIGNONS);
+       Con_DPrint(cl_connect_status);
+}
+
 /*
 ==================
 CL_ParseStartSoundPacket
@@ -614,7 +621,7 @@ static void QW_CL_RequestNextDownload(void)
                // if we're still in signon stages, request the next one
                if (cls.signon != SIGNONS)
                {
-                       cls.signon = SIGNONS-1;
+                       CL_SetSignonStage_WithMsg(SIGNONS - 1);
                        // we'll go to SIGNONS when the first entity update is received
                        MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
                        MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "begin %i", cl.qw_servercount));
@@ -842,7 +849,7 @@ static void QW_CL_ParseModelList(void)
                return;
        }
 
-       cls.signon = 2;
+       CL_SetSignonStage_WithMsg(2);
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_model;
        QW_CL_RequestNextDownload();
@@ -878,7 +885,7 @@ static void QW_CL_ParseSoundList(void)
                return;
        }
 
-       cls.signon = 2;
+       CL_SetSignonStage_WithMsg(2);
        cls.qw_downloadnumber = 0;
        cls.qw_downloadtype = dl_sound;
        QW_CL_RequestNextDownload();
@@ -898,7 +905,7 @@ static void QW_CL_Changing_f(cmd_state_t *cmd)
 
        S_StopAllSounds();
        cl.intermission = 0;
-       cls.signon = 1; // not active anymore, but not disconnected
+       CL_SetSignonStage_WithMsg(1); // not active anymore, but not disconnected
        Con_Printf("\nChanging map...\n");
 }
 
@@ -1602,8 +1609,10 @@ CL_SignonReply
 An svc_signonnum has been received, perform a client side setup
 =====================
 */
-static void CL_SignonReply (void)
+static void CL_SignonReply(int signon_stage)
 {
+       CL_SetSignonStage_WithMsg(signon_stage);
+
        Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
 
        switch (cls.signon)
@@ -1787,7 +1796,7 @@ static void CL_ParseServerInfo (void)
                cl.loadfinished = false;
 
                cls.state = ca_connected;
-               cls.signon = 1;
+               CL_SetSignonStage_WithMsg(1);
 
                // note: on QW protocol we can't set up the gameworld until after
                // downloads finish...
@@ -3773,20 +3782,14 @@ void CL_ParseServerMessage(void)
                                EntityFrameQW_CL_ReadFrame(false);
                                // first update is the final signon stage
                                if (cls.signon == SIGNONS - 1)
-                               {
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                break;
 
                        case qw_svc_deltapacketentities:
                                EntityFrameQW_CL_ReadFrame(true);
                                // first update is the final signon stage
                                if (cls.signon == SIGNONS - 1)
-                               {
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                break;
 
                        case qw_svc_maxspeed:
@@ -3846,11 +3849,8 @@ void CL_ParseServerMessage(void)
                                cmdlogname[cmdindex] = temp;
                                SHOWNET("fast update");
                                if (cls.signon == SIGNONS - 1)
-                               {
                                        // first update is the final signon stage
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                EntityFrameQuake_ReadEntity (cmd&127);
                                continue;
                        }
@@ -4141,8 +4141,7 @@ void CL_ParseServerMessage(void)
                                // reconnect somehow, so allow signon 1 even if at signon 1
                                if (i <= cls.signon && i != 1)
                                        Host_Error ("Received signon %i when at %i", i, cls.signon);
-                               cls.signon = i;
-                               CL_SignonReply ();
+                               CL_SignonReply(i);
                                break;
 
                        case svc_killedmonster:
@@ -4246,11 +4245,8 @@ void CL_ParseServerMessage(void)
                                break;
                        case svc_entities:
                                if (cls.signon == SIGNONS - 1)
-                               {
                                        // first update is the final signon stage
-                                       cls.signon = SIGNONS;
-                                       CL_SignonReply ();
-                               }
+                                       CL_SignonReply(SIGNONS);
                                if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
                                        EntityFrame_CL_ReadFrame();
                                else if (cls.protocol == PROTOCOL_DARKPLACES4)
index 6e9fbeb6c1bb10438bc2f2bf7472f6d2c2ad2d94..a313e1e3acd8c9ab7b2a25af94193c7254c65069 100644 (file)
@@ -1558,6 +1558,9 @@ rtexture_t *loadingscreentexture = NULL; // last framebuffer before loading scre
 static float loadingscreentexture_vertex3f[12];
 static float loadingscreentexture_texcoord2f[8];
 static int loadingscreenpic_number = 0;
+/// User-friendly connection status for the menu and/or loading screen,
+/// colours and \n not supported.
+char cl_connect_status[MAX_QPATH]; // should match size of loadingscreenstack_t msg[]
 
 static void SCR_DrawLoadingScreen(void);
 static void SCR_DrawScreen (void)
@@ -1729,24 +1732,19 @@ static void SCR_DrawScreen (void)
 
        if (scr_loading)
        {
-               loadingscreenstack_t connect_status;
-               qbool show_connect_status = !loadingscreenstack && (cls.connect_trying || cls.state == ca_connected);
-               if (show_connect_status)
+               // connect_status replaces any dummy_status
+               if ((!loadingscreenstack || loadingscreenstack->msg[0] == '\0') && cl_connect_status[0] != '\0')
                {
+                       loadingscreenstack_t connect_status, *og_ptr = loadingscreenstack;
+
                        connect_status.absolute_loading_amount_min = 0;
-                       if (cls.signon > 0)
-                               dpsnprintf(connect_status.msg, sizeof(connect_status.msg), "Connect: Signon stage %i of %i", cls.signon, SIGNONS);
-                       else if (cls.connect_remainingtries > 0)
-                               dpsnprintf(connect_status.msg, sizeof(connect_status.msg), "Connect: Trying...  %i", cls.connect_remainingtries);
-                       else
-                               dpsnprintf(connect_status.msg, sizeof(connect_status.msg), "Connect: Waiting %i seconds for reply", 10 + cls.connect_remainingtries);
+                       strlcpy(connect_status.msg, cl_connect_status, sizeof(cl_connect_status));
                        loadingscreenstack = &connect_status;
+                       SCR_DrawLoadingScreen();
+                       loadingscreenstack = og_ptr;
                }
-
-               SCR_DrawLoadingScreen();
-
-               if (show_connect_status)
-                       loadingscreenstack = NULL;
+               else
+                       SCR_DrawLoadingScreen();
        }
 
        SCR_DrawConsole();
index aef5a234fae02c7ac165044f220e825b76d37b54..57cfd282a135f2b2c027afda24556d6f455f51e9 100644 (file)
@@ -17,6 +17,8 @@ extern struct cvar_s scr_screenshot_png;
 extern struct cvar_s scr_screenshot_gammaboost;
 extern struct cvar_s scr_screenshot_name;
 
+extern char cl_connect_status[MAX_QPATH];
+
 void CL_Screen_NewMap(void);
 void CL_Screen_Init(void);
 void CL_Screen_Shutdown(void);
index 21b70954fc9045e163faf138591ece7bc0828051..d910e4919f3033391a26205c63500abfeb631378 100644 (file)
--- a/crypto.c
+++ b/crypto.c
@@ -1141,11 +1141,11 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch
        {
                if(length_received >= 5 && Crypto_LittleLong((const char *) buffer) == FOURCC_D0ER)
                {
-                       Con_Printf("Error response from keygen server: %.*s\n", (int)(length_received - 5), buffer + 5);
+                       Con_Printf(CON_ERROR "Error response from keygen server: %.*s\n", (int)(length_received - 5), buffer + 5);
                }
                else
                {
-                       Con_Printf("Invalid response from keygen server:\n");
+                       Con_Printf(CON_ERROR "Invalid response from keygen server:\n");
                        Com_HexDumpToConsole(buffer, (int)length_received);
                }
                keygen_i = -1;
@@ -2106,7 +2106,7 @@ static int Crypto_SoftClientError(char *data_out, size_t *len_out, const char *m
        return CRYPTO_DISCARD;
 }
 
-int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress)
+int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress, const char *peeraddressstring)
 {
        crypto_t *crypto = &cls.crypto;
        const char *string = data_in;
@@ -2214,7 +2214,12 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server.
                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
-                       return Crypto_SoftClientError(data_out, len_out, "challenge message from wrong server");
+               {
+                       char warn_msg[128];
+
+                       dpsnprintf(warn_msg, sizeof(warn_msg), "ignoring challenge message from wrong server %s", peeraddressstring);
+                       return Crypto_SoftClientError(data_out, len_out, warn_msg);
+               }
 
                // if we have a stored host key for the server, assume serverid to already be selected!
                // (the loop will refuse to overwrite this one then)
@@ -2422,7 +2427,12 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out,
 
                // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server.
                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
-                       return Crypto_SoftClientError(data_out, len_out, "d0pk\\ message from wrong server");
+               {
+                       char warn_msg[128];
+
+                       dpsnprintf(warn_msg, sizeof(warn_msg), "ignoring d0pk\\ message from wrong server %s", peeraddressstring);
+                       return Crypto_SoftClientError(data_out, len_out, warn_msg);
+               }
 
                cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue));
                id = (cnt ? atoi(cnt) : -1);
index 306134de96eeb2bb1c743e9cdd50dac05b72b0ea..bbb4ea1fa4526513eca80f838c18070df625b35b 100644 (file)
--- a/crypto.h
+++ b/crypto.h
@@ -65,8 +65,8 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t
 #define CRYPTO_MATCH 1          // process as usual (packet was used)
 #define CRYPTO_DISCARD 2        // discard this packet
 #define CRYPTO_REPLACE 3        // make the buffer the current packet
-int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, struct lhnetaddress_s *peeraddress);
-int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, struct lhnetaddress_s *peeraddress);
+int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress, const char *peeraddressstring);
+int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress);
 
 // if len_out is nonzero, the packet is to be sent to the client
 
diff --git a/menu.c b/menu.c
index f2745353621e9b5e5d241342e56af616104f728f..6749f6daa4d715cc5990ca4fba1744f6c9eb0028 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -34,7 +34,6 @@ static cvar_t menu_progs = {CF_CLIENT, "menu_progs", "menu.dat", "name of quakec
 static int NehGameType;
 
 enum m_state_e m_state;
-char m_return_reason[128];
 
 void M_Menu_Main_f(cmd_state_t *cmd);
        void M_Menu_SinglePlayer_f(cmd_state_t *cmd);
@@ -108,13 +107,6 @@ static void M_ModList_Key(cmd_state_t *cmd, int key, int ascii);
 
 static qbool   m_entersound;           ///< play after drawing a frame, so caching won't disrupt the sound
 
-void M_Update_Return_Reason(const char *s)
-{
-       strlcpy(m_return_reason, s, sizeof(m_return_reason));
-       if (s)
-               Con_DPrintf("%s\n", s);
-}
-
 #define StartingGame   (m_multiplayer_cursor == 1)
 #define JoiningGame            (m_multiplayer_cursor == 0)
 
@@ -3344,7 +3336,7 @@ void M_Menu_LanConfig_f(cmd_state_t *cmd)
        lanConfig_port = 26000;
        dpsnprintf(lanConfig_portname, sizeof(lanConfig_portname), "%u", (unsigned int) lanConfig_port);
 
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
 }
 
 
@@ -3397,8 +3389,8 @@ static void M_LanConfig_Draw (void)
        if (lanConfig_cursor == 3)
                M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [lanConfig_cursor], 10+((int)(host.realtime*4)&1));
 
-       if (*m_return_reason)
-               M_Print(basex, 168, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(basex, 168, cl_connect_status);
 }
 
 
@@ -4394,7 +4386,7 @@ void M_Menu_ServerList_f(cmd_state_t *cmd)
        m_state = m_slist;
        m_entersound = true;
        slist_cursor = 0;
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
        if (lanConfig_cursor == 2)
                Net_SlistQW_f(cmd);
        else
@@ -4418,8 +4410,8 @@ static void M_ServerList_Draw (void)
        ServerList_GetPlayerStatistics(&statnumplayers, &statmaxplayers);
        s = va(vabuf, sizeof(vabuf), "%u/%u masters %u/%u servers %u/%u players", masterreplycount, masterquerycount, serverreplycount, serverquerycount, statnumplayers, statmaxplayers);
        M_PrintRed((640 - strlen(s) * 8) / 2, 32, s);
-       if (*m_return_reason)
-               M_Print(16, menu_height - 8, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(16, menu_height - 8, cl_connect_status);
        y = 48;
        slist_visible = (menu_height - 16 - y) / 8 / 2;
        start = min(slist_cursor - min(slist_cursor, slist_visible >> 1), serverlist_viewcount - min(serverlist_viewcount, slist_visible));
@@ -4617,7 +4609,7 @@ void M_Menu_ModList_f(cmd_state_t *cmd)
        m_state = m_modlist;
        m_entersound = true;
        modlist_cursor = 0;
-       M_Update_Return_Reason("");
+       cl_connect_status[0] = '\0';
        ModList_RebuildList();
 }
 
@@ -4667,8 +4659,8 @@ static void M_ModList_Draw (void)
        for (y = 0; y < modlist_numenabled; y++)
                M_PrintRed(432, 48 + y * 8, modlist[modlist_enabled[y]].dir);
 
-       if (*m_return_reason)
-               M_Print(16, menu_height - 8, m_return_reason);
+       if (*cl_connect_status)
+               M_Print(16, menu_height - 8, cl_connect_status);
        // scroll the list as the cursor moves
        y = 48;
        visible = (int)((menu_height - 16 - y) / 8 / 2);
diff --git a/menu.h b/menu.h
index 38a034f6629b1d419dbc6b996bca5c025e184e31..6dd0b37c38dfac1c0a7681790f7fd7701fe133fd 100644 (file)
--- a/menu.h
+++ b/menu.h
@@ -52,8 +52,7 @@ enum m_state_e {
 };
 
 extern enum m_state_e m_state;
-extern char m_return_reason[128];
-void M_Update_Return_Reason(const char *s);
+
 
 /*
 // hard-coded menus
index cb2992ea06700ac32d23be131f006269bd0c289e..bcc5f66edd7c85ecfa946833b991f47c71002194 100644 (file)
--- a/netconn.c
+++ b/netconn.c
@@ -1632,15 +1632,14 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
 {
        crypto_t *crypto;
+
        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-       M_Update_Return_Reason("");
-#endif
        // Disconnect from the current server or stop demo playback
        if(cls.state == ca_connected || cls.demoplayback)
                CL_Disconnect();
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
+       strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status));
        crypto = &cls.netcon->crypto;
        if(cls.crypto.authenticated)
        {
@@ -1656,7 +1655,9 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_
                                crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
                                );
        }
-       Con_Printf("Connection accepted to %s\n", cls.netcon->address);
+       else
+               Con_Printf("%s to %s\n", cl_connect_status, cls.netcon->address);
+
        key_dest = key_game;
 #ifdef CONFIG_MENU
        m_state = m_none;
@@ -2038,7 +2039,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
 
                sendlength = sizeof(senddata) - 4;
-               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress, addressstring2))
                {
                        case CRYPTO_NOMATCH:
                                // nothing to do
@@ -2119,15 +2120,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
-                       Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring challenge message from wrong server %s\n", addressstring2);
                                return true;
                        }
+                       Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+                       strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
+
                        Protocol_Names(protocolnames, sizeof(protocolnames));
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Got challenge response");
-#endif
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
                        // TODO: add userinfo stuff here instead of using NQ commands?
@@ -2140,30 +2141,23 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring accept message from wrong server %s\n", addressstring2);
                                return true;
                        }
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Accepted");
-#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
                        return true;
                }
                if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
                {
-                       char rejectreason[128];
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("reject message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring reject message from wrong server %s\n", addressstring2);
                                return true;
                        }
                        cls.connect_trying = false;
                        string += 7;
-                       length = min(length - 7, (int)sizeof(rejectreason) - 1);
-                       memcpy(rejectreason, string, length);
-                       rejectreason[length] = 0;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason(rejectreason);
-#endif
+                       length = min(length - 7, (int)sizeof(cl_connect_status) - 1);
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %.*s", length, string);
+                       Con_Printf(CON_ERROR "Connect: rejected by %s\n" CON_ERROR "%.*s\n", addressstring2, length, string);
                        return true;
                }
 #ifdef CONFIG_MENU
@@ -2315,13 +2309,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // challenge message
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("c message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring c message from wrong server %s\n", addressstring2);
                                return true;
                        }
-                       Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Got QuakeWorld challenge response");
-#endif
+                       Con_DPrintf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
+                       strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
+
                        cls.qw_qport = qport.integer;
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
@@ -2334,12 +2327,9 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // accept message
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                               Con_DPrintf("j message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring j message from wrong server %s\n", addressstring2);
                                return true;
                        }
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("QuakeWorld Accepted");
-#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
                        return true;
                }
@@ -2396,7 +2386,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // qw print command, used by rcon replies too
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
-                               Con_DPrintf("n message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring n message from wrong server %s\n", addressstring2);
                                return true;
                        }
                        Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
@@ -2435,7 +2425,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                lhnetaddress_t clientportaddress;
                                if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
-                                       Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
+                                       Con_Printf(CON_WARN "ignoring CCREP_ACCEPT message from wrong server %s\n", addressstring2);
                                        break;
                                }
                                clientportaddress = *peeraddress;
@@ -2457,23 +2447,18 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
                                // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                                InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
-#ifdef CONFIG_MENU
-                               M_Update_Return_Reason("Accepted");
-#endif
                                NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
                        }
                        break;
                case CCREP_REJECT:
-                       if (developer_extra.integer) {
-                               Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
-                               break;
-                       }
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
+                       {
+                               Con_Printf(CON_WARN "ignoring CCREP_REJECT message from wrong server %s\n", addressstring2);
                                break;
+                       }
                        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
-#endif
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %s", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
+                       Con_Printf(CON_ERROR "Connect: rejected by %s\n%s\n", addressstring2, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
                        break;
                case CCREP_SERVER_INFO:
                        if (developer_extra.integer)
@@ -2501,7 +2486,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        break;
                case CCREP_RCON: // RocketGuy: ProQuake rcon support
                        if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
-                               Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
+                               Con_Printf(CON_WARN "ignoring CCREP_RCON message from wrong server %s\n", addressstring2);
                                break;
                        }
                        if (developer_extra.integer)
@@ -2653,22 +2638,26 @@ void NetConn_ClientFrame(void)
        unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
 
        NetConn_UpdateSockets();
+
        if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
        {
-#ifdef CONFIG_MENU
-               if (cls.connect_remainingtries == 0)
-                       M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
-#endif
-               cls.connect_nextsendtime = host.realtime + 1;
-               cls.connect_remainingtries--;
-               if (cls.connect_remainingtries <= -10)
+               if (cls.connect_remainingtries > 0)
                {
+                       cls.connect_remainingtries--;
+                       dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: sending initial request, %i %s left...", cls.connect_remainingtries, cls.connect_remainingtries == 1 ? "retry" : "retries");
+               }
+               else
+               {
+                       char address[128];
+
                        cls.connect_trying = false;
-#ifdef CONFIG_MENU
-                       M_Update_Return_Reason("Connect: Failed");
-#endif
+                       LHNETADDRESS_ToString(&cls.connect_address, address, sizeof(address), true);
+                       strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status));
+                       Con_Printf(CON_ERROR "%s from %s\n", cl_connect_status, address);
                        return;
                }
+               cls.connect_nextsendtime = host.realtime + 1;
+
                // try challenge first (newer DP server or QW)
                NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
                // then try netquake as a fallback (old server, or netquake)
@@ -2691,6 +2680,7 @@ void NetConn_ClientFrame(void)
                NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
                SZ_Clear(&cl_message);
        }
+
        for (i = 0;i < cl_numsockets;i++)
        {
                while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
@@ -3939,7 +3929,7 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw)
        if (!masterquerycount)
        {
                Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
-               M_Update_Return_Reason("No network");
+               strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status));
        }
 }
 #endif