X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=netconn.c;h=342d78b253cdb623d36ad5c1c9fee34b025bf970;hb=HEAD;hp=bcc5f66edd7c85ecfa946833b991f47c71002194;hpb=6dca288eb9bac8251395386e6495866e05806f4e;p=xonotic%2Fdarkplaces.git diff --git a/netconn.c b/netconn.c index bcc5f66e..e1da0e3d 100644 --- a/netconn.c +++ b/netconn.c @@ -49,17 +49,20 @@ static cvar_t sv_masters [] = {CF_CLIENT | CF_SERVER, "sv_masterextra3", "dpm.dpmaster.org:27777", "dpm.dpmaster.org - default master server 3 (admin: gazby/soylent_cow)"}, }; +// asgaard.morphos-team.net resolves to the same ipv4 as qwmaster.fodquake.net, +// its reverse PTR is for asgaard.morphos-team.net but qwmaster.fodquake.net seems more popular. +// qwmaster.ocrana.de seems long dead. +// https://www.quakeservers.net/quakeworld/master_servers/ #ifdef CONFIG_MENU static cvar_t sv_qwmasters [] = { - {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"}, - {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"}, - {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"}, - {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"}, - {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"}, - {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"}, - {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"}, - {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"}, + {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"}, + {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"}, + {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"}, + {CF_CLIENT | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"}, + {CF_CLIENT, "sv_qwmasterextra1", "master.quakeservers.net:27000", "QW master in Germany, admin: unknown"}, + {CF_CLIENT, "sv_qwmasterextra2", "qwmaster.fodquake.net:27000", "QW master in Germany, same IP as asgaard.morphos-team.net, admin: bigfoot"}, + {CF_CLIENT, "sv_qwmasterextra3", "master.quakeworld.nu:27000", "QW master in Sweden, admin: unknown"}, }; #endif @@ -77,6 +80,7 @@ cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"}; cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"}; cvar_t net_connecttimeout = {CF_CLIENT | CF_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_connect_entnum_ofs = {CF_SERVER, "net_connect_entnum_ofs", "0", "entity number offset of human clients (for developer testing only)"}; cvar_t net_connectfloodblockingtimeout = {CF_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 = {CF_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 = {CF_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 net_slist_timeout 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."}; @@ -91,7 +95,7 @@ static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "dr #ifdef CONFIG_MENU static cvar_t net_slist_debug = {CF_CLIENT, "net_slist_debug", "0", "enables verbose messages for master server queries"}; static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"}; -static cvar_t net_slist_interval = {CF_CLIENT, "net_slist_interval", "1.125", "minimum number of seconds to wait between getstatus queries to the same DP server, must be >= server's net_getstatusfloodblockingtimeout"}; +static cvar_t net_slist_interval = {CF_CLIENT, "net_slist_interval", "1", "minimum number of seconds to wait between getstatus queries to the same DP server, must be >= server's net_getstatusfloodblockingtimeout"}; static cvar_t net_slist_maxping = {CF_CLIENT | CF_ARCHIVE, "net_slist_maxping", "420", "server query responses are ignored if their ping in milliseconds is higher than this"}; static cvar_t net_slist_maxtries = {CF_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_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list sorting in the menu won't update until it is set back to 0"}; @@ -131,9 +135,9 @@ uint8_t serverlist_querystage = 0; static uint8_t dpmasterstatus[DPMASTER_COUNT] = {0}; static uint8_t qwmasterstatus[QWMASTER_COUNT] = {0}; -#define MASTER_TX_QUERY 1 // we sent the query -#define MASTER_RX_RESPONSE 2 // we got at least 1 packet of the response -#define MASTER_RX_COMPLETE 3 // we saw the EOT marker (assumes dpmaster >= 2.0, see dpmaster/doc/techinfo.txt) +#define MASTER_TX_QUERY 1 ///< we sent the query +#define MASTER_RX_RESPONSE 2 ///< we got at least 1 packet of the response +#define MASTER_RX_COMPLETE 3 ///< we saw the EOT marker (assumes dpmaster >= 2.0, see dpmaster/doc/techinfo.txt) /// the hash password for timestamp verification char serverlist_dpserverquerykey[12]; // challenge_t uses [12] @@ -195,7 +199,7 @@ void NetConn_UpdateFavorites_c(cvar_t *var) // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works // (if v6 address contains port, it must start with '[') { - strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp])); + dp_strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp])); ++nFavorites_idfp; } else @@ -635,11 +639,6 @@ void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool lhnetaddress_t broadcastaddress; char dpquery[53]; // theoretical max: 14+22+16+1 - if (net_slist_debug.integer) - Con_Printf("^2Querying master, favourite and LAN servers, reset=%u\n", resetcache); - serverlist_querystage = (querydp ? SLIST_QUERYSTAGE_DPMASTERS : 0) | (queryqw ? SLIST_QUERYSTAGE_QWMASTERS : 0); - masterquerycount = 0; - masterreplycount = 0; if (resetcache) { serverquerycount = 0; @@ -651,15 +650,25 @@ void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool } else { - // refresh all entries - for (i = 0; i < serverlist_cachecount; ++i) + if (serverlist_querystage) { - serverlist_entry_t *entry = &serverlist_cache[i]; - entry->responded = false; - entry->querycounter = 0; + if (net_slist_debug.integer) + Con_Printf(CON_WARN "Ignoring server list refresh request: already refreshing!\n"); + return; // unsetting `responded` now would cause live servers to be timed out } + + // refresh all entries + for (i = 0; i < serverlist_cachecount; ++i) + serverlist_cache[i].responded = false; } + serverlist_querystage = (querydp ? SLIST_QUERYSTAGE_DPMASTERS : 0) | (queryqw ? SLIST_QUERYSTAGE_QWMASTERS : 0); + masterquerycount = 0; + masterreplycount = 0; serverlist_consoleoutput = consoleoutput; + if (net_slist_debug.integer) + Con_Printf("^2Querying %s master, favourite and LAN servers, reset=%u\n", + querydp && queryqw ? "DP and QW" : querydp ? "DP" : "QW", + resetcache); //_ServerList_Test(); @@ -1354,8 +1363,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s conn->packetsReceived++; reliable_message = (sequence >> 31) != 0; reliable_ack = (sequence_ack >> 31) != 0; - sequence &= ~(1<<31); - sequence_ack &= ~(1<<31); + sequence &= ~(1u<<31); + sequence_ack &= ~(1u<<31); if (sequence <= conn->qw.incoming_sequence) { //Con_DPrint("Got a stale datagram\n"); @@ -1639,7 +1648,7 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_ CL_Disconnect(); // allocate a net connection to keep track of things cls.netcon = NetConn_Open(mysocket, peeraddress); - strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status)); + dp_strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status)); crypto = &cls.netcon->crypto; if(cls.crypto.authenticated) { @@ -1711,10 +1720,10 @@ static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *address if (n == serverlist_cachecount) { if (net_slist_debug.integer) - Con_Printf("^6Received LAN broadcast response from %s\n", addressstring); + Con_Printf("^6Received reply from unlisted %sserver %s\n", challenge ? "DarkPlaces " : "", addressstring); // find a slot - if (serverlist_cachecount == SERVERLIST_TOTALSIZE) + if (serverlist_cachecount >= SERVERLIST_TOTALSIZE) return -1; if (serverlist_maxcachecount <= serverlist_cachecount) @@ -1726,11 +1735,9 @@ static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *address entry = &serverlist_cache[n]; memset(entry, 0, sizeof(*entry)); - strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname)); + entry->info.cname_len = dp_strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname)); - // consider the broadcast to be the first query - // NetConn_QueryQueueFrame() will perform more until net_slist_maxtries is reached - entry->querycounter = 1; + // use the broadcast as the first query, NetConn_QueryQueueFrame() will send more entry->querytime = masterquerytime; // protocol is one of these at all // NetConn_ClientParsePacket_ServerList_PrepareQuery() callsites @@ -1788,14 +1795,14 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n) serverlist_info_t *info = &entry->info; // update description strings for engine menu and console output - dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s", + entry->line1_len = dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), - info->ping ?: INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal + info->ping ? info->ping : INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name); - dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, + entry->line2_len = dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, ( info->gameversion != gameversion.integer && @@ -1825,7 +1832,7 @@ static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, con serverlist_entry_t *entry; // ignore the rest of the message if the serverlist is full - if (serverlist_cachecount == SERVERLIST_TOTALSIZE) + if (serverlist_cachecount >= SERVERLIST_TOTALSIZE) return false; for (n = 0; n < serverlist_cachecount; ++n) @@ -1845,7 +1852,7 @@ static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, con entry = &serverlist_cache[n]; memset(entry, 0, sizeof(*entry)); entry->protocol = protocol; - strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname)); + entry->info.cname_len = dp_strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname)); entry->info.isfavorite = isfavorite; serverlist_cachecount++; @@ -1866,14 +1873,21 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *mas break; if (net_sourceaddresscheck.integer && masternum >= DPMASTER_COUNT) { - Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", isextended ? "extended " : "", masteraddressstring); + Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", + isextended ? "extended " : "", masteraddressstring); return; } masterreplycount++; - dpmasterstatus[masternum] = MASTER_RX_RESPONSE; - if (serverlist_consoleoutput || net_slist_debug.integer) - Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string); + if (dpmasterstatus[masternum] < MASTER_RX_RESPONSE) // Don't reduce status if it's already COMPLETE + dpmasterstatus[masternum] = MASTER_RX_RESPONSE; // which happens when packets are out of order. + + if (dpmasterstatus[masternum] == MASTER_RX_COMPLETE) + Con_Printf(CON_WARN "Received out of order DarkPlaces server list %sfrom %s\n", + isextended ? "(extended) " : "", sv_masters[masternum].string); + else if (serverlist_consoleoutput || net_slist_debug.integer) + Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", + isextended ? "(extended) " : "", sv_masters[masternum].string); while (length >= 7) { @@ -1947,7 +1961,7 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *mas } if (serverlist_querystage & SLIST_QUERYSTAGE_QWMASTERS) - return; // we must wait if we're also querying QW as it has no EOT marker + return; // we must wait if we're (also) querying QW as its protocol has no EOT marker // begin or resume serverlist queries for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum) if (dpmasterstatus[masternum] && dpmasterstatus[masternum] < MASTER_RX_COMPLETE) @@ -2012,7 +2026,6 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat size_t sendlength; #ifdef CONFIG_MENU char infostringvalue[MAX_INPUTLINE]; - const char *s; #endif // quakeworld ingame packet @@ -2092,7 +2105,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat { int k; buf[45] = ' '; - strlcpy(buf + 46, argbuf, sizeof(buf) - 46); + dp_strlcpy(buf + 46, argbuf, sizeof(buf) - 46); NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress); cls.rcon_commands[i][0] = 0; --cls.rcon_trying; @@ -2126,7 +2139,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat return true; } Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2); - strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status)); + dp_strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status)); Protocol_Names(protocolnames, sizeof(protocolnames)); // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command) @@ -2172,44 +2185,34 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat string += 15; // search the cache for this server and update it // the challenge is (ab)used to return the query time - s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)); - n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s); + InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)); + n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, infostringvalue); if (n < 0) return true; info = &serverlist_cache[n].info; - info->game[0] = 0; - info->mod[0] = 0; - info->map[0] = 0; - info->name[0] = 0; - info->qcstatus[0] = 0; - info->players[0] = 0; - info->protocol = -1; - info->numplayers = 0; - info->numbots = -1; - info->maxplayers = 0; - info->gameversion = 0; - p = strchr(string, '\n'); if(p) { *p = 0; // cut off the string there ++p; + info->players_len = dp_strlcpy(info->players, p, sizeof(info->players)); } else + { Con_Printf("statusResponse without players block?\n"); - - if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game)); - if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod )); - if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map )); - if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name)); - if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s); - if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s); - if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s); - if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s); - if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s); - if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus)); - if (p != NULL) strlcpy(info->players, p, sizeof(info->players)); + info->players_len = info->players[0] = 0; + } + info->game_len = InfoString_GetValue(string, "gamename", info->game, sizeof(info->game)); + info->mod_len = InfoString_GetValue(string, "modname", info->mod, sizeof(info->mod)); + info->map_len = InfoString_GetValue(string, "mapname", info->map, sizeof(info->map)); + info->name_len = InfoString_GetValue(string, "hostname", info->name, sizeof(info->name)); + info->qcstatus_len = InfoString_GetValue(string, "qcstatus", info->qcstatus, sizeof(info->qcstatus)); + info->protocol = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1; + info->numplayers = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; + info->numbots = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1; + info->maxplayers = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; + info->gameversion = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; info->numhumans = info->numplayers - max(0, info->numbots); info->freeslots = info->maxplayers - info->numplayers; @@ -2225,34 +2228,23 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat string += 13; // search the cache for this server and update it // the challenge is (ab)used to return the query time - s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)); - n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s); + InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)); + n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, infostringvalue); if (n < 0) return true; info = &serverlist_cache[n].info; - info->game[0] = 0; - info->mod[0] = 0; - info->map[0] = 0; - info->name[0] = 0; - info->qcstatus[0] = 0; - info->players[0] = 0; - info->protocol = -1; - info->numplayers = 0; - info->numbots = -1; - info->maxplayers = 0; - info->gameversion = 0; - - if ((s = InfoString_GetValue(string, "gamename" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game)); - if ((s = InfoString_GetValue(string, "modname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod )); - if ((s = InfoString_GetValue(string, "mapname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map )); - if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name)); - if ((s = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s); - if ((s = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s); - if ((s = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s); - if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s); - if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s); - if ((s = InfoString_GetValue(string, "qcstatus" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus)); + info->players_len = info->players[0] = 0; + info->game_len = InfoString_GetValue(string, "gamename", info->game, sizeof(info->game)); + info->mod_len = InfoString_GetValue(string, "modname", info->mod, sizeof(info->mod)); + info->map_len = InfoString_GetValue(string, "mapname", info->map, sizeof(info->map)); + info->name_len = InfoString_GetValue(string, "hostname", info->name, sizeof(info->name)); + info->qcstatus_len = InfoString_GetValue(string, "qcstatus", info->qcstatus, sizeof(info->qcstatus)); + info->protocol = InfoString_GetValue(string, "protocol" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1; + info->numplayers = InfoString_GetValue(string, "clients" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; + info->numbots = InfoString_GetValue(string, "bots" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : -1; + info->maxplayers = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; + info->gameversion = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; info->numhumans = info->numplayers - max(0, info->numbots); info->freeslots = info->maxplayers - info->numplayers; @@ -2313,7 +2305,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat return true; } Con_DPrintf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2); - strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status)); + dp_strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status)); cls.qw_qport = qport.integer; // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command) @@ -2338,6 +2330,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat #ifdef CONFIG_MENU serverlist_info_t *info; int n; + const char *s; // qw server status if (serverlist_consoleoutput && developer_networking.integer >= 2) @@ -2350,15 +2343,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat return true; info = &serverlist_cache[n].info; - strlcpy(info->game, "QuakeWorld", sizeof(info->game)); - if ((s = InfoString_GetValue(string, "*gamedir" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0] = 0; - if ((s = InfoString_GetValue(string, "map" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0] = 0; - if ((s = InfoString_GetValue(string, "hostname" , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0; + dp_strlcpy(info->game, "QuakeWorld", sizeof(info->game)); + info->mod_len = InfoString_GetValue(string, "*gamedir", info->mod, sizeof(info->mod)); + info->map_len = InfoString_GetValue(string, "map" , info->map, sizeof(info->map)); + info->name_len = InfoString_GetValue(string, "hostname", info->name, sizeof(info->name)); info->protocol = 0; info->numplayers = 0; // updated below info->numhumans = 0; // updated below - if ((s = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers = 0; - if ((s = InfoString_GetValue(string, "gameversion" , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0; + info->maxplayers = InfoString_GetValue(string, "maxclients" , infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; + info->gameversion = InfoString_GetValue(string, "gameversion", infostringvalue, sizeof(infostringvalue)) ? atoi(infostringvalue) : 0; // count active players on server // (we could gather more info, but we're just after the number) @@ -2473,10 +2466,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat break; info = &serverlist_cache[n].info; - strlcpy(info->game, "Quake", sizeof(info->game)); - strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified - strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name)); - strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map)); + info->game_len = dp_strlcpy(info->game, "Quake", sizeof(info->game)); + info->mod_len = dp_strlcpy(info->mod, "", sizeof(info->mod)); // mod name is not specified + info->name_len = dp_strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name)); + info->map_len = dp_strlcpy(info->map, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map)); info->numplayers = MSG_ReadByte(&cl_message); info->maxplayers = MSG_ReadByte(&cl_message); info->protocol = MSG_ReadByte(&cl_message); @@ -2522,11 +2515,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat void NetConn_QueryQueueFrame(void) { unsigned index; - unsigned queries, maxqueries; + unsigned maxqueries; char dpquery[53]; // theoretical max: 14+22+16+1 double currentrealtime; static double querycounter = 0; - qbool pending = false; + static unsigned pass = 0, server = 0; + unsigned queriesperserver = bound(1, net_slist_maxtries.integer, 8); if (!serverlist_querystage) return; @@ -2539,16 +2533,32 @@ void NetConn_QueryQueueFrame(void) // to avoid messing up the ping times on the servers if (serverlist_querystage < SLIST_QUERYSTAGE_SERVERS) { + lhnetaddress_t masteraddress; + if (currentrealtime < masterquerytime + net_slist_timeout.value) return; // Report the masters that timed out or whose response was incomplete. + // Some people have IPv6 DNS but no v6 connectivity + // so v6 master timeouts are currently silent by default. for (index = 0; index < DPMASTER_COUNT; ++index) - if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE) - Con_Printf(CON_WARN "WARNING: dpmaster %s %s\n", sv_masters[index].string, dpmasterstatus[index] == MASTER_TX_QUERY ? "timed out" : "response incomplete"); + { + if (dpmasterstatus[index] == MASTER_TX_QUERY) + { + if (net_slist_debug.integer || + (LHNETADDRESS_FromString(&masteraddress, sv_masters[index].string, DPMASTER_PORT) + && LHNETADDRESS_GetAddressType(&masteraddress) != LHNETADDRESSTYPE_INET6)) + Con_Printf(CON_WARN "WARNING: dpmaster %s query timed out\n", sv_masters[index].string); + } + else if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE) + Con_Printf(CON_WARN "WARNING: dpmaster %s list incomplete (packet loss)\n", sv_masters[index].string); + } + for (index = 0; index < QWMASTER_COUNT; ++index) - if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE) // no EOT marker in QW lists - Con_Printf(CON_WARN "WARNING: qwmaster %s timed out\n", sv_qwmasters[index].string); + if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE // no EOT marker in QW lists + && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[index].string, DPMASTER_PORT) + && LHNETADDRESS_GetAddressType(&masteraddress) != LHNETADDRESSTYPE_INET6) // some people have v6 DNS but no connectivity + Con_Printf(CON_WARN "WARNING: qwmaster %s query timed out\n", sv_qwmasters[index].string); serverlist_querystage = SLIST_QUERYSTAGE_SERVERS; } @@ -2560,25 +2570,21 @@ void NetConn_QueryQueueFrame(void) if (maxqueries == 0) return; - // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime - // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings - ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime); - - for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index) + if (pass < queriesperserver) { - serverlist_entry_t *entry = &serverlist_cache[index]; + // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime + // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings + ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime); - if (entry->querycounter < min(8, (unsigned)net_slist_maxtries.integer)) + for (unsigned queries = 0; server < serverlist_cachecount; ++server) { lhnetaddress_t address; unsigned socket; + serverlist_entry_t *entry = &serverlist_cache[server]; - // only check this when there are tries remaining so we finish querying sooner - if (currentrealtime < entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value) - { - pending = true; - continue; - } + if (queries >= maxqueries + || currentrealtime <= entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value) + return; // continue this pass at the current server on a later frame LHNETADDRESS_FromString(&address, entry->info.cname, 0); if (entry->protocol == PROTOCOL_QUAKEWORLD) @@ -2595,37 +2601,72 @@ void NetConn_QueryQueueFrame(void) } entry->querytime = currentrealtime; - entry->querycounter++; queries++; if (serverlist_consoleoutput) - Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter); + Con_Printf("querying %25s (%i. try)\n", entry->info.cname, pass + 1); } - else // reached net_slist_maxtries - if (!entry->responded // no acceptable response during this refresh cycle - && (entry->info.ping)) // visible in the list (has old ping from previous refresh cycle) + } + else + { + // check timeouts + for (; server < serverlist_cachecount; ++server) { - if (currentrealtime >= entry->querytime + net_slist_maxping.integer/1000) + serverlist_entry_t *entry = &serverlist_cache[server]; + + if (!entry->responded // no acceptable response during this refresh cycle + && entry->info.ping) // visible in the list (has old ping from previous refresh cycle) { - // you have no chance to survive make your timeout - serverreplycount--; - if(!net_slist_pause.integer) - ServerList_ViewList_Remove(entry); - entry->info.ping = 0; // removed later if net_slist_pause + if (currentrealtime > entry->querytime + net_slist_maxping.value/1000.0f) + { + // you have no chance to survive make your timeout + serverreplycount--; + if(!net_slist_pause.integer) + { + if (net_slist_debug.integer) + Con_Printf(CON_WARN "Removing timed out server %s from viewlist\n", entry->info.cname); + ServerList_ViewList_Remove(entry); + } + entry->info.ping = 0; // removed later by ServerList_ViewList_Insert if net_slist_pause + } + else // still has time + return; // continue this pass at the current server on a later frame } - else // still has time - pending = true; } } - // If we got to the end of the list (didn't hit maxqueries) - // and no servers remain to be queried or checked for timeout, - // there's nothing else to do here until the next refresh cycle. - if (index >= serverlist_cachecount && !pending) + // We finished the pass, ie didn't stop at maxqueries + // or a server that can't be (re)queried or timed out yet. + ++pass; + server = 0; + + if (pass == queriesperserver) { + // timeout pass begins next frame if (net_slist_debug.integer) - Con_Printf("^2Finished querying masters and servers in %f\n", currentrealtime - masterquerytime); + { + int dpmastersqueried = 0, qwmastersqueried = 0; + + for (index = 0; index < DPMASTER_COUNT; ++index) + if (dpmasterstatus[index]) + ++dpmastersqueried; + for (index = 0; index < QWMASTER_COUNT; ++index) + if (qwmasterstatus[index]) + ++qwmastersqueried; + Con_Printf("^2Finished querying %i DP %i QW masters and %i servers in %f seconds\n", + dpmastersqueried, qwmastersqueried, serverlist_cachecount, currentrealtime - masterquerytime); + } + } + else if (pass > queriesperserver) + { + // Nothing else to do until the next refresh cycle. serverlist_querystage = 0; + pass = 0; + if (net_slist_debug.integer && serverlist_cachecount) + Con_Printf("^4Finished checking server timeouts in %f\n", + currentrealtime - serverlist_cache[serverlist_cachecount - 1].querytime); + if (serverlist_cachecount >= SERVERLIST_TOTALSIZE) + Con_Printf(CON_ERROR "ERROR: too many servers, some will not be listed!\n"); } } #endif @@ -2652,7 +2693,7 @@ void NetConn_ClientFrame(void) cls.connect_trying = false; LHNETADDRESS_ToString(&cls.connect_address, address, sizeof(address), true); - strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status)); + dp_strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status)); Con_Printf(CON_ERROR "%s from %s\n", cl_connect_status, address); return; } @@ -2829,17 +2870,17 @@ static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, s if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0)) { if(client->frags == -666) // spectator - strlcpy(teambuf, " 0", sizeof(teambuf)); + dp_strlcpy(teambuf, " 0", sizeof(teambuf)); else if(client->colors == 0x44) // red team - strlcpy(teambuf, " 1", sizeof(teambuf)); + dp_strlcpy(teambuf, " 1", sizeof(teambuf)); else if(client->colors == 0xDD) // blue team - strlcpy(teambuf, " 2", sizeof(teambuf)); + dp_strlcpy(teambuf, " 2", sizeof(teambuf)); else if(client->colors == 0xCC) // yellow team - strlcpy(teambuf, " 3", sizeof(teambuf)); + dp_strlcpy(teambuf, " 3", sizeof(teambuf)); else if(client->colors == 0x99) // pink team - strlcpy(teambuf, " 4", sizeof(teambuf)); + dp_strlcpy(teambuf, " 4", sizeof(teambuf)); else - strlcpy(teambuf, " 0", sizeof(teambuf)); + dp_strlcpy(teambuf, " 0", sizeof(teambuf)); } else *teambuf = 0; @@ -3023,7 +3064,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa while((userpass_end = strchr(userpass_start, ' '))) { have_usernames = true; - strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start); if(buf[0]) // Ignore empty entries due to leading/duplicate space. if(comparator(peeraddress, buf, password, cs, cslen)) goto allow; @@ -3042,7 +3083,7 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa while((userpass_end = strchr(userpass_start, ' '))) { have_usernames = true; - strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + dp_ustr2stp(buf, sizeof(buf), userpass_start, userpass_end - userpass_start); if(buf[0]) // Ignore empty entries due to leading/duplicate space. if(comparator(peeraddress, buf, password, cs, cslen)) goto check; @@ -3132,7 +3173,7 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c if(l) { client_t *host_client_save = host_client; - Cmd_ExecuteString(cmd_local, s, src_local, true); + Cmd_PreprocessAndExecuteString(cmd_local, s, l, src_local, true); host_client = host_client_save; // in case it is a command that changes host_client (like restart) } @@ -3142,7 +3183,10 @@ static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, c } else { - Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2); + if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP) + Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol); + Con_Printf(CON_ERROR "server denied rcon access to %s\n", host_client ? host_client->name : addressstring2); + Con_Rcon_Redirect_End(); } } @@ -3251,7 +3295,6 @@ 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; @@ -3276,12 +3319,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } else { - if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue)))) + if (InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))) { // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) if(challenges[i].time > 0) - if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s)) + if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, infostringvalue)) break; // if the challenge is not recognized, drop the packet if (i == MAX_CHALLENGES) @@ -3289,8 +3332,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } } - if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue)))) - Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s); + if(InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))) + Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, infostringvalue); if(!(islocal || sv_public.integer > -2)) { @@ -3303,7 +3346,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } // check engine protocol - if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3")) + if(!InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue)) || strcmp(infostringvalue, "darkplaces 3")) { if (developer_extra.integer) Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2); @@ -3380,10 +3423,14 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat return true; // find an empty client slot for this new client - for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++) + for (clientnum = 0; clientnum < svs.maxclients; clientnum++) { netconn_t *conn; - if (!client->active && (conn = NetConn_Open(mysocket, peeraddress))) + int offset_clientnum = (net_connect_entnum_ofs.integer > 0) + ? (clientnum + net_connect_entnum_ofs.integer) % svs.maxclients + : clientnum; + + if (!svs.clients[offset_clientnum].active && (conn = NetConn_Open(mysocket, peeraddress))) { // allocated connection if (developer_extra.integer) @@ -3392,7 +3439,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat // now set up the client if(crypto && crypto->authenticated) Crypto_FinishInstance(&conn->crypto, crypto); - SV_ConnectClient(clientnum, conn); + SV_ConnectClient(offset_clientnum, conn); NetConn_Heartbeat(1); return true; } @@ -3635,7 +3682,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat { // connect to the client // everything is allocated, just fill in the details - strlcpy (conn->address, addressstring2, sizeof (conn->address)); + dp_strlcpy (conn->address, addressstring2, sizeof (conn->address)); if (developer_extra.integer) Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2); // send back the info about the server connection @@ -3688,7 +3735,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true); MSG_WriteString(&sv_message, myaddressstring); MSG_WriteString(&sv_message, hostname.string); - MSG_WriteString(&sv_message, sv.name); + MSG_WriteString(&sv_message, sv.worldbasename); // How many clients are there? for (i = 0, numclients = 0;i < svs.maxclients;i++) if (svs.clients[i].active) @@ -3783,8 +3830,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat char *s; char *endpos; const char *userlevel; - strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password)); - strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd)); + dp_strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password)); + dp_strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd)); s = cmd; endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0); @@ -3835,8 +3882,8 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw) if (serverlist_cachecount >= SERVERLIST_TOTALSIZE) return; - memset(dpmasterstatus, 0, sizeof(*dpmasterstatus)); - memset(qwmasterstatus, 0, sizeof(*qwmasterstatus)); + memset(dpmasterstatus, 0, sizeof(dpmasterstatus)); + memset(qwmasterstatus, 0, sizeof(qwmasterstatus)); if (querydp) { @@ -3858,8 +3905,8 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw) cmdname = "getservers"; 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); + dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", + cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions); // search internet for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum) @@ -3929,7 +3976,7 @@ void NetConn_QueryMasters(qbool querydp, qbool queryqw) if (!masterquerycount) { Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n"); - strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status)); + dp_strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status)); } } #endif @@ -4047,7 +4094,9 @@ void Net_SlistQW_f(cmd_state_t *cmd) void NetConn_Init(void) { int i; + unsigned j; lhnetaddress_t tempaddress; + netconn_mempool = Mem_AllocPool("network connections", 0, NULL); Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics"); #ifdef CONFIG_MENU @@ -4082,6 +4131,7 @@ void NetConn_Init(void) #endif Cvar_RegisterVariable(&net_messagetimeout); Cvar_RegisterVariable(&net_connecttimeout); + Cvar_RegisterVariable(&net_connect_entnum_ofs); Cvar_RegisterVariable(&net_connectfloodblockingtimeout); Cvar_RegisterVariable(&net_challengefloodblockingtimeout); Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout); @@ -4103,8 +4153,12 @@ void NetConn_Init(void) Cvar_RegisterVariable(&sv_public); Cvar_RegisterVariable(&sv_public_rejectreason); Cvar_RegisterVariable(&sv_heartbeatperiod); - for (uint8_t masternum = 0; masternum < DPMASTER_COUNT; ++masternum) - Cvar_RegisterVariable(&sv_masters[masternum]); + for (j = 0; j < DPMASTER_COUNT; ++j) + Cvar_RegisterVariable(&sv_masters[j]); +#ifdef CONFIG_MENU + for (j = 0; j < QWMASTER_COUNT; ++j) + Cvar_RegisterVariable(&sv_qwmasters[j]); +#endif Cvar_RegisterVariable(&gameversion); Cvar_RegisterVariable(&gameversion_min); Cvar_RegisterVariable(&gameversion_max);