engine: implement new static master server protocol

This commit is contained in:
Alibek Omarov
2026-06-06 02:42:58 +05:00
parent efad71bcc9
commit 74fcab9bfb
4 changed files with 219 additions and 19 deletions

View File

@@ -1864,19 +1864,6 @@ static void CL_InternetServers_f( void )
cls.internetservers_wait = NET_MasterQuery( 1, cls.internetservers_nat, cls.internetservers_customfilter );
}
static void CL_QueryServer( netadr_t adr, connprotocol_t proto )
{
switch( proto )
{
case PROTO_GOLDSRC:
Netchan_OutOfBand( NS_CLIENT, adr, sizeof( A2S_GOLDSRC_INFO ), A2S_GOLDSRC_INFO ); // includes null terminator!
break;
case PROTO_CURRENT:
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION );
break;
}
}
static void CL_QueryServer_f( void )
{
netadr_t adr;
@@ -1902,7 +1889,7 @@ static void CL_QueryServer_f( void )
if( !CL_StringToProtocol( Cmd_Argv( 2 ), &proto ))
return;
CL_QueryServer( adr, proto );
NET_QueryServerByAddress( adr, proto );
}
/*
@@ -2652,7 +2639,7 @@ static void CL_ServerList( netadr_t from, sizebuf_t *msg )
break;
NET_Config( true, false ); // allow remote
CL_QueryServer( servadr, proto );
NET_QueryServerByAddress( servadr, proto );
}
if( cls.internetservers_pending )

View File

@@ -901,6 +901,7 @@ void NET_MasterClear( void );
void NET_MasterShutdown( void );
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat );
qboolean NET_MasterQuery( uint32_t key, qboolean net, const char *filter );
void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto );
//
// munge.c

View File

@@ -14,6 +14,7 @@ GNU General Public License for more details.
*/
#include "common.h"
#include "netchan.h"
#include "net_ws.h"
#include "server.h"
typedef struct master_s
@@ -32,9 +33,18 @@ typedef struct master_s
double resolve_time;
} master_t;
typedef struct masterstatic_s
{
struct masterstatic_s *next;
qboolean save;
qboolean in_flight;
char base[];
} masterstatic_t;
static struct masterlist_s
{
master_t *head, *tail;
masterstatic_t *static_head, *static_tail;
qboolean modified;
} ml;
@@ -199,9 +209,119 @@ void NET_MasterClear( void )
m->last_heartbeat = MAX_HEARTBEAT;
}
/*
========================
NET_QueryServerByAddress
========================
*/
void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto )
{
if( proto == PROTO_GOLDSRC )
Netchan_OutOfBand( NS_CLIENT, adr, sizeof( A2S_GOLDSRC_INFO ), (const byte *)A2S_GOLDSRC_INFO ); // includes null terminator
else
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO " %i", PROTOCOL_VERSION );
}
static int NET_ParseMasterStaticBody( char *body )
{
char token[MAX_TOKEN];
char *pfile = body;
int count = 0;
while(( pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL )))
{
qboolean gs;
if( !Q_strcmp( token, "ip" ))
gs = false;
else if( !Q_strcmp( token, "gs" ))
gs = true;
else
{
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
if( !pfile )
break;
continue;
}
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
if( !pfile )
break;
netadr_t adr = { 0 };
if( !NET_StringToAdr( token, &adr ))
{
Con_Reportf( S_WARN "masterstatic: can't parse address \"%s\"\n", token );
continue;
}
if( adr.port == 0 )
adr.port = MSG_BigShort( PORT_SERVER );
NET_QueryServerByAddress( adr, gs );
count++;
}
return count;
}
/*
========================
NET_MasterStaticResponse
========================
*/
static void NET_MasterStaticResponse( const char *url, qboolean success, const byte *data, size_t size, void *userdata )
{
masterstatic_t *ms = (masterstatic_t *)userdata;
if( ms )
ms->in_flight = false;
if( !success || !data || size == 0 )
{
Con_Reportf( "masterstatic: %s returned no data\n", url );
return;
}
// HTTP buffer isn't NUL-terminated; COM_ParseFileSafe needs one.
char *body = Mem_Malloc( host.mempool, size + 1 );
memcpy( body, data, size );
body[size] = 0;
NET_Config( true, false ); // allow remote sends
int count = NET_ParseMasterStaticBody( body );
Mem_Free( body );
Con_Reportf( "masterstatic: %s yielded %d server(s)\n", url, count );
}
static void NET_MasterStaticQuery( void )
{
const char *gamedir = GI ? GI->gamefolder : "valve";
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
{
char url[1024];
if( ms->in_flight )
continue;
Q_snprintf( url, sizeof( url ), "%s/v1/servers/%s", ms->base, gamedir );
if( HTTP_GetToMemory( url, NET_MasterStaticResponse, ms ))
ms->in_flight = true;
}
}
/*
=================
NET_MasterQuery
=================
*/
qboolean NET_MasterQuery( uint32_t key, qboolean nat, const char *filter )
@@ -217,6 +337,8 @@ qboolean NET_MasterQuery( uint32_t key, qboolean nat, const char *filter )
wait |= NET_SendToMasters( NS_CLIENT, len, buf, PROTO_GOLDSRC );
}
NET_MasterStaticQuery();
if( !wait )
NET_ClearSendState();
@@ -298,6 +420,7 @@ static master_t *NET_GetMasterFromAdr( netadr_t adr )
/*
========================
NET_GetMaster
========================
*/
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat )
@@ -380,6 +503,54 @@ static void NET_AddMaster_f( void )
ml.modified = true; // save config
}
static masterstatic_t *NET_AddMasterStatic( const char *base )
{
size_t base_len = Q_strlen( base );
while( base_len > 0 && base[base_len - 1] == '/' )
base_len--;
if( base_len == 0 )
return NULL;
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
{
if( Q_strlen( ms->base ) == base_len && !Q_strnicmp( ms->base, base, base_len ))
return ms;
}
masterstatic_t *ms = Mem_Calloc( host.mempool, sizeof( *ms ) + base_len + 1 );
memcpy( ms->base, base, base_len );
if( ml.static_tail )
{
ml.static_tail->next = ms;
ml.static_tail = ms;
}
else
{
ml.static_head = ml.static_tail = ms;
}
return ms;
}
static void NET_AddMasterStatic_f( void )
{
if( Cmd_Argc() != 2 )
{
Msg( S_USAGE "addmasterstatic <base-url>\n" );
return;
}
masterstatic_t *ms = NET_AddMasterStatic( Cmd_Argv( 1 ));
if( !ms )
return;
ms->save = true;
ml.modified = true;
}
/*
========================
NET_ClearMasters
@@ -397,6 +568,15 @@ static void NET_ClearMasters_f( void )
}
ml.tail = NULL;
while( ml.static_head )
{
masterstatic_t *head = ml.static_head;
ml.static_head = ml.static_head->next;
Mem_Free( head );
}
ml.static_tail = NULL;
}
/*
@@ -425,6 +605,13 @@ static void NET_ListMasters_f( void )
Con_Printf( "\n" );
}
if( ml.static_head )
Con_Printf( "Static master base URLs:\n" );
i = 1;
for( masterstatic_t *ms = ml.static_head; ms; i++, ms = ms->next )
Con_Printf( "%d\t%s%s\n", i, ms->base, ms->in_flight ? " (in flight)" : "" );
}
/*
@@ -472,6 +659,16 @@ static void NET_LoadMasters( void )
master = NET_AddMaster( token );
master->gs = true;
}
else if( !Q_strcmp( token, "masterstatic" ))
{
pfile = COM_ParseFile( pfile, token, sizeof( token ));
if( pfile )
{
masterstatic_t *ms = NET_AddMasterStatic( token );
if( ms )
ms->save = true;
}
}
if( master )
master->save = true;
@@ -519,6 +716,14 @@ void NET_SaveMasters( void )
FS_Printf( f, "%s %s\n", key, m->address );
}
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
{
if( !ms->save )
continue;
FS_Printf( f, "masterstatic \"%s\"\n", ms->base );
}
FS_Close( f );
}
@@ -532,6 +737,7 @@ Initialize master server list
void NET_InitMasters( void )
{
Cmd_AddRestrictedCommand( "addmaster", NET_AddMaster_f, "add address to masterserver list" );
Cmd_AddRestrictedCommand( "addmasterstatic", NET_AddMasterStatic_f, "add static HTTP masterserver base URL" );
Cmd_AddRestrictedCommand( "clearmasters", NET_ClearMasters_f, "clear masterserver list" );
Cmd_AddCommand( "listmasters", NET_ListMasters_f, "list masterservers" );
@@ -548,10 +754,13 @@ void NET_InitMasters( void )
NET_AddMaster( "aaaa.ms2.mentality.rip:27010" )->v6only = true;
}
{ // testing servers, might be offline
NET_AddMaster( "mentality.rip:27011" );
NET_AddMaster( "aaaa.mentality.rip:27011" )->v6only = true;
}
#if 0
NET_AddMasterStatic( "http://meltdown.lan/test" );
#endif
NET_AddMasterStatic( "http://fwgs.github.io/server-list" );
// FIXME: https raw.githubcontent source
// FIXME: cloudflare'd sources both HTTP and HTTPS
NET_LoadMasters();
}

View File

@@ -99,4 +99,7 @@ void HTTP_ResetProcessState( void );
void HTTP_Init( void );
void HTTP_Run( void );
typedef void ( *http_memory_cb_t )( const char *url, qboolean success, const byte *data, size_t size, void *userdata );
qboolean HTTP_GetToMemory( const char *url, http_memory_cb_t cb, void *userdata );
#endif//NET_WS_H