#define MASTER_PORT 27950
-cvar_t sv_public = {0, "sv_public", "1"};
+// 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"};
// 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 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"};
/* statistic counters */
static int packetsSent = 0;
static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
{
+ int i;
+ char bufferA[ 256 ], bufferB[ 256 ]; // should be more than enough
+ for (i = 0;i < sizeof(bufferA)-1 && A[i];i++)
+ bufferA[i] = (A[i] >= 'A' && A[i] <= 'Z') ? (A[i] + 'a' - 'A') : A[i];
+ bufferA[i] = 0;
+ for (i = 0;i < sizeof(bufferB)-1 && B[i];i++)
+ bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
+ bufferB[i] = 0;
+
// Same here, also using an intermediate & final return would be more appropriate
// A info B mask
switch( op ) {
case SLMO_CONTAINS:
- return *B && !!strstr( A, B ); // we want a real bool
+ return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
case SLMO_NOTCONTAIN:
- return !*B || !strstr( A, B );
+ return !*bufferB || !strstr( bufferA, bufferB );
case SLMO_LESS:
- return strcmp( A, B ) < 0;
+ return strcmp( bufferA, bufferB ) < 0;
case SLMO_LESSEQUAL:
- return strcmp( A, B ) <= 0;
+ return strcmp( bufferA, bufferB ) <= 0;
case SLMO_EQUAL:
- return strcmp( A, B ) == 0;
+ return strcmp( bufferA, bufferB ) == 0;
case SLMO_GREATER:
- return strcmp( A, B ) > 0;
+ return strcmp( bufferA, bufferB ) > 0;
case SLMO_NOTEQUAL:
- return strcmp( A, B ) != 0;
+ return strcmp( bufferA, bufferB ) != 0;
case SLMO_GREATEREQUAL:
- return strcmp( A, B ) >= 0;
+ return strcmp( bufferA, bufferB ) >= 0;
default:
Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
return false;
//#ifdef DEBUG
if (data->cursize == 0)
- Sys_Error("Datagram_SendMessage: zero length message\n");
+ {
+ Con_Printf ("Datagram_SendMessage: zero length message\n");
+ return -1;
+ }
if (data->cursize > (int)sizeof(conn->sendMessage))
- Sys_Error("Datagram_SendMessage: message too big (%u > %u)\n", data->cursize, sizeof(conn->sendMessage));
+ {
+ Con_Printf ("Datagram_SendMessage: message too big (%u > %u)\n", data->cursize, sizeof(conn->sendMessage));
+ return -1;
+ }
if (conn->canSend == false)
- Sys_Error("SendMessage: called with canSend == false\n");
+ {
+ Con_Printf ("SendMessage: called with canSend == false\n");
+ return -1;
+ }
//#endif
memcpy(conn->sendMessage, data->data, data->cursize);
//#ifdef DEBUG
if (data->cursize == 0)
- Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
+ {
+ Con_Printf ("Datagram_SendUnreliableMessage: zero length message\n");
+ return -1;
+ }
if (packetLen > (int)sizeof(sendbuffer))
- Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
+ {
+ Con_Printf ("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
+ return -1;
+ }
//#endif
header = (void *)sendbuffer;
{
lhnetaddress_t address;
lhnetsocket_t *s;
+ int port;
char addressstring2[1024];
- if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
+
+ for (port = defaultport; port <= defaultport + 100; port++)
{
- if ((s = LHNET_OpenSocket_Connectionless(&address)))
+ if (LHNETADDRESS_FromString(&address, addressstring, port))
{
- sv_sockets[sv_numsockets++] = s;
- LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
- Con_Printf("Server listening on address %s\n", addressstring2);
+ if ((s = LHNET_OpenSocket_Connectionless(&address)))
+ {
+ sv_sockets[sv_numsockets++] = s;
+ LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Server listening on address %s\n", addressstring2);
+ break;
+ }
+ else
+ {
+ LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Server failed to open socket on address %s\n", addressstring2);
+ }
}
- else
+ else
{
- LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
- Con_Printf("Server failed to open socket on address %s\n", addressstring2);
+ Con_Printf("Server unable to parse address %s\n", addressstring);
+ // if it cant parse one address, it wont be able to parse another for sure
+ break;
}
}
- else
- Con_Printf("Server unable to parse address %s\n", addressstring);
}
void NetConn_OpenServerPorts(int opennetports)
// 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_PENDING;
+ serverlist_cache[serverlist_cachecount].query = SQS_QUERYING;
++serverlist_cachecount;
}
int index;
int queries;
int maxqueries;
+ double timeouttime;
static double querycounter = 0;
if (serverlist_querysleep)
maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
querycounter -= maxqueries;
+ if( maxqueries == 0 ) {
+ return;
+ }
+
// scan serverlist and issue queries as needed
- // (note: this aborts immediately if maxqueries is 0)
+ serverlist_querysleep = true;
+
+ timeouttime = realtime - net_slist_timeout.value;
for( index = 0, queries = 0 ; index < serverlist_cachecount && queries < maxqueries ; index++ )
{
- if( serverlist_cache[ index ].query == SQS_PENDING )
+ serverlist_entry_t *entry = &serverlist_cache[ index ];
+ if( entry->query != SQS_QUERYING )
+ {
+ continue;
+ }
+
+ serverlist_querysleep = false;
+ if( entry->querycounter != 0 && entry->querytime > timeouttime )
+ {
+ continue;
+ }
+
+ if( entry->querycounter != (unsigned) net_slist_maxtries.integer )
{
lhnetaddress_t address;
int socket;
- LHNETADDRESS_FromString(&address, serverlist_cache[ index ].info.cname, 0);
+ LHNETADDRESS_FromString(&address, entry->info.cname, 0);
for (socket = 0; socket < cl_numsockets ; socket++) {
NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getinfo", &address);
}
- serverlist_cache[ index ].querytime = realtime;
- serverlist_cache[ index ].query = SQS_QUERYING;
+ entry->querytime = realtime;
+ entry->querycounter++;
// if not in the slist menu we should print the server to console
if (serverlist_consoleoutput)
- Con_Printf("querying %s\n", serverlist_cache[ index ].info.cname);
-
+ Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
queries++;
}
+ else
+ {
+ entry->query = SQS_TIMEDOUT;
+ }
}
-
- // if we didn't find any to query, go back to sleep
- if (index == serverlist_cachecount)
- serverlist_querysleep = true;
}
void NetConn_ClientFrame(void)
buffer[i] = 0;
}
+static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
+{
+ unsigned int nb_clients = 0, i;
+ int length;
+
+ // How many clients are there?
+ for (i = 0;i < (unsigned int)svs.maxclients;i++)
+ if (svs.clients[i].active)
+ nb_clients++;
+
+ // TODO: we should add more information for the full status string
+ length = dpsnprintf(out_msg, out_size,
+ "\377\377\377\377%s\x0A"
+ "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
+ "\\clients\\%d\\mapname\\%s\\hostname\\%s""\\protocol\\%d"
+ "%s%s"
+ "%s",
+ fullstatus ? "statusResponse" : "infoResponse",
+ gamename, com_modname, svs.maxclients,
+ nb_clients, sv.name, hostname.string, NET_PROTOCOL_VERSION,
+ challenge ? "\\challenge\\" : "", challenge ? challenge : "",
+ fullstatus ? "\n" : "");
+
+ // Make sure it fits in the buffer
+ if (length < 0)
+ return false;
+
+ if (fullstatus)
+ {
+ char *ptr;
+ int left;
+
+ ptr = out_msg + length;
+ left = out_size - length;
+
+ for (i = 0;i < (unsigned int)svs.maxclients;i++)
+ {
+ client_t *cl = &svs.clients[i];
+ if (cl->active)
+ {
+ int nameind, cleanind;
+ char curchar;
+ char cleanname [sizeof(cl->name)];
+
+ // Remove all characters '"' and '\' in the player name
+ nameind = 0;
+ cleanind = 0;
+ do
+ {
+ curchar = cl->name[nameind++];
+ if (curchar != '"' && curchar != '\\')
+ {
+ cleanname[cleanind++] = curchar;
+ if (cleanind == sizeof(cleanname) - 1)
+ break;
+ }
+ } while (curchar != '\0');
+
+ length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
+ cl->frags,
+ (int)(cl->ping * 1000.0f),
+ cleanname);
+ if(length < 0)
+ return false;
+ left -= length;
+ ptr += length;
+ }
+ }
+ }
+
+ return true;
+}
+
extern void SV_SendServerinfo (client_t *client);
int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
{
- int i, n, ret, clientnum, responselength, best;
+ int i, ret, clientnum, best;
double besttime;
client_t *client;
netconn_t *conn;
if (length >= 7 && !memcmp(string, "getinfo", 7))
{
const char *challenge = NULL;
+
// If there was a challenge in the getinfo message
if (length > 8 && string[7] == ' ')
challenge = string + 8;
- for (i = 0, n = 0;i < svs.maxclients;i++)
- if (svs.clients[i].active)
- n++;
- responselength = dpsnprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
- "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
- "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
- gamename, com_modname, svs.maxclients, n,
- sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
- // does it fit in the buffer?
- if (responselength >= 0)
+
+ if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
{
if (developer.integer)
Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
}
return true;
}
+ if (length >= 9 && !memcmp(string, "getstatus", 9))
+ {
+ const char *challenge = NULL;
+
+ // If there was a challenge in the getinfo message
+ if (length > 10 && string[9] == ' ')
+ challenge = string + 10;
+
+ if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
+ {
+ if (developer.integer)
+ Con_Printf("Sending reply to client %s - %s\n", addressstring2, response);
+ NetConn_WriteString(mysocket, response, peeraddress);
+ }
+ return true;
+ }
/*
if (!strncmp(string, "ping", 4))
{
Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
Cvar_RegisterVariable(&net_slist_queriespersecond);
Cvar_RegisterVariable(&net_slist_queriesperframe);
+ Cvar_RegisterVariable(&net_slist_timeout);
+ Cvar_RegisterVariable(&net_slist_maxtries);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&net_messagerejointimeout);
Cvar_RegisterVariable(&net_connecttimeout);