]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
rearrange SV_VM_Begin/End again to fix crashes
[xonotic/darkplaces.git] / netconn.c
index df02ca4c7b82e070af155afaefc300baacd3e836..74ba01ba951e988b9d822be4e33f53530c4a2e7d 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -26,20 +26,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define MASTER_PORT 27950
 
 // note this defaults on for dedicated servers, off for listen servers
-cvar_t sv_public = {0, "sv_public", "0"};
-static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120"};
+cvar_t sv_public = {0, "sv_public", "0", "advertises this server on the master server (so that players can find it in the server browser)"};
+static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
 
 // FIXME: resolve DNS on masters whenever their value changes and cache it (to avoid major delays in active servers when they heartbeat)
 static cvar_t sv_masters [] =
 {
-       {CVAR_SAVE, "sv_master1", ""},
-       {CVAR_SAVE, "sv_master2", ""},
-       {CVAR_SAVE, "sv_master3", ""},
-       {CVAR_SAVE, "sv_master4", ""},
-       {0, "sv_masterextra1", "ghdigital.com"}, //69.59.212.88
-       {0, "sv_masterextra2", "dpmaster.deathmask.net"}, //64.253.41.49
-       {0, "sv_masterextra3", "12.166.196.192"}, //blaze.mindphukd.org (doesn't resolve currently but works as an ip)
-       {0, NULL, NULL}
+       {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", "ghdigital.com", "default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
+       {0, "sv_masterextra2", "dpmaster.deathmask.net", "default master server 2 (admin: Willis)"}, // admin: Willis
+       {0, "sv_masterextra3", "12.166.196.192", "default master server 3 (admin: Venim)"}, // admin: Venim
+       {0, "sv_masterextra4", "excalibur.nvg.ntnu.no", "default master server 4 (admin: tChr)"}, // admin: tChr
+       {0, NULL, NULL, NULL}
 };
 
 static double nextheartbeattime = 0;
@@ -47,18 +48,18 @@ static double nextheartbeattime = 0;
 sizebuf_t net_message;
 static unsigned char net_message_buf[NET_MAXMESSAGE];
 
-cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
-cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10"};
-cvar_t net_connecttimeout = {0, "net_connecttimeout","10"};
-cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
-cvar_t developer_networking = {0, "developer_networking", "0"};
+cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
+cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10", "give a player this much time in seconds to rejoin and continue playing (not losing frags and such)"};
+cvar_t net_connecttimeout = {0, "net_connecttimeout","10", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods)"};
+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"};
-static cvar_t cl_netpacketloss = {0, "cl_netpacketloss","0"};
-static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20"};
-static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4"};
-static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4"};
-static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3"};
+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 = {0, "cl_netpacketloss","0", "drops this percentage of packets (incoming and outgoing), useful for testing network protocol robustness (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_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)"};
 
 /* statistic counters */
 static int packetsSent = 0;
@@ -92,10 +93,10 @@ lhnetsocket_t *sv_sockets[16];
 netconn_t *netconn_list = NULL;
 mempool_t *netconn_mempool = NULL;
 
-cvar_t cl_netport = {0, "cl_port", "0"};
-cvar_t sv_netport = {0, "port", "26000"};
-cvar_t net_address = {0, "net_address", "0.0.0.0"};
-//cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]"};
+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", "0.0.0.0", "network address to open ports on"};
+//cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
 
 // ServerList interface
 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
@@ -431,74 +432,15 @@ int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnet
        return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
 }
 
-int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data)
-{
-       unsigned int packetLen;
-       unsigned int dataLen;
-       unsigned int eom;
-       unsigned int *header;
-
-//#ifdef DEBUG
-       if (data->cursize == 0)
-       {
-               Con_Printf ("Datagram_SendMessage: zero length message\n");
-               return -1;
-       }
-
-       if (data->cursize > (int)sizeof(conn->sendMessage))
-       {
-               Con_Printf ("Datagram_SendMessage: message too big (%u > %u)\n", data->cursize, sizeof(conn->sendMessage));
-               return -1;
-       }
-
-       if (conn->canSend == false)
-       {
-               Con_Printf ("SendMessage: called with canSend == false\n");
-               return -1;
-       }
-//#endif
-
-       memcpy(conn->sendMessage, data->data, data->cursize);
-       conn->sendMessageLength = data->cursize;
-
-       if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
-       {
-               dataLen = conn->sendMessageLength;
-               eom = NETFLAG_EOM;
-       }
-       else
-       {
-               dataLen = MAX_PACKETFRAGMENT;
-               eom = 0;
-       }
-
-       packetLen = NET_HEADERSIZE + dataLen;
-
-       header = (unsigned int *)sendbuffer;
-       header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-       header[1] = BigLong(conn->sendSequence);
-       memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
-
-       conn->sendSequence++;
-       conn->canSend = false;
-
-       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
-               return -1;
-
-       conn->lastSendTime = realtime;
-       packetsSent++;
-       reliableMessagesSent++;
-       return 1;
-}
-
-static void NetConn_SendMessageNext(netconn_t *conn)
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
 {
        unsigned int packetLen;
        unsigned int dataLen;
        unsigned int eom;
        unsigned int *header;
 
-       if (conn->sendMessageLength && !conn->canSend && conn->sendNext)
+       // if a reliable message fragment has been lost, send it again
+       if (!conn->canSend && conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
        {
                if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
                {
@@ -515,29 +457,36 @@ static void NetConn_SendMessageNext(netconn_t *conn)
 
                header = (unsigned int *)sendbuffer;
                header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-               header[1] = BigLong(conn->sendSequence);
+               header[1] = BigLong(conn->sendSequence - 1);
                memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
-               conn->sendSequence++;
-               conn->sendNext = false;
+               if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+               {
+                       conn->lastSendTime = realtime;
+                       packetsReSent++;
+               }
+       }
 
-               if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
-                       return;
+       // if we have a new reliable message to send, do so
+       if (conn->canSend && conn->message.cursize)
+       {
+               if (conn->message.cursize > (int)sizeof(conn->sendMessage))
+               {
+                       Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, sizeof(conn->sendMessage));
+                       conn->message.overflowed = true;
+                       return -1;
+               }
 
-               conn->lastSendTime = realtime;
-               packetsSent++;
-       }
-}
+               if (developer_networking.integer && conn == cls.netcon)
+               {
+                       Con_Print("client sending reliable message to server:\n");
+                       SZ_HexDumpToConsole(&conn->message);
+               }
 
-static void NetConn_ReSendMessage(netconn_t *conn)
-{
-       unsigned int packetLen;
-       unsigned int dataLen;
-       unsigned int eom;
-       unsigned int *header;
+               memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
+               conn->sendMessageLength = conn->message.cursize;
+               SZ_Clear(&conn->message);
 
-       if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0)
-       {
                if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
                {
                        dataLen = conn->sendMessageLength;
@@ -553,58 +502,43 @@ static void NetConn_ReSendMessage(netconn_t *conn)
 
                header = (unsigned int *)sendbuffer;
                header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-               header[1] = BigLong(conn->sendSequence - 1);
+               header[1] = BigLong(conn->sendSequence);
                memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
-               conn->sendNext = false;
+               conn->sendSequence++;
+               conn->canSend = false;
 
-               if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
-                       return;
+               NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
                conn->lastSendTime = realtime;
-               packetsReSent++;
+               packetsSent++;
+               reliableMessagesSent++;
        }
-}
-
-qboolean NetConn_CanSendMessage(netconn_t *conn)
-{
-       return conn->canSend;
-}
-
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
-{
-       int packetLen;
-       unsigned int *header;
 
-       packetLen = NET_HEADERSIZE + data->cursize;
-
-//#ifdef DEBUG
-       if (data->cursize == 0)
+       // if we have an unreliable message to send, do so
+       if (data->cursize)
        {
-               Con_Printf ("Datagram_SendUnreliableMessage: zero length message\n");
-               return -1;
-       }
+               packetLen = NET_HEADERSIZE + data->cursize;
 
-       if (packetLen > (int)sizeof(sendbuffer))
-       {
-               Con_Printf ("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
-               return -1;
-       }
-//#endif
+               if (packetLen > (int)sizeof(sendbuffer))
+               {
+                       Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
+                       return -1;
+               }
 
-       header = (unsigned int *)sendbuffer;
-       header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
-       header[1] = BigLong(conn->unreliableSendSequence);
-       memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
+               header = (unsigned int *)sendbuffer;
+               header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
+               header[1] = BigLong(conn->unreliableSendSequence);
+               memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
 
-       conn->unreliableSendSequence++;
+               conn->unreliableSendSequence++;
 
-       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
-               return -1;
+               NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
-       packetsSent++;
-       unreliableMessagesSent++;
-       return 1;
+               packetsSent++;
+               unreliableMessagesSent++;
+       }
+       return 0;
 }
 
 void NetConn_CloseClientPorts(void)
@@ -690,12 +624,11 @@ void NetConn_OpenServerPort(const char *addressstring, int defaultport)
        }
 }
 
-static void NetConn_UpdateServerStuff(void);
 void NetConn_OpenServerPorts(int opennetports)
 {
        int port;
        NetConn_CloseServerPorts();
-       NetConn_UpdateServerStuff();
+       NetConn_UpdateSockets();
        port = bound(0, sv_netport.integer, 65535);
        if (port == 0)
                port = 26000;
@@ -739,6 +672,9 @@ netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
        conn->peeraddress = *peeraddress;
        conn->canSend = true;
        conn->lastMessageTime = realtime;
+       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
        // reduce effectiveness of connection request floods
        conn->timeout = realtime + net_connecttimeout.value;
@@ -775,7 +711,7 @@ void NetConn_Close(netconn_t *conn)
 static int clientport = -1;
 static int clientport2 = -1;
 static int hostport = -1;
-static void NetConn_UpdateServerStuff(void)
+void NetConn_UpdateSockets(void)
 {
        if (cls.state != ca_dedicated)
        {
@@ -802,7 +738,7 @@ static void NetConn_UpdateServerStuff(void)
        }
 }
 
-int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length)
+static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length)
 {
        unsigned int count;
        unsigned int flags;
@@ -858,12 +794,41 @@ int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length)
                                                        Con_DPrint("ack sequencing error\n");
                                                conn->lastMessageTime = realtime;
                                                conn->timeout = realtime + net_messagetimeout.value;
-                                               conn->sendMessageLength -= MAX_PACKETFRAGMENT;
-                                               if (conn->sendMessageLength > 0)
+                                               if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
                                                {
+                                                       unsigned int packetLen;
+                                                       unsigned int dataLen;
+                                                       unsigned int eom;
+                                                       unsigned int *header;
+
+                                                       conn->sendMessageLength -= MAX_PACKETFRAGMENT;
                                                        memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
-                                                       conn->sendNext = true;
-                                                       NetConn_SendMessageNext(conn);
+
+                                                       if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
+                                                       {
+                                                               dataLen = conn->sendMessageLength;
+                                                               eom = NETFLAG_EOM;
+                                                       }
+                                                       else
+                                                       {
+                                                               dataLen = MAX_PACKETFRAGMENT;
+                                                               eom = 0;
+                                                       }
+
+                                                       packetLen = NET_HEADERSIZE + dataLen;
+
+                                                       header = (unsigned int *)sendbuffer;
+                                                       header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
+                                                       header[1] = BigLong(conn->sendSequence);
+                                                       memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
+
+                                                       conn->sendSequence++;
+
+                                                       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+                                                       {
+                                                               conn->lastSendTime = realtime;
+                                                               packetsSent++;
+                                                       }
                                                }
                                                else
                                                {
@@ -929,7 +894,7 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
        CL_Disconnect();
        // if we're connecting to a remote server, shut down any local server
        if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
-               Host_ShutdownServer (false);
+               Host_ShutdownServer ();
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
        Con_Printf("Connection accepted to %s\n", cls.netcon->address);
@@ -947,7 +912,7 @@ int NetConn_IsLocalGame(void)
        return false;
 }
 
-int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
+static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
 {
        int ret, c, control;
        const char *s;
@@ -965,9 +930,10 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                stringbuf[length] = 0;
                string = stringbuf;
 
+               LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
+
                if (developer.integer)
                {
-                       LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
                        Com_HexDumpToConsole(data, length);
                }
@@ -976,7 +942,6 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                {
                        char protocolnames[1400];
                        Protocol_Names(protocolnames, sizeof(protocolnames));
-                       LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
                        M_Update_Return_Reason("Got challenge response");
                        NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
@@ -1121,6 +1086,11 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                if (!strncmp(string, "ack", 3))
                        return true;
                */
+               if (string[0] == 'n')
+               {
+                       // qw print command
+                       Con_Printf("QW print command from server at %s:\n", addressstring2, string + 1);
+               }
                // we may not have liked the packet, but it was a command packet, so
                // we're done processing this packet now
                return true;
@@ -1286,8 +1256,7 @@ void NetConn_ClientFrame(void)
 {
        int i, length;
        lhnetaddress_t peeraddress;
-       netconn_t *conn;
-       NetConn_UpdateServerStuff();
+       NetConn_UpdateSockets();
        if (cls.connect_trying && cls.connect_nextsendtime < realtime)
        {
                if (cls.connect_remainingtries == 0)
@@ -1313,20 +1282,16 @@ void NetConn_ClientFrame(void)
                NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
                SZ_Clear(&net_message);
        }
-       for (i = 0;i < cl_numsockets;i++) {
-               while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0) {
+       for (i = 0;i < cl_numsockets;i++)
+               while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
                        NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
-               }
-       }
        NetConn_QueryQueueFrame();
        if (cls.netcon && realtime > cls.netcon->timeout)
        {
                Con_Print("Connection timed out\n");
                CL_Disconnect();
-               Host_ShutdownServer (false);
+               Host_ShutdownServer ();
        }
-       for (conn = netconn_list;conn;conn = conn->next)
-               NetConn_ReSendMessage(conn);
 }
 
 #define MAX_CHALLENGES 128
@@ -1427,13 +1392,22 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 }
 
 extern void SV_SendServerinfo (client_t *client);
-int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
+static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
 {
        int i, ret, clientnum, best;
        double besttime;
        client_t *client;
        netconn_t *conn;
-       char *s, *string, response[512], addressstring2[128], stringbuf[16384];
+       char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
+
+       // see if we can identify the sender as a local player
+       // (this is necessary for rcon to send a reliable reply if the client is
+       //  actually on the server, not sending remotely)
+       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+               if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+                       break;
+       if (i == svs.maxclients)
+               host_client = NULL;
 
        if (sv.active)
        {
@@ -1540,7 +1514,9 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                                                                                        Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
                                                                                NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                                                                // now set up the client
+                                                                               SV_VM_Begin();
                                                                                SV_ConnectClient(clientnum, conn);
+                                                                               SV_VM_End();
                                                                                NetConn_Heartbeat(1);
                                                                        }
                                                                }
@@ -1589,6 +1565,46 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                                }
                                return true;
                        }
+                       if (length >= 5 && !memcmp(string, "rcon ", 5))
+                       {
+                               int i;
+                               char *s = string + 5;
+                               char password[64];
+                               for (i = 0;*s > ' ';s++)
+                                       if (i < (int)sizeof(password) - 1)
+                                               password[i++] = *s;
+                               password[i] = 0;
+                               if (!strcmp(rcon_password.string, password))
+                               {
+                                       // looks like a legitimate rcon command with the correct password
+                                       Con_Printf("server received rcon command from %s:\n%s\n", host_client ? host_client->name : addressstring2, s);
+                                       rcon_redirect = true;
+                                       rcon_redirect_bufferpos = 0;
+                                       Cmd_ExecuteString(s, src_command);
+                                       rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
+                                       rcon_redirect = false;
+                                       // print resulting text to client
+                                       // if client is playing, send a reliable reply instead of
+                                       // a command packet
+                                       if (host_client)
+                                       {
+                                               // if the netconnection is loop, then this is the
+                                               // local player on a listen mode server, and it would
+                                               // result in duplicate printing to the console
+                                               // (not that the local player should be using rcon
+                                               //  when they have the console)
+                                               if (host_client->netconnection && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+                                                       SV_ClientPrintf("%s", rcon_redirect_buffer);
+                                       }
+                                       else
+                                       {
+                                               // qw print command
+                                               dpsnprintf(response, sizeof(response), "\377\377\377\377n%s", rcon_redirect_buffer);
+                                               NetConn_WriteString(mysocket, response, peeraddress);
+                                       }
+                               }
+                               return true;
+                       }
                        /*
                        if (!strncmp(string, "ping", 4))
                        {
@@ -1694,7 +1710,9 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                                                                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                                                        SZ_Clear(&net_message);
                                                                        // now set up the client struct
+                                                                       SV_VM_Begin();
                                                                        SV_ConnectClient(clientnum, conn);
+                                                                       SV_VM_End();
                                                                        NetConn_Heartbeat(1);
                                                                }
                                                                else
@@ -1807,12 +1825,13 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int
                        }
                }
 #endif
-               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+               if (host_client)
                {
-                       if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+                       if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
                        {
-                               if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
-                                       SV_ReadClientMessage();
+                               SV_VM_Begin();
+                               SV_ReadClientMessage();
+                               SV_VM_End();
                                return ret;
                        }
                }
@@ -1824,8 +1843,7 @@ void NetConn_ServerFrame(void)
 {
        int i, length;
        lhnetaddress_t peeraddress;
-       netconn_t *conn;
-       NetConn_UpdateServerStuff();
+       NetConn_UpdateSockets();
        for (i = 0;i < sv_numsockets;i++)
                while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
                        NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
@@ -1838,8 +1856,6 @@ void NetConn_ServerFrame(void)
                        SV_DropClient(false);
                }
        }
-       for (conn = netconn_list;conn;conn = conn->next)
-               NetConn_ReSendMessage(conn);
 }
 
 void NetConn_QueryMasters(void)
@@ -1928,43 +1944,6 @@ void NetConn_Heartbeat(int priority)
        }
 }
 
-int NetConn_SendToAll(sizebuf_t *data, double blocktime)
-{
-       int i, count = 0;
-       unsigned char sent[MAX_SCOREBOARD];
-
-       memset(sent, 0, sizeof(sent));
-
-       // simultaneously wait for the first CanSendMessage and send the message,
-       // then wait for a second CanSendMessage (verifying it was received), or
-       // the client drops and is no longer counted
-       // the loop aborts when either it runs out of clients to send to, or a
-       // timeout expires
-       blocktime += Sys_DoubleTime();
-       do
-       {
-               count = 0;
-               // run a network frame to check for packets
-               NetConn_ClientFrame();SV_VM_Begin();NetConn_ServerFrame();SV_VM_End();
-               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
-               {
-                       if (host_client->netconnection)
-                       {
-                               if (NetConn_CanSendMessage(host_client->netconnection))
-                               {
-                                       if (!sent[i])
-                                               NetConn_SendReliableMessage(host_client->netconnection, data);
-                                       sent[i] = true;
-                               }
-                               if (!NetConn_CanSendMessage(host_client->netconnection))
-                                       count++;
-                       }
-               }
-       }
-       while (count && Sys_DoubleTime() < blocktime);
-       return count;
-}
-
 static void Net_Heartbeat_f(void)
 {
        if (sv.active)
@@ -2014,9 +1993,9 @@ void NetConn_Init(void)
        int i;
        lhnetaddress_t tempaddress;
        netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
-       Cmd_AddCommand("net_stats", Net_Stats_f);
-       Cmd_AddCommand("net_slist", Net_Slist_f);
-       Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
+       Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
+       Cmd_AddCommand("net_slist", Net_Slist_f, "query master series and print all server information");
+       Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
        Cvar_RegisterVariable(&net_slist_queriespersecond);
        Cvar_RegisterVariable(&net_slist_queriesperframe);
        Cvar_RegisterVariable(&net_slist_timeout);