]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
Reworked VM_CL_PolygonBegin/Vertex/End functions to do the commit to the mesh in...
[xonotic/darkplaces.git] / netconn.c
index 4badfab6735f1360276abd1cca3438f07d33fc19..95b6a8a3a342f98e06d7ce3acfd353bab9facd92 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -1,7 +1,7 @@
 /*
 Copyright (C) 1996-1997 Id Software, Inc.
 Copyright (C) 2002 Mathieu Olivier
-Copyright (C) 2003 Forest Hale
+Copyright (C) 2003 Ashley Rose Hale (LadyHavoc)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
@@ -33,39 +33,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define DPMASTER_PORT 27950
 
 // note this defaults on for dedicated servers, off for listen servers
-cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
-cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
-static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
+cvar_t sv_public = {CVAR_SERVER, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
+cvar_t sv_public_rejectreason = {CVAR_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
+static cvar_t sv_heartbeatperiod = {CVAR_SERVER | CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
 extern cvar_t sv_status_privacy;
 
 static cvar_t sv_masters [] =
 {
-       {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
-       {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
-       {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
-       {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
-       {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
-       {0, "sv_masterextra2", "107.161.23.68", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
-       {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
-#ifdef SUPPORTIPV6
-       {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
-       {0, "sv_masterextra5", "[2604:180::4ac:98c1]:27950", "dpmaster.deathmask.net - default master server 5 ipv6 address of dpmaster.deathmask.net (admin: Willis)"}, // admin: Willis
-#endif
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
+       {CVAR_CLIENT | CVAR_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"}, // admin: Willis
+       {CVAR_CLIENT | CVAR_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"}, // admin: tChr
        {0, NULL, NULL, NULL}
 };
 
 #ifdef CONFIG_MENU
 static cvar_t sv_qwmasters [] =
 {
-       {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
-       {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
-       {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
-       {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
-       {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
+       {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
+       {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
+       {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
+       {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
+       {CVAR_CLIENT | CVAR_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
        {0, NULL, NULL, NULL}
 };
 #endif
@@ -79,33 +73,34 @@ static unsigned char sv_message_buf[NET_MAXMESSAGE];
 char cl_readstring[MAX_INPUTLINE];
 char sv_readstring[MAX_INPUTLINE];
 
-cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
-cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
-cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
-cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
-cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
-cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
-cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
-cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
-cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
-cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
-
-cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
-static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
-static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
-static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
-static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
-static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
-static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
-static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
-static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
-static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
-static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
-static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
-static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
-static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
-static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
-static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
+cvar_t net_test = {CVAR_CLIENT | CVAR_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
+cvar_t net_usesizelimit = {CVAR_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
+cvar_t net_burstreserve = {CVAR_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
+cvar_t net_messagetimeout = {CVAR_CLIENT | CVAR_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
+cvar_t net_connecttimeout = {CVAR_CLIENT | CVAR_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
+cvar_t net_connectfloodblockingtimeout = {CVAR_SERVER, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
+cvar_t net_challengefloodblockingtimeout = {CVAR_SERVER, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
+cvar_t net_getstatusfloodblockingtimeout = {CVAR_SERVER, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
+cvar_t net_sourceaddresscheck = {CVAR_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
+cvar_t hostname = {CVAR_SERVER | CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
+cvar_t developer_networking = {CVAR_CLIENT | CVAR_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
+
+cvar_t cl_netlocalping = {CVAR_CLIENT, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
+static cvar_t cl_netpacketloss_send = {CVAR_CLIENT, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
+static cvar_t cl_netpacketloss_receive = {CVAR_CLIENT, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
+static cvar_t net_slist_queriespersecond = {CVAR_CLIENT, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
+static cvar_t net_slist_queriesperframe = {CVAR_CLIENT, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
+static cvar_t net_slist_timeout = {CVAR_CLIENT, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
+static cvar_t net_slist_pause = {CVAR_CLIENT, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
+static cvar_t net_slist_maxtries = {CVAR_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
+static cvar_t net_slist_favorites = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
+static cvar_t net_tos_dscp = {CVAR_CLIENT | CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
+static cvar_t gameversion = {CVAR_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
+static cvar_t gameversion_min = {CVAR_CLIENT | CVAR_SERVER, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
+static cvar_t gameversion_max = {CVAR_CLIENT | CVAR_SERVER, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
+static cvar_t rcon_restricted_password = {CVAR_SERVER | CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
+static cvar_t rcon_restricted_commands = {CVAR_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
+static cvar_t rcon_secure_maxdiff = {CVAR_SERVER, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
 extern cvar_t rcon_secure;
 extern cvar_t rcon_secure_challengetimeout;
 
@@ -115,7 +110,7 @@ int masterreplycount = 0;
 int serverquerycount = 0;
 int serverreplycount = 0;
 
-challenge_t challenge[MAX_CHALLENGES];
+challenge_t challenges[MAX_CHALLENGES];
 
 #ifdef CONFIG_MENU
 /// this is only false if there are still servers left to query
@@ -136,10 +131,10 @@ netconn_t *netconn_list = NULL;
 mempool_t *netconn_mempool = NULL;
 void *netconn_mutex = NULL;
 
-cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
-cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
-cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
-cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
+cvar_t cl_netport = {CVAR_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
+cvar_t sv_netport = {CVAR_SERVER, "port", "26000", "server port for players to connect to"};
+cvar_t net_address = {CVAR_CLIENT | CVAR_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
+cvar_t net_address_ipv6 = {CVAR_CLIENT | CVAR_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
 
 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
 int cl_net_extresponse_count = 0;
@@ -764,9 +759,9 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        sendreliable = true;
                }
                // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
+               StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
                // last received unreliable packet number, and last received reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
+               StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
                packetLen = 8;
                conn->outgoing_unreliable_sequence++;
                // client sends qport in every packet
@@ -1000,7 +995,7 @@ void NetConn_OpenClientPorts(void)
                Con_Printf("Client using port %i\n", port);
        NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
        NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
        NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
 #endif
 }
@@ -1072,7 +1067,7 @@ void NetConn_OpenServerPorts(int opennetports)
                NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
        if (opennetports)
        {
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
                qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
                NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
 #else
@@ -1111,7 +1106,7 @@ netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
        conn->message.data = conn->messagedata;
        conn->message.maxsize = sizeof(conn->messagedata);
        conn->message.cursize = 0;
-       // LordHavoc: (inspired by ProQuake) use a short connect timeout to
+       // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
        // reduce effectiveness of connection request floods
        conn->timeout = realtime + net_connecttimeout.value;
        LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
@@ -1212,8 +1207,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
-               int sequence, sequence_ack;
-               int reliable_ack, reliable_message;
+               unsigned int sequence, sequence_ack;
+               qboolean reliable_ack, reliable_message;
                int count;
                //int qport;
 
@@ -1234,8 +1229,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                }
 
                conn->packetsReceived++;
-               reliable_message = (sequence >> 31) & 1;
-               reliable_ack = (sequence_ack >> 31) & 1;
+               reliable_message = (sequence >> 31) != 0;
+               reliable_ack = (sequence_ack >> 31) != 0;
                sequence &= ~(1<<31);
                sequence_ack &= ~(1<<31);
                if (sequence <= conn->qw.incoming_sequence)
@@ -1248,6 +1243,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                {
                        conn->droppedDatagrams += count;
                        //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                       // If too may packets have been dropped, only write the
+                       // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                       // Because there's no point in writing more than
+                       // these as the netgraph is going to be full anyway.
+                       if (count > NETGRAPH_PACKETS)
+                               count = NETGRAPH_PACKETS;
                        while (count--)
                        {
                                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
@@ -1338,6 +1339,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                                                count = sequence - conn->nq.unreliableReceiveSequence;
                                                conn->droppedDatagrams += count;
                                                //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                                               // If too may packets have been dropped, only write the
+                                               // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                                               // Because there's no point in writing more than
+                                               // these as the netgraph is going to be full anyway.
+                                               if (count > NETGRAPH_PACKETS)
+                                                       count = NETGRAPH_PACKETS;
                                                while (count--)
                                                {
                                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
@@ -1506,8 +1513,6 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_
 #ifdef CONFIG_MENU
        M_Update_Return_Reason("");
 #endif
-       // the connection request succeeded, stop current connection and set up a new connection
-       CL_Disconnect();
        // if we're connecting to a remote server, shut down any local server
        if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
        {
@@ -1790,7 +1795,6 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        char ipstring[32];
        const char *s;
 #endif
-       char vabuf[1024];
 
        // quakeworld ingame packet
        fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
@@ -1897,20 +1901,30 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
-                       Protocol_Names(protocolnames, sizeof(protocolnames));
                        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);
+                               return true;
+                       }
+                       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?
-                       NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
                {
                        // darkplaces or quake3
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("Accepted");
 #endif
@@ -1920,6 +1934,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                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);
+                               return true;
+                       }
                        cls.connect_trying = false;
                        string += 7;
                        length = min(length - 7, (int)sizeof(rejectreason) - 1);
@@ -2090,6 +2108,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
                {
                        // challenge message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("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");
@@ -2097,12 +2119,18 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        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);
-                       NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length >= 1 && string[0] == 'j' && cls.connect_trying)
                {
                        // accept message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("j message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("QuakeWorld Accepted");
 #endif
@@ -2160,7 +2188,11 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (string[0] == 'n')
                {
-                       // qw print command
+                       // 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);
+                               return true;
+                       }
                        Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
                }
                // we may not have liked the packet, but it was a command packet, so
@@ -2196,6 +2228,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (cls.connect_trying)
                        {
                                lhnetaddress_t clientportaddress;
+                               if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                                       Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
+                                       break;
+                               }
                                clientportaddress = *peeraddress;
                                LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
                                // extra ProQuake stuff
@@ -2222,8 +2258,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                        break;
                case CCREP_REJECT:
-                       if (developer_extra.integer)
-                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
+                       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))
+                               break;
                        cls.connect_trying = false;
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
@@ -2233,7 +2273,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
 #ifdef CONFIG_MENU
-                       // LordHavoc: because the quake server may report weird addresses
+                       // LadyHavoc: because the quake server may report weird addresses
                        // we just ignore it and keep the real address
                        MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
                        // search the cache for this server and update it
@@ -2254,6 +2294,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 #endif
                        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);
+                               break;
+                       }
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
 
@@ -2464,7 +2508,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        int length;
        char teambuf[3];
        const char *crypto_idstring;
-       const char *str;
+       const char *worldstatusstr;
 
        // How many clients are there?
        for (i = 0;i < (unsigned int)svs.maxclients;i++)
@@ -2478,13 +2522,13 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        }
 
        *qcstatus = 0;
-       str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
-       if(str && *str)
+       worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
+       if(worldstatusstr && *worldstatusstr)
        {
                char *p;
                const char *q;
                p = qcstatus;
-               for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
+               for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
                        if(*q != '\\' && *q != '\n')
                                *p++ = *q;
                *p = 0;
@@ -2525,13 +2569,13 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 
                for (i = 0;i < (unsigned int)svs.maxclients;i++)
                {
-                       client_t *cl = &svs.clients[i];
-                       if (cl->active)
+                       client_t *client = &svs.clients[i];
+                       if (client->active)
                        {
                                int nameind, cleanind, pingvalue;
                                char curchar;
-                               char cleanname [sizeof(cl->name)];
-                               const char *str;
+                               char cleanname [sizeof(client->name)];
+                               const char *statusstr;
                                prvm_edict_t *ed;
 
                                // Remove all characters '"' and '\' in the player name
@@ -2539,7 +2583,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                cleanind = 0;
                                do
                                {
-                                       curchar = cl->name[nameind++];
+                                       curchar = client->name[nameind++];
                                        if (curchar != '"' && curchar != '\\')
                                        {
                                                cleanname[cleanind++] = curchar;
@@ -2549,21 +2593,21 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                } while (curchar != '\0');
                                cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
 
-                               pingvalue = (int)(cl->ping * 1000.0f);
-                               if(cl->netconnection)
+                               pingvalue = (int)(client->ping * 1000.0f);
+                               if(client->netconnection)
                                        pingvalue = bound(1, pingvalue, 9999);
                                else
                                        pingvalue = 0;
 
                                *qcstatus = 0;
                                ed = PRVM_EDICT_NUM(i + 1);
-                               str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
-                               if(str && *str)
+                               statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+                               if(statusstr && *statusstr)
                                {
                                        char *p;
                                        const char *q;
                                        p = qcstatus;
-                                       for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+                                       for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
                                                if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
                                                        *p++ = *q;
                                        *p = 0;
@@ -2571,15 +2615,15 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 
                                if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
                                {
-                                       if(cl->frags == -666) // spectator
+                                       if(client->frags == -666) // spectator
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
-                                       else if(cl->colors == 0x44) // red team
+                                       else if(client->colors == 0x44) // red team
                                                strlcpy(teambuf, " 1", sizeof(teambuf));
-                                       else if(cl->colors == 0xDD) // blue team
+                                       else if(client->colors == 0xDD) // blue team
                                                strlcpy(teambuf, " 2", sizeof(teambuf));
-                                       else if(cl->colors == 0xCC) // yellow team
+                                       else if(client->colors == 0xCC) // yellow team
                                                strlcpy(teambuf, " 3", sizeof(teambuf));
-                                       else if(cl->colors == 0x99) // pink team
+                                       else if(client->colors == 0x99) // pink team
                                                strlcpy(teambuf, " 4", sizeof(teambuf));
                                        else
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
@@ -2596,7 +2640,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                                                                cleanname);
                                else
                                        length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
-                                                                               cl->frags,
+                                                                               client->frags,
                                                                                pingvalue,
                                                                                teambuf,
                                                                                cleanname);
@@ -2692,6 +2736,11 @@ static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const cha
        char mdfourbuf[16];
        long t1, t2;
 
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        t1 = (long) time(NULL);
        t2 = strtol(s, NULL, 0);
        if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
@@ -2708,13 +2757,18 @@ static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, cons
        char mdfourbuf[16];
        int i;
 
-       if(slen < (int)(sizeof(challenge[0].string)) - 1)
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
+       if(slen < (int)(sizeof(challenges[0].string)) - 1)
                return false;
 
        // validate the challenge
        for (i = 0;i < MAX_CHALLENGES;i++)
-               if(challenge[i].time > 0)
-                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
+               if(challenges[i].time > 0)
+                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
                                break;
        // if the challenge is not recognized, drop the packet
        if (i == MAX_CHALLENGES)
@@ -2727,13 +2781,18 @@ static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, cons
                return false;
 
        // unmark challenge to prevent replay attacks
-       challenge[i].time = 0;
+       challenges[i].time = 0;
 
        return true;
 }
 
 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        return !strcmp(password, hash);
 }
 
@@ -2752,12 +2811,12 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto allow;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2771,12 +2830,12 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto check;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2860,7 +2919,7 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c
                        if(l)
                        {
                                client_t *host_client_save = host_client;
-                               Cmd_ExecuteString(s, src_command, true);
+                               Cmd_ExecuteString(&cmd_server, s, src_command, true);
                                host_client = host_client_save;
                                // in case it is a command that changes host_client (like restart)
                        }
@@ -2878,14 +2937,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 {
        int i, ret, clientnum, best;
        double besttime;
-       client_t *client;
-       char *s, *string, response[1400], addressstring2[128];
+       char *string, response[1400], addressstring2[128];
        static char stringbuf[16384]; // server only
        qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
        char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
        size_t sendlength, response_len;
        char infostringvalue[MAX_INPUTLINE];
-       char vabuf[1024];
 
        if (!sv.active)
                return false;
@@ -2950,29 +3007,30 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
                        {
-                               if(challenge[i].time > 0)
-                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                               if(challenges[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
                                                break;
-                               if (besttime > challenge[i].time)
-                                       besttime = challenge[best = i].time;
+                               if (besttime > challenges[i].time)
+                                       besttime = challenges[best = i].time;
                        }
                        // if we did not find an exact match, choose the oldest and
                        // update address and string
                        if (i == MAX_CHALLENGES)
                        {
                                i = best;
-                               challenge[i].address = *peeraddress;
-                               NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
+                               challenges[i].address = *peeraddress;
+                               NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
                        }
                        else
                        {
                                // flood control: drop if requesting challenge too often
-                               if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
+                               if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
                                        return true;
                        }
-                       challenge[i].time = realtime;
+                       challenges[i].time = realtime;
                        // send the challenge
-                       dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
+                       memcpy(response, "\377\377\377\377", 4);
+                       dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
                        response_len = strlen(response) + 1;
                        Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
                        NetConn_Write(mysocket, response, (int)response_len, peeraddress);
@@ -2980,6 +3038,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (length > 8 && !memcmp(string, "connect\\", 8))
                {
+                       char *s;
+                       client_t *client;
                        crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
                        string += 7;
                        length -= 7;
@@ -3007,8 +3067,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                {
                                        // validate the challenge
                                        for (i = 0;i < MAX_CHALLENGES;i++)
-                                               if(challenge[i].time > 0)
-                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
+                                               if(challenges[i].time > 0)
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
                                                                break;
                                        // if the challenge is not recognized, drop the packet
                                        if (i == MAX_CHALLENGES)
@@ -3023,7 +3083,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
-                               NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
+                               memcpy(response, "\377\377\377\377", 4);
+                               dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
+                               NetConn_WriteString(mysocket, response, peeraddress);
                                return true;
                        }
 
@@ -3204,7 +3266,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (length >= 5 && !memcmp(string, "rcon ", 5))
                {
-                       int i;
+                       int j;
                        char *s = string + 5;
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
@@ -3212,12 +3274,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if(rcon_secure.integer > 0)
                                return true;
 
-                       for (i = 0;!ISWHITESPACE(*s);s++)
-                               if (i < (int)sizeof(password) - 1)
-                                       password[i++] = *s;
+                       for (j = 0;!ISWHITESPACE(*s);s++)
+                               if (j < (int)sizeof(password) - 1)
+                                       password[j++] = *s;
                        if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
                                ++s;
-                       password[i] = 0;
+                       password[j] = 0;
                        if (!ISWHITESPACE(password[0]))
                        {
                                const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
@@ -3257,6 +3319,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                int c;
                int protocolnumber;
                const char *protocolname;
+               client_t *knownclient;
+               client_t *newclient;
                data += 4;
                length -= 4;
                SZ_Clear(&sv_message);
@@ -3276,7 +3340,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // save space for the header, filled in later
                                MSG_WriteLong(&sv_message, 0);
                                MSG_WriteByte(&sv_message, CCREP_REJECT);
-                               MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
+                               MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
+                               MSG_WriteString(&sv_message, "\n");
                                StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
                                NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
                                SZ_Clear(&sv_message);
@@ -3301,16 +3366,16 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        // see if this connect request comes from a known client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
                        {
-                               if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+                               if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
                                {
                                        // this is either a duplicate connection request
                                        // or coming back from a timeout
                                        // (if so, keep their stuff intact)
 
                                        crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
-                                       if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
+                                       if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
                                        {
                                                if (developer_extra.integer)
                                                        Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
@@ -3332,15 +3397,15 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        // save space for the header, filled in later
                                        MSG_WriteLong(&sv_message, 0);
                                        MSG_WriteByte(&sv_message, CCREP_ACCEPT);
-                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
+                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
                                        StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
                                        SZ_Clear(&sv_message);
 
                                        // if client is already spawned, re-send the
                                        // serverinfo message as they'll need it to play
-                                       if (client->begun)
-                                               SV_SendServerinfo(client);
+                                       if (knownclient->begun)
+                                               SV_SendServerinfo(knownclient);
                                        return true;
                                }
                        }
@@ -3350,10 +3415,10 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                break;
 
                        // find a slot for the new client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
                        {
                                netconn_t *conn;
-                               if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
+                               if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
                                {
                                        // connect to the client
                                        // everything is allocated, just fill in the details
@@ -3478,7 +3543,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                                // find the search start location
                                prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
-                               var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
+                               var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CVAR_NOTIFY);
 
                                // send the response
                                SZ_Clear(&sv_message);
@@ -3611,7 +3676,8 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                        cmdname = "getservers";
                                        extraoptions = "";
                                }
-                               dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
+                               memcpy(request, "\377\377\377\377", 4);
+                               dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
 
                                // search internet
                                for (masternum = 0;sv_masters[masternum].name;masternum++)
@@ -3725,7 +3791,7 @@ void NetConn_Heartbeat(int priority)
        }
 }
 
-static void Net_Heartbeat_f(void)
+static void Net_Heartbeat_f(cmd_state_t *cmd)
 {
        if (sv.active)
                NetConn_Heartbeat(2);
@@ -3750,7 +3816,7 @@ static void PrintStats(netconn_t *conn)
        Con_Printf("droppedDatagrams           = %i\n", conn->droppedDatagrams);
 }
 
-void Net_Stats_f(void)
+void Net_Stats_f(cmd_state_t *cmd)
 {
        netconn_t *conn;
        Con_Print("connections                =\n");
@@ -3759,7 +3825,7 @@ void Net_Stats_f(void)
 }
 
 #ifdef CONFIG_MENU
-void Net_Refresh_f(void)
+void Net_Refresh_f(cmd_state_t *cmd)
 {
        if (m_state != m_slist) {
                Con_Print("Sending new requests to master servers\n");
@@ -3769,7 +3835,7 @@ void Net_Refresh_f(void)
                ServerList_QueryList(false, true, false, false);
 }
 
-void Net_Slist_f(void)
+void Net_Slist_f(cmd_state_t *cmd)
 {
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
@@ -3782,7 +3848,7 @@ void Net_Slist_f(void)
                ServerList_QueryList(true, true, false, false);
 }
 
-void Net_SlistQW_f(void)
+void Net_SlistQW_f(cmd_state_t *cmd)
 {
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
@@ -3802,13 +3868,14 @@ void NetConn_Init(void)
        int i;
        lhnetaddress_t tempaddress;
        netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
-       Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
+       Cmd_AddCommand(&cmd_client, "net_stats", Net_Stats_f, "print network statistics");
+       Cmd_AddCommand(&cmd_server, "net_stats", Net_Stats_f, "print network statistics");
 #ifdef CONFIG_MENU
-       Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
-       Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
-       Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
+       Cmd_AddCommand(&cmd_client, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
+       Cmd_AddCommand(&cmd_client, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
+       Cmd_AddCommand(&cmd_client, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
 #endif
-       Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
+       Cmd_AddCommand(&cmd_server, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
        Cvar_RegisterVariable(&net_test);
        Cvar_RegisterVariable(&net_usesizelimit);
        Cvar_RegisterVariable(&net_burstreserve);
@@ -3828,6 +3895,7 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
        Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
        Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
+       Cvar_RegisterVariable(&net_sourceaddresscheck);
        Cvar_RegisterVariable(&cl_netlocalping);
        Cvar_RegisterVariable(&cl_netpacketloss_send);
        Cvar_RegisterVariable(&cl_netpacketloss_receive);