diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index d6708280..168bd441 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -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 ) diff --git a/engine/common/common.h b/engine/common/common.h index 1ac016dc..41e6074a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -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 diff --git a/engine/common/masterlist.c b/engine/common/masterlist.c index 305118d3..81039dc5 100644 --- a/engine/common/masterlist.c +++ b/engine/common/masterlist.c @@ -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 \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(); } diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 6530faac..0deb4327 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -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