2013-03-31 04:21:08 +00:00
# include "quakedef.h"
2004-08-23 00:15:46 +00:00
# ifdef SV_MASTER
# ifdef _WIN32
# include "winquake.h"
# ifdef _MSC_VER
# include "wsipx.h"
# endif
# endif
2005-11-29 13:22:58 +00:00
# include "netinc.h"
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
//quakeworld protocol
// heartbeat: "a"
// query: "c\n%i<sequence>\n%i<numplayers>\n"
//queryresponse: "d\naaaappaaaapp"
2004-08-23 00:15:46 +00:00
//quake2 protocol
2018-12-28 00:04:36 +00:00
// heartbeat: "heartbeat\n%s<serverinfo>\n%i<frags> %i<ping> \"%s\"<name>\n<repeat>"
// query: "query\0"
//queryresponse: "servers\naaaappaaaapp"
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
//quake3/dpmaster protocol
// heartbeat: "heartbeat DarkPlaces\n"
// query: "getservers[Ext<ipv6>] [%s<game>] %u<version> [empty] [full] [ipv6]"
//queryresponse: "getservers[Ext]Response\\aaaapp/aaaaaaaaaaaapp\\EOF"
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
enum gametypes_e
{
GT_FFA = 0 ,
GT_TOURNEY = 1 ,
GT_TEAM = 3 ,
GT_CTF = 4 ,
} ;
2004-08-23 00:15:46 +00:00
typedef struct svm_server_s {
netadr_t adr ;
2018-12-28 00:04:36 +00:00
int protover ;
unsigned int clients ;
unsigned int maxclients ;
char hostname [ 48 ] ; //just for our own listings.
char mapname [ 16 ] ; //just for our own listings.
char gamedir [ 16 ] ; //again...
unsigned short gametype ;
2004-08-23 00:15:46 +00:00
float expiretime ;
2018-12-28 00:04:36 +00:00
bucket_t bucket ;
struct svm_game_s * game ;
2004-08-23 00:15:46 +00:00
struct svm_server_s * next ;
} svm_server_t ;
2018-12-28 00:04:36 +00:00
typedef struct svm_game_s {
struct svm_game_s * next ;
svm_server_t * firstserver ;
size_t numservers ;
char name [ 1 ] ;
} svm_game_t ;
2004-08-23 00:15:46 +00:00
typedef struct {
float time ;
2018-12-28 00:04:36 +00:00
svm_game_t * firstgame ;
size_t numgames ;
hashtable_t serverhash ;
size_t numservers ;
struct rates_s
{
double timestamp ;
size_t heartbeats ;
size_t queries ;
size_t junk ;
size_t drops ;
size_t adds ;
} total , stamps [ 60 ] ;
size_t stampring ;
double nextstamp ;
2004-08-23 00:15:46 +00:00
} masterserver_t ;
2018-12-28 00:04:36 +00:00
static masterserver_t svm ;
ftenet_connections_t * svm_sockets ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
static void QDECL SVM_Tcpport_Callback ( struct cvar_s * var , char * oldvalue )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
FTENET_AddToCollection ( svm_sockets , var - > name , var - > string , NA_IP , NP_STREAM ) ;
}
static void QDECL SVM_Port_Callback ( struct cvar_s * var , char * oldvalue )
{
FTENET_AddToCollection ( svm_sockets , var - > name , var - > string , NA_IP , NP_DGRAM ) ;
}
static cvar_t sv_heartbeattimeout = CVARD ( " sv_heartbeattimeout " , " 600 " , " How many seconds a server should remain listed after its latest heartbeat. Larger values can avoid issues from packetloss, but can also make dos attacks easier. " ) ;
static cvar_t sv_masterport = CVARC ( " sv_masterport " , " 27000 28000 " , SVM_Port_Callback ) ;
static cvar_t sv_masterport_tcp = CVARC ( " sv_masterport_tcp " , " 27000 28000 " , SVM_Tcpport_Callback ) ;
static cvar_t sv_maxgames = CVARD ( " sv_maxgames " , " 100 " , " Limits the number of games that may be known. This is to reduce denial of service attacks. " ) ;
static cvar_t sv_maxservers = CVARD ( " sv_maxservers " , " 1000 " , " Limits the number of servers (total from all games) that may be known. This is to reduce denial of service attacks. " ) ;
//returns a hash key for a server's address.
static unsigned int SVM_GenerateAddressKey ( const netadr_t * adr )
{
unsigned int key ;
switch ( adr - > type )
{
case NA_IP :
key = * ( const unsigned int * ) adr - > address . ip ;
key ^ = 0xffff0000 ; //match ipv6's ipv4-mapped addresses.
key ^ = adr - > port ;
break ;
case NA_IPV6 :
key = * ( const unsigned int * ) ( adr - > address . ip6 + 0 ) ;
key ^ = * ( const unsigned int * ) ( adr - > address . ip6 + 4 ) ;
key ^ = * ( const unsigned int * ) ( adr - > address . ip6 + 8 ) ;
key ^ = * ( const unsigned int * ) ( adr - > address . ip6 + 12 ) ;
key ^ = adr - > port ;
break ;
default :
key = 0 ;
break ;
}
return key ;
}
static svm_server_t * SVM_GetServer ( netadr_t * adr )
{
svm_server_t * server ;
unsigned int key = SVM_GenerateAddressKey ( adr ) ;
server = Hash_GetKey ( & svm . serverhash , key ) ;
while ( server & & ! NET_CompareAdr ( & server - > adr , adr ) )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
server = Hash_GetNextKey ( & svm . serverhash , key , server ) ;
}
return server ;
}
static svm_game_t * SVM_FindGame ( const char * game , qboolean create )
{
svm_game_t * g ;
for ( g = svm . firstgame ; g ; g = g - > next )
{
if ( ! Q_strcasecmp ( game , g - > name ) )
return g ;
}
if ( create )
{
if ( svm . numgames > = sv_maxgames . ival )
{
Con_DPrintf ( " game limit exceeded \n " ) ;
return NULL ;
}
//block some chars that may cause issues/exploits. sorry.
if ( strchr ( game , ' . ' ) | | strchr ( game , ' \" ' ) | | strchr ( game , ' / ' ) | | strchr ( game , ' ? ' ) | | strchr ( game , ' & ' ) | | strchr ( game , ' + ' ) | | strchr ( game , ' \' ' ) | | strchr ( game , ' < ' ) | | strchr ( game , ' > ' ) )
return NULL ;
if ( ! * game | | ( * game > = ' 0 ' & & * game < = ' 9 ' ) )
return NULL ; //must not start with a number either.
g = ZF_Malloc ( sizeof ( * g ) + strlen ( game ) ) ;
if ( g )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
strcpy ( g - > name , game ) ;
g - > next = svm . firstgame ;
svm . firstgame = g ;
svm . numgames + + ;
Con_DPrintf ( " Creating game \" %s \" \n " , g - > name ) ;
}
}
return g ;
}
static void SVM_RemoveOldServers ( void )
{
svm_game_t * * gamelink , * g ;
svm_server_t * * serverlink , * s ;
for ( gamelink = & svm . firstgame ; ( g = * gamelink ) ; )
{
for ( serverlink = & g - > firstserver ; ( s = * serverlink ) ; )
{
if ( s - > expiretime < svm . time )
{
if ( developer . ival )
{
char buf [ 256 ] ;
Con_Printf ( " timeout: %s \n " , NET_AdrToString ( buf , sizeof ( buf ) , & s - > adr ) ) ;
}
Hash_RemoveDataKey ( & svm . serverhash , SVM_GenerateAddressKey ( & s - > adr ) , s ) ;
svm . total . drops + + ;
* serverlink = s - > next ;
BZ_Free ( s ) ;
g - > numservers - - ;
svm . numservers - - ;
}
2004-08-23 00:15:46 +00:00
else
2018-12-28 00:04:36 +00:00
serverlink = & s - > next ;
}
if ( ! g - > firstserver )
{
Con_DPrintf ( " game \" %s \" has no active servers \n " , g - > name ) ;
* gamelink = g - > next ;
Z_Free ( g ) ;
svm . numgames - - ;
2004-08-23 00:15:46 +00:00
}
else
2018-12-28 00:04:36 +00:00
gamelink = & g - > next ;
2004-08-23 00:15:46 +00:00
}
}
2019-01-29 19:41:31 +00:00
int SVM_AddIPAddresses ( sizebuf_t * sb , int first , const char * gamename , int v4 , int v6 , qboolean prefixes , int gametype )
2004-08-23 00:15:46 +00:00
{
int number = 0 ;
svm_server_t * server ;
2018-12-28 00:04:36 +00:00
int prefix ;
int len ;
svm_game_t * game = SVM_FindGame ( gamename , false ) ;
if ( game )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
for ( server = game - > firstserver ; server ; server = server - > next )
{
if ( number = = first )
break ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
first - - ;
}
for ( ; server ; server = server - > next )
{
2019-01-29 19:41:31 +00:00
//FIXME
// if (gametype != -1 && server->gametype != gametype)
// continue;
2018-12-28 00:04:36 +00:00
switch ( server - > adr . type )
{
case NA_IP :
if ( ! v4 )
continue ;
prefix = ' \\ ' ;
len = 4 ;
break ;
case NA_IPV6 :
if ( ! v6 )
continue ;
prefix = ' / ' ;
len = 16 ;
break ;
default :
continue ;
}
if ( prefixes )
2019-01-29 19:41:31 +00:00
MSG_WriteByte ( sb , prefix ) ;
2018-12-28 00:04:36 +00:00
SZ_Write ( sb , server - > adr . address . ip , len ) ;
MSG_WriteShort ( sb , server - > adr . port ) ;
number + + ;
}
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
return number ;
}
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
vfsfile_t * SVM_GenerateIndex ( const char * fname )
{
static const char * thecss =
" <style type= \" text/css \" > "
" body { "
" background-color: #303030; "
" color: #998080; "
" font-family: verdanah, trebuchet ms, arial, sans-serif; "
" } "
" a { text-decoration: none; } "
" a:link { color: #308090; } "
" a:visited { color: #308090; } "
" a:hover { color: #308090; text-decoration: underline; } "
" .header { text-align: center; } "
" .footer { text-align: center; } "
" .main { width: 1024px; margin: 0 auto; } "
" H1 { "
" text-align: center; "
" background-color: #202020; "
" color: #808080; "
" } "
" table { width: 100%; border-collapse: collapse; } "
" th { text-align: left; } "
" tr:hover { background-color: #202020; } "
" </style> " ;
char tmpbuf [ 256 ] ;
svm_game_t * game ;
svm_server_t * server ;
vfsfile_t * f = NULL ;
unsigned clients = 0 , maxclients = 0 ;
if ( ! strcmp ( fname , " index.html " ) )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
f = VFSPIPE_Open ( 1 , false ) ;
VFS_PRINTF ( f , " %s " , thecss ) ;
VFS_PRINTF ( f , " <h1>FTE-Master</h1> \n " ) ;
VFS_PRINTF ( f , " <table border=1> \n " ) ;
for ( game = svm . firstgame ; game ; game = game - > next )
{
VFS_PRINTF ( f , " <tr><td><a href= \" game/%s.html \" >%s</a></td><td%u server(s)</td></tr> \n " , game - > name , game - > name , ( unsigned ) game - > numservers ) ;
clients + = game - > numservers ;
}
VFS_PRINTF ( f , " </table> \n " ) ;
VFS_PRINTF ( f , " %u game(s), %u server(s) \n " , ( unsigned ) svm . numgames , clients ) ;
}
else if ( ! strncmp ( fname , " server/ " , 7 ) )
{
netadr_t adr [ 64 ] ;
int count ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
f = VFSPIPE_Open ( 1 , false ) ;
VFS_PRINTF ( f , " %s " , thecss ) ;
2019-01-29 19:41:31 +00:00
VFS_PRINTF ( f , " <h1>Single Server Info</h1> \n " ) ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
VFS_PRINTF ( f , " <table border=1> \n " ) ;
VFS_PRINTF ( f , " <tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr> \n " ) ;
count = NET_StringToAdr2 ( fname + 7 , 0 , adr , countof ( adr ) ) ;
while ( count - - > 0 )
{
server = SVM_GetServer ( & adr [ count ] ) ;
if ( server )
VFS_PRINTF ( f , " <tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr> \n " , server - > game ? server - > game - > name : " Unknown " , NET_AdrToString ( tmpbuf , sizeof ( tmpbuf ) , & server - > adr ) , server - > hostname , server - > gamedir , server - > mapname , server - > clients , server - > maxclients ) ;
else
VFS_PRINTF ( f , " <tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr> \n " , NET_AdrToString ( tmpbuf , sizeof ( tmpbuf ) , & adr [ count ] ) ) ;
}
VFS_PRINTF ( f , " </table> \n " ) ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
else if ( ! strncmp ( fname , " game/ " , 5 ) )
{
COM_StripExtension ( fname + 5 , tmpbuf , sizeof ( tmpbuf ) ) ;
game = SVM_FindGame ( tmpbuf , false ) ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
f = VFSPIPE_Open ( 1 , false ) ;
VFS_PRINTF ( f , " %s " , thecss ) ;
VFS_PRINTF ( f , " <h1>Servers for %s</h1> \n " , tmpbuf ) ;
if ( game )
{
VFS_PRINTF ( f , " <table border=1> \n " ) ;
VFS_PRINTF ( f , " <tr><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr> \n " ) ;
for ( server = game - > firstserver ; server ; server = server - > next )
{
VFS_PRINTF ( f , " <tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr> \n " , NET_AdrToString ( tmpbuf , sizeof ( tmpbuf ) , & server - > adr ) , server - > hostname , server - > gamedir , server - > mapname , server - > clients , server - > maxclients ) ;
clients + = server - > clients ;
maxclients + = server - > maxclients ;
}
VFS_PRINTF ( f , " </table> \n " ) ;
VFS_PRINTF ( f , " %u server(s), %u/%u client(s) \n " , ( unsigned ) game - > numservers , clients , maxclients ) ;
}
else
VFS_PRINTF ( f , " No servers known for %s \n " , tmpbuf ) ;
}
else if ( ! strncmp ( fname , " raw/ " , 4 ) )
{ //just spews all
COM_StripExtension ( fname + 4 , tmpbuf , sizeof ( tmpbuf ) ) ;
game = SVM_FindGame ( tmpbuf , false ) ;
f = VFSPIPE_Open ( 1 , false ) ;
for ( server = ( game ? game - > firstserver : NULL ) ; server ; server = server - > next )
VFS_PRINTF ( f , " %s \n " , NET_AdrToString ( tmpbuf , sizeof ( tmpbuf ) , & server - > adr ) ) ;
}
return f ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
static svm_server_t * SVM_Heartbeat ( const char * gamename , netadr_t * adr , int numclients , float validuntil )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
svm_server_t * server = SVM_GetServer ( adr ) ;
svm_game_t * game = SVM_FindGame ( gamename , true ) ;
if ( ! game )
return NULL ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
if ( server & & server - > game ! = game )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
server - > expiretime = realtime - 1 ;
server = NULL ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
2004-08-23 00:15:46 +00:00
if ( ! server ) //not found
{
2018-12-28 00:04:36 +00:00
if ( svm . numservers > = sv_maxservers . ival )
{
Con_DPrintf ( " server limit exceeded \n " ) ;
return NULL ;
}
if ( developer . ival )
{
char buf [ 256 ] ;
Con_Printf ( " heartbeat(new - %s): %s \n " , game - > name , NET_AdrToString ( buf , sizeof ( buf ) , adr ) ) ;
}
2004-08-23 00:15:46 +00:00
server = Z_Malloc ( sizeof ( svm_server_t ) ) ;
2018-12-28 00:04:36 +00:00
server - > game = game ;
server - > next = game - > firstserver ;
game - > firstserver = server ;
game - > numservers + + ;
svm . numservers + + ;
2004-08-23 00:15:46 +00:00
server - > adr = * adr ;
2018-12-28 00:04:36 +00:00
svm . total . adds + + ;
Hash_AddKey ( & svm . serverhash , SVM_GenerateAddressKey ( adr ) , server , & server - > bucket ) ;
}
else
{
if ( developer . ival )
{
char buf [ 256 ] ;
Con_Printf ( " heartbeat(refresh): %s \n " , NET_AdrToString ( buf , sizeof ( buf ) , & server - > adr ) ) ;
}
2004-08-23 00:15:46 +00:00
}
server - > clients = numclients ;
server - > expiretime = validuntil ;
2018-12-28 00:04:36 +00:00
return server ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
void SVM_Think ( int port )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
char * s ;
int cookie = 0 ;
int giveup = 500 ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
while ( giveup - - > 0 & & ( cookie = NET_GetPacket ( svm_sockets , cookie ) ) > = 0 )
2005-10-01 03:09:17 +00:00
{
2018-12-28 00:04:36 +00:00
net_message . data [ net_message . cursize ] = ' \0 ' ; //null term all strings.
svm . time = Sys_DoubleTime ( ) ;
MSG_BeginReading ( msg_nullnetprim ) ;
if ( MSG_ReadLong ( ) ! = - 1 | | msg_badread )
{ //go back to start...
MSG_BeginReading ( msg_nullnetprim ) ;
}
s = MSG_ReadStringLine ( ) ;
s = COM_Parse ( s ) ;
if ( ! strcmp ( com_token , " getservers " ) | | ! strcmp ( com_token , " getserversExt " ) )
{ //q3
sizebuf_t sb ;
int ver ;
char * eos ;
char game [ 64 ] ;
qboolean ext = ! strcmp ( com_token , " getserversExt " ) ;
qboolean empty = false ;
qboolean full = false ;
qboolean ipv4 = ! ext ;
qboolean ipv6 = false ;
int gametype = - 1 ;
s = COM_ParseOut ( s , game , sizeof ( game ) ) ;
ver = strtol ( game , & eos , 0 ) ;
if ( * eos )
{
s = COM_Parse ( s ) ;
ver = strtol ( com_token , NULL , 0 ) ;
}
else
Q_strncpyz ( game , " Quake3 " , sizeof ( game ) ) ;
for ( ; s & & * s ; )
{
s = COM_Parse ( s ) ;
if ( ! strcmp ( com_token , " empty " ) )
empty = true ;
else if ( ! strcmp ( com_token , " full " ) )
full = true ;
else if ( ! strcmp ( com_token , " ipv4 " ) )
ipv4 = true ;
else if ( ! strcmp ( com_token , " ipv6 " ) )
ipv6 = true ;
else if ( ! strcmp ( com_token , " ffa " ) )
gametype = GT_FFA ;
else if ( ! strcmp ( com_token , " tourney " ) )
gametype = GT_TOURNEY ;
else if ( ! strcmp ( com_token , " team " ) )
gametype = GT_TEAM ;
else if ( ! strcmp ( com_token , " ctf " ) )
gametype = GT_CTF ;
else if ( ! strncmp ( com_token , " gametype= " , 9 ) )
gametype = atoi ( com_token + 9 ) ;
2019-01-29 19:41:31 +00:00
else
{
char buf [ 256 ] ;
Con_DPrintf ( " Unknown request filter: %s \n " , COM_QuotedString ( com_token , buf , sizeof ( buf ) , false ) ) ;
}
2018-12-28 00:04:36 +00:00
}
svm . total . queries + + ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( net_message_buffer ) - 2 ;
sb . data = net_message_buffer ;
MSG_WriteLong ( & sb , - 1 ) ;
if ( ! ipv4 & & ! ipv6 )
ipv4 = ipv6 = true ; //neither specified? use both
2019-01-29 19:41:31 +00:00
( void ) ver , ( void ) full , ( void ) empty ;
2018-12-28 00:04:36 +00:00
if ( ext )
{ //ipv6 and ipv4 addresses
MSG_WriteString ( & sb , " getserversExtResponse " ) ;
2019-01-29 19:41:31 +00:00
SVM_AddIPAddresses ( & sb , 0 , game , ipv4 , ipv6 , true , gametype ) ;
2018-12-28 00:04:36 +00:00
}
else
{ //ipv4 only
MSG_WriteString ( & sb , " getserversResponse " ) ;
2019-01-29 19:41:31 +00:00
SVM_AddIPAddresses ( & sb , 0 , game , ipv4 , ipv6 , true , gametype ) ;
2018-12-28 00:04:36 +00:00
}
sb . maxsize + = 2 ;
MSG_WriteByte ( & sb , ' \\ ' ) ; //otherwise the last may be considered invalid and ignored.
// MSG_WriteByte(&sb, 'E');
// MSG_WriteByte(&sb, 'O');
// MSG_WriteByte(&sb, 'T');
NET_SendPacket ( svm_sockets , sb . cursize , sb . data , & net_from ) ;
}
else if ( ! strcmp ( com_token , " heartbeat " ) )
{ //quake2 heartbeat. Serverinfo and players should follow.
if ( * s = = ' \n ' & & s [ 1 ] = = ' \\ ' )
{ //there's some serverinfo there, must be q2...
svm . total . heartbeats + + ;
SVM_Heartbeat ( " Quake2 " , & net_from , 0 , svm . time + sv_heartbeattimeout . ival ) ;
}
else
{ //dp/q3/etc are annoying, but we can query from an emphemerial socket to check NAT rules.
sizebuf_t sb ;
svm . total . queries + + ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( net_message_buffer ) ;
sb . data = net_message_buffer ;
MSG_WriteLong ( & sb , - 1 ) ;
MSG_WriteString ( & sb , " getinfo CAKE \n " ) ;
sb . cursize - - ;
NET_SendPacket ( svm_sockets , sb . cursize , sb . data , & net_from ) ;
}
}
else if ( ! strcmp ( com_token , " infoResponse " ) )
{
int clients ;
const char * game , * chal ;
svm_server_t * srv ;
s = MSG_ReadStringLine ( ) ;
svm . total . heartbeats + + ;
chal = Info_ValueForKey ( s , " challenge " ) ;
if ( ! strcmp ( chal , " CAKE " ) )
{
clients = atoi ( Info_ValueForKey ( s , " clients " ) ) ;
game = Info_ValueForKey ( s , " gamename " ) ;
srv = SVM_Heartbeat ( game , & net_from , clients , svm . time + sv_heartbeattimeout . ival ) ;
if ( srv )
{
if ( developer . ival )
Info_Print ( s , " \t " ) ;
srv - > protover = atoi ( Info_ValueForKey ( s , " protocol " ) ) ;
srv - > maxclients = atoi ( Info_ValueForKey ( s , " sv_maxclients " ) ) ;
Q_strncpyz ( srv - > hostname , Info_ValueForKey ( s , " hostname " ) , sizeof ( srv - > hostname ) ) ;
Q_strncpyz ( srv - > gamedir , Info_ValueForKey ( s , " modname " ) , sizeof ( srv - > gamedir ) ) ;
Q_strncpyz ( srv - > mapname , Info_ValueForKey ( s , " mapname " ) , sizeof ( srv - > mapname ) ) ;
}
}
}
else if ( ! strcmp ( com_token , " query " ) )
{ //quake2 server listing request
sizebuf_t sb ;
svm . total . queries + + ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( net_message_buffer ) ;
sb . data = net_message_buffer ;
MSG_WriteLong ( & sb , - 1 ) ;
MSG_WriteString ( & sb , " servers \n " ) ;
sb . cursize - - ;
2019-01-29 19:41:31 +00:00
SVM_AddIPAddresses ( & sb , 0 , " Quake2 " , true , false , false , - 1 ) ;
2018-12-28 00:04:36 +00:00
NET_SendPacket ( svm_sockets , sb . cursize , sb . data , & net_from ) ;
}
else if ( * com_token = = S2M_HEARTBEAT ) //sequence, players
{ //quakeworld heartbeat
int players ;
s = MSG_ReadStringLine ( ) ;
//sequence = atoi(s);
s = MSG_ReadStringLine ( ) ;
players = atoi ( s ) ;
svm . total . heartbeats + + ;
SVM_Heartbeat ( " QuakeWorld " , & net_from , players , svm . time + sv_heartbeattimeout . ival ) ;
}
else if ( * com_token = = C2M_MASTER_REQUEST )
{ //quakeworld server listing request
sizebuf_t sb ;
svm . total . queries + + ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( net_message_buffer ) ;
sb . data = net_message_buffer ;
MSG_WriteLong ( & sb , - 1 ) ;
MSG_WriteByte ( & sb , M2C_MASTER_REPLY ) ;
MSG_WriteByte ( & sb , ' \n ' ) ;
2019-01-29 19:41:31 +00:00
SVM_AddIPAddresses ( & sb , 0 , " QuakeWorld " , true , false , false , - 1 ) ;
2018-12-28 00:04:36 +00:00
NET_SendPacket ( svm_sockets , sb . cursize , sb . data , & net_from ) ;
}
else if ( * com_token = = A2A_PING )
{ //quakeworld server listing request
sizebuf_t sb ;
svm . total . queries + + ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( net_message_buffer ) ;
sb . data = net_message_buffer ;
MSG_WriteLong ( & sb , - 1 ) ;
MSG_WriteByte ( & sb , A2A_ACK ) ;
MSG_WriteByte ( & sb , ' \n ' ) ;
NET_SendPacket ( svm_sockets , sb . cursize , sb . data , & net_from ) ;
}
else
svm . total . junk + + ;
2005-10-01 03:09:17 +00:00
}
2018-12-28 00:04:36 +00:00
SVM_RemoveOldServers ( ) ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
# else
void SVM_Think ( int port ) { }
# endif
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
# ifdef MASTERONLY
static void SV_Quit_f ( void )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
Con_TPrintf ( " Shutting down. \n " ) ;
Sys_Quit ( ) ;
}
static void SVM_Status_f ( void )
{
svm_game_t * g ;
svm_server_t * s ;
unsigned clients ;
struct rates_s * s1 , * s2 ;
float period ;
size_t r ;
NET_PrintAddresses ( svm_sockets ) ;
NET_PrintConnectionsStatus ( svm_sockets ) ;
Con_Printf ( " Game count: %u \n " , ( unsigned ) svm . numgames ) ;
for ( g = svm . firstgame ; g ; g = g - > next )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
clients = 0 ;
for ( s = g - > firstserver ; s ; s = s - > next )
clients + = s - > clients ;
Con_Printf ( " Game %s: %u servers, %u clients \n " , g - > name , ( unsigned ) g - > numservers , clients ) ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
s1 = & svm . total ;
r = ( svm . stampring > = countof ( svm . stamps ) - 1 ) ? svm . stampring - countof ( svm . stamps ) - 1 : 0 ;
s2 = & svm . stamps [ r % countof ( svm . stamps ) ] ;
period = s1 - > timestamp - s2 - > timestamp ;
period / = 60 ;
if ( ! period )
period = 1 ;
Con_Printf ( " Heartbeats/min: %f \n " , ( s1 - > heartbeats - s2 - > heartbeats ) / period ) ;
Con_Printf ( " Queries/min: %f \n " , ( s1 - > queries - s2 - > queries ) / period ) ;
}
void SV_Init ( struct quakeparms_s * parms )
{
int manarg ;
COM_InitArgv ( parms - > argc , parms - > argv ) ;
host_parms = * parms ;
Cvar_Init ( ) ;
Memory_Init ( ) ;
Sys_Init ( ) ;
COM_ParsePlusSets ( false ) ;
Cbuf_Init ( ) ;
Cmd_Init ( ) ;
NET_Init ( ) ;
COM_Init ( ) ;
# ifdef WEBSERVER
IWebInit ( ) ;
# endif
Cmd_AddCommand ( " quit " , SV_Quit_f ) ;
Cmd_AddCommand ( " status " , SVM_Status_f ) ;
svm_sockets = FTENET_CreateCollection ( true ) ;
Hash_InitTable ( & svm . serverhash , 1024 , Z_Malloc ( Hash_BytesForBuckets ( 1024 ) ) ) ;
Cvar_Register ( & sv_masterport , " server control variables " ) ;
Cvar_Register ( & sv_masterport_tcp , " server control variables " ) ;
Cvar_Register ( & sv_heartbeattimeout , " server control variables " ) ;
Cvar_Register ( & sv_maxgames , " server control variables " ) ;
Cvar_Register ( & sv_maxservers , " server control variables " ) ;
Cvar_ParseWatches ( ) ;
host_initialized = true ;
manarg = COM_CheckParm ( " -manifest " ) ;
if ( manarg & & manarg < com_argc - 1 & & com_argv [ manarg + 1 ] )
2004-08-23 00:15:46 +00:00
{
2018-12-28 00:04:36 +00:00
char * man = FS_MallocFile ( com_argv [ manarg + 1 ] , FS_SYSTEM , NULL ) ;
FS_ChangeGame ( FS_Manifest_Parse ( NULL , man ) , true , true ) ;
BZ_Free ( man ) ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
else
FS_ChangeGame ( NULL , true , true ) ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
Cmd_StuffCmds ( ) ;
Cbuf_Execute ( ) ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
Cvar_ForceCallback ( & sv_masterport ) ;
Cvar_ForceCallback ( & sv_masterport_tcp ) ;
2005-10-01 03:09:17 +00:00
2018-12-28 00:04:36 +00:00
Con_TPrintf ( " Exe: %s %s \n " , __DATE__ , __TIME__ ) ;
2004-08-23 00:15:46 +00:00
2018-12-28 00:04:36 +00:00
Con_Printf ( " %s \n " , version_string ( ) ) ;
Con_TPrintf ( " ======== %s Initialized ======== \n " , " FTEMaster " ) ;
}
float SV_Frame ( void )
{
realtime = Sys_DoubleTime ( ) ;
while ( 1 )
{
const char * cmd = Sys_ConsoleInput ( ) ;
if ( ! cmd )
break ;
Log_String ( LOG_CONSOLE , cmd ) ;
Cbuf_AddText ( cmd , RESTRICT_LOCAL ) ;
Cbuf_AddText ( " \n " , RESTRICT_LOCAL ) ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
Cbuf_Execute ( ) ;
SVM_Think ( sv_masterport . ival ) ;
//record lots of info over multiple frames, for smoother stats info.
svm . total . timestamp = realtime ;
if ( svm . nextstamp < realtime )
{
svm . stamps [ svm . stampring % countof ( svm . stamps ) ] = svm . total ;
svm . stampring + + ;
svm . nextstamp = realtime + 60 ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
return 4 ;
2004-08-23 00:15:46 +00:00
}
2018-12-28 00:04:36 +00:00
# endif