case SLIF_NUMHUMANS:
result = A->info.numhumans - B->info.numhumans;
break;
+ case SLIF_FREESLOTS:
+ result = A->info.freeslots - B->info.freeslots;
+ break;
case SLIF_PROTOCOL:
result = A->info.protocol - B->info.protocol;
break;
return false;
if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
return false;
+ if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
+ return false;
if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
return false;
if( *mask->info.cname
int i;
serverlist_viewcount = 0;
- for( i = 0 ; i < serverlist_cachecount ; i++ )
- if( serverlist_cache[i].query == SQS_QUERIED )
- ServerList_ViewList_Insert( &serverlist_cache[i] );
+ for( i = 0 ; i < serverlist_cachecount ; i++ ) {
+ serverlist_entry_t *entry = &serverlist_cache[i];
+ // also display entries that are currently being refreshed [11/8/2007 Black]
+ if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
+ ServerList_ViewList_Insert( entry );
+ }
}
void ServerList_ResetMasks(void)
}
#endif
-void ServerList_QueryList(qboolean querydp, qboolean queryqw)
+void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
{
masterquerytime = realtime;
masterquerycount = 0;
masterreplycount = 0;
- serverquerycount = 0;
- serverreplycount = 0;
- serverlist_cachecount = 0;
- serverlist_viewcount = 0;
- serverlist_consoleoutput = false;
+ if( resetcache ) {
+ serverquerycount = 0;
+ serverreplycount = 0;
+ serverlist_cachecount = 0;
+ serverlist_viewcount = 0;
+ } else {
+ // refresh all entries
+ int n;
+ for( n = 0 ; n < serverlist_cachecount ; n++ ) {
+ serverlist_entry_t *entry = &serverlist_cache[ n ];
+ entry->query = SQS_REFRESHING;
+ entry->querycounter = 0;
+ }
+ }
+ serverlist_consoleoutput = consoleoutput;
//_ServerList_Test();
}
}
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate)
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
{
int totallen = 0;
}
// if we have a new reliable message to send, do so
- if (!conn->sendMessageLength && conn->message.cursize)
+ if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
{
if (conn->message.cursize > (int)sizeof(conn->sendMessage))
{
}
// if we have an unreliable message to send, do so
- //if (data->cursize)
+ if (data->cursize)
{
packetLen = NET_HEADERSIZE + data->cursize;
return 0;
}
+qboolean NetConn_HaveClientPorts(void)
+{
+ return !!cl_numsockets;
+}
+
+qboolean NetConn_HaveServerPorts(void)
+{
+ return !!sv_numsockets;
+}
+
void NetConn_CloseClientPorts(void)
{
for (;cl_numsockets > 0;cl_numsockets--)
unsigned int *header;
conn->sendMessageLength -= MAX_PACKETFRAGMENT;
- memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
+ memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
{
msg.data = buf;
msg.maxsize = sizeof(buf);
MSG_WriteChar(&msg, clc_nop);
- NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000);
+ NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
}
}
{
int n;
int pingtime;
+ serverlist_entry_t *entry = NULL;
+
// search the cache for this server and update it
- for (n = 0;n < serverlist_cachecount;n++)
- if (!strcmp(addressstring, serverlist_cache[n].info.cname))
+ for (n = 0;n < serverlist_cachecount;n++) {
+ entry = &serverlist_cache[ n ];
+ if (!strcmp(addressstring, entry->info.cname))
break;
+ }
+
if (n == serverlist_cachecount)
{
// LAN search doesnt require an answer from the master server so we wont
if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
return -1;
- memset(&serverlist_cache[serverlist_cachecount], 0, sizeof(serverlist_cache[serverlist_cachecount]));
+ entry = &serverlist_cache[n];
+
+ memset(entry, 0, sizeof(*entry));
// store the data the engine cares about (address and ping)
- strlcpy(serverlist_cache[serverlist_cachecount].info.cname, addressstring, sizeof(serverlist_cache[serverlist_cachecount].info.cname));
- serverlist_cache[serverlist_cachecount].info.ping = 100000;
- serverlist_cache[serverlist_cachecount].querytime = realtime;
+ strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
+ entry->info.ping = 100000;
+ entry->querytime = realtime;
// if not in the slist menu we should print the server to console
if (serverlist_consoleoutput)
Con_Printf("querying %s\n", addressstring);
++serverlist_cachecount;
}
// if this is the first reply from this server, count it as having replied
- if (serverlist_cache[n].info.ping == 100000)
- serverreplycount++;
- pingtime = (int)((realtime - serverlist_cache[n].querytime) * 1000.0 + 0.5);
+ pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
pingtime = bound(0, pingtime, 9999);
- // update the ping
- serverlist_cache[n].info.ping = min(serverlist_cache[n].info.ping, pingtime);
+ if (entry->query == SQS_REFRESHING) {
+ entry->info.ping = pingtime;
+ entry->query = SQS_QUERIED;
+ } else {
+ // convert to unsigned to catch the -1
+ // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
+ entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
+ serverreplycount++;
+ }
+
// other server info is updated by the caller
return n;
}
static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
{
- serverlist_info_t *info = &serverlist_cache[n].info;
+ serverlist_entry_t *entry = &serverlist_cache[n];
+ serverlist_info_t *info = &entry->info;
// update description strings for engine menu and console output
- dpsnprintf(serverlist_cache[n].line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
- dpsnprintf(serverlist_cache[n].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) ? '1' : '4', info->mod, info->map);
- if (serverlist_cache[n].query == SQS_QUERIED)
- ServerList_ViewList_Remove(&serverlist_cache[n]);
+ dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((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, (info->gameversion != gameversion.integer) ? '1' : '4', info->mod, info->map);
+ if (entry->query == SQS_QUERIED)
+ ServerList_ViewList_Remove(entry);
// if not in the slist menu we should print the server to console (if wanted)
else if( serverlist_consoleoutput )
Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
// and finally, update the view set
- ServerList_ViewList_Insert( &serverlist_cache[n] );
+ ServerList_ViewList_Insert( entry );
+ // update the entry's state
serverlist_cache[n].query = SQS_QUERIED;
}
+// returns true, if it's sensible to continue the processing
+static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring ) {
+ int n;
+ serverlist_entry_t *entry;
+
+ // ignore the rest of the message if the serverlist is full
+ if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
+ return false;
+ // also ignore it if we have already queried it (other master server response)
+ for( n = 0 ; n < serverlist_cachecount ; n++ )
+ if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
+ break;
+
+ entry = &serverlist_cache[n];
+
+ if( n < serverlist_cachecount ) {
+ // the entry has already been queried once or
+ return true;
+ }
+
+ memset(entry, 0, sizeof(entry));
+ entry->protocol = protocol;
+ // store the data the engine cares about (address and ping)
+ strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
+
+ // no, then reset the ping right away
+ entry->info.ping = -1;
+ // we also want to increase the serverlist_cachecount then
+ serverlist_cachecount++;
+ serverquerycount++;
+
+ entry->query = SQS_QUERYING;
+
+ return true;
+}
+
static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
{
qboolean fromserver;
if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
if ((s = SearchInfostring(string, "gameversion" )) != NULL) info->gameversion = atoi(s);
info->numhumans = info->numplayers - max(0, info->numbots);
+ info->freeslots = info->maxplayers - info->numplayers;
NetConn_ClientParsePacket_ServerList_UpdateCache(n);
Con_Print("received DarkPlaces server list...\n");
while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
{
- int n;
-
dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], data[5] * 256 + data[6]);
if (serverlist_consoleoutput && developer_networking.integer)
Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
- // ignore the rest of the message if the serverlist is full
- if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
+
+ if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring ) ) {
break;
- // also ignore it if we have already queried it (other master server response)
- for( n = 0 ; n < serverlist_cachecount ; n++ )
- if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
- break;
- if( n >= serverlist_cachecount )
- {
- serverquerycount++;
-
- memset(&serverlist_cache[serverlist_cachecount], 0, sizeof(serverlist_cache[serverlist_cachecount]));
- serverlist_cache[serverlist_cachecount].protocol = PROTOCOL_DARKPLACES7;
- // store the data the engine cares about (address and ping)
- strlcpy (serverlist_cache[serverlist_cachecount].info.cname, ipstring, sizeof (serverlist_cache[serverlist_cachecount].info.cname));
- serverlist_cache[serverlist_cachecount].info.ping = 100000;
- serverlist_cache[serverlist_cachecount].query = SQS_QUERYING;
-
- ++serverlist_cachecount;
}
// move on to next address in packet
Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
{
- int n;
-
dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
if (serverlist_consoleoutput && developer_networking.integer)
Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
- // ignore the rest of the message if the serverlist is full
- if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
+
+ if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring ) ) {
break;
- // also ignore it if we have already queried it (other master server response)
- for( n = 0 ; n < serverlist_cachecount ; n++ )
- if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
- break;
- if( n >= serverlist_cachecount )
- {
- serverquerycount++;
-
- memset(&serverlist_cache[serverlist_cachecount], 0, sizeof(serverlist_cache[serverlist_cachecount]));
- serverlist_cache[serverlist_cachecount].protocol = PROTOCOL_QUAKEWORLD;
- // store the data the engine cares about (address and ping)
- strlcpy (serverlist_cache[serverlist_cachecount].info.cname, ipstring, sizeof (serverlist_cache[serverlist_cachecount].info.cname));
- serverlist_cache[serverlist_cachecount].info.ping = 100000;
- serverlist_cache[serverlist_cachecount].query = SQS_QUERYING;
-
- ++serverlist_cachecount;
}
// move on to next address in packet
return;
}
- // scan serverlist and issue queries as needed
- serverlist_querysleep = true;
+ // scan serverlist and issue queries as needed
+ serverlist_querysleep = true;
- timeouttime = realtime - net_slist_timeout.value;
- for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
+ timeouttime = realtime - net_slist_timeout.value;
+ for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
{
serverlist_entry_t *entry = &serverlist_cache[ index ];
- if( entry->query != SQS_QUERYING )
+ if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
{
continue;
}
- serverlist_querysleep = false;
- if( entry->querycounter != 0 && entry->querytime > timeouttime )
+ serverlist_querysleep = false;
+ if( entry->querycounter != 0 && entry->querytime > timeouttime )
{
continue;
}
- if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
+ if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
{
- lhnetaddress_t address;
+ lhnetaddress_t address;
int socket;
LHNETADDRESS_FromString(&address, entry->info.cname, 0);
- if (entry->protocol == PROTOCOL_QUAKEWORLD)
+ if (entry->protocol == PROTOCOL_QUAKEWORLD)
{
- for (socket = 0; socket < cl_numsockets ; socket++)
+ for (socket = 0; socket < cl_numsockets ; socket++)
NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
}
else
{
- for (socket = 0; socket < cl_numsockets ; socket++)
+ for (socket = 0; socket < cl_numsockets ; socket++)
NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getinfo", &address);
}
+ // update the entry fields
entry->querytime = realtime;
entry->querycounter++;
}
else
{
+ // have we tried to refresh this server?
+ if( entry->query == SQS_REFRESHING ) {
+ // yes, so update the reply count (since its not responding anymore)
+ serverreplycount--;
+ ServerList_ViewList_Remove(entry);
+ }
entry->query = SQS_TIMEDOUT;
}
}
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)
+ if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
{
Con_Print("Connection timed out\n");
CL_Disconnect();
{
int i;
char *s = string + 5;
+ char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
char password[64];
for (i = 0;*s > ' ';s++)
if (i < (int)sizeof(password) - 1)
password[i++] = *s;
+ if(*s <= ' ' && s != endpos) // skip leading ugly space
+ ++s;
password[i] = 0;
if (password[0] > ' ' && !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);
+ char *s_ptr = s;
+ Con_Printf("server received rcon command from %s:\n", host_client ? host_client->name : addressstring2);
+ while(s_ptr != endpos)
+ {
+ size_t l = strlen(s_ptr);
+ if(l)
+ Con_Printf(" %s;", s_ptr);
+ s_ptr += l + 1;
+ }
+ Con_Printf("\n");
rcon_redirect = true;
rcon_redirect_bufferpos = 0;
- Cmd_ExecuteString(s, src_command);
+ while(s != endpos)
+ {
+ size_t l = strlen(s);
+ if(l)
+ Cmd_ExecuteString(s, src_command);
+ s += l + 1;
+ }
rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
rcon_redirect = false;
// print resulting text to client
// protocol
// (this protects more modern protocols against being used for
// Quake packet flood Denial Of Service attacks)
- if (length >= 5 && (i = BigLong(*((int *)data))) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
+ if (length >= 5 && (i = BigLong(*((int *)data))) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
{
int c;
int protocolnumber;
PrintStats(conn);
}
+void Net_Refresh_f(void)
+{
+ if (m_state != m_slist) {
+ Con_Print("Sending new requests to master servers\n");
+ ServerList_QueryList(false, true, false, true);
+ Con_Print("Listening for replies...\n");
+ } else
+ ServerList_QueryList(false, true, false, false);
+}
+
void Net_Slist_f(void)
{
ServerList_ResetMasks();
serverlist_sortdescending = false;
if (m_state != m_slist) {
Con_Print("Sending requests to master servers\n");
- ServerList_QueryList(true, false);
- serverlist_consoleoutput = true;
+ ServerList_QueryList(true, true, false, true);
Con_Print("Listening for replies...\n");
} else
- ServerList_QueryList(true, false);
+ ServerList_QueryList(true, true, false, false);
}
void Net_SlistQW_f(void)
serverlist_sortdescending = false;
if (m_state != m_slist) {
Con_Print("Sending requests to master servers\n");
- ServerList_QueryList(false, true);
+ ServerList_QueryList(true, false, true, true);
serverlist_consoleoutput = true;
Con_Print("Listening for replies...\n");
} else
- ServerList_QueryList(false, true);
+ ServerList_QueryList(true, false, true, false);
}
void NetConn_Init(void)
Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
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("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);